diff --git a/.clang-format b/.clang-format index 0470e6f..3344c87 100644 --- a/.clang-format +++ b/.clang-format @@ -1,28 +1,28 @@ ---- -BasedOnStyle: LLVM -AccessModifierOffset: -8 -AlignAfterOpenBracket: true -AlignEscapedNewlinesLeft: false -AlignOperands: true -AlignTrailingComments: false -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortFunctionsOnASingleLine: Empty -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false -AlwaysBreakBeforeMultilineStrings: true -BinPackArguments: false -BinPackParameters: false -BreakBeforeBinaryOperators: None -BreakBeforeBraces: WebKit -BreakConstructorInitializers: AfterColon -BreakStringLiterals: false -ContinuationIndentWidth: 8 -ConstructorInitializerIndentWidth: 16 -IndentCaseLabels: false -IndentPPDirectives: AfterHash -IndentWidth: 8 -Language: Cpp -MaxEmptyLinesToKeep: 2 -SortIncludes: true -SpaceAfterCStyleCast: false -UseTab: ForContinuationAndIndentation +--- +BasedOnStyle: LLVM +AccessModifierOffset: -8 +AlignAfterOpenBracket: true +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakBeforeMultilineStrings: true +BinPackArguments: false +BinPackParameters: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: WebKit +BreakConstructorInitializers: AfterColon +BreakStringLiterals: false +ContinuationIndentWidth: 8 +ConstructorInitializerIndentWidth: 16 +IndentCaseLabels: false +IndentPPDirectives: AfterHash +IndentWidth: 8 +Language: Cpp +MaxEmptyLinesToKeep: 2 +SortIncludes: true +SpaceAfterCStyleCast: false +UseTab: ForContinuationAndIndentation diff --git a/AUTHORS b/AUTHORS index f25056b..4e808ec 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,4 @@ -Aurelien Barre - Original author -Parrot Drones SAS - Maintainer +Aurelien Barre + Original author +Parrot Drones SAS + Maintainer diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0fb3096 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.15) +project(pdraw VERSION 1.0) + + + + +add_subdirectory(libpdraw) +add_subdirectory(libpdraw-vsink) + diff --git a/COPYING b/COPYING index d0daebc..0034d6a 100644 --- a/COPYING +++ b/COPYING @@ -1,24 +1,24 @@ -Copyright (c) 2018 Parrot Drones SAS -Copyright (c) 2016 Aurelien Barre - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the copyright holders nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Copyright (c) 2018 Parrot Drones SAS +Copyright (c) 2016 Aurelien Barre + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index 79194d7..d7c8187 100644 --- a/README.md +++ b/README.md @@ -1,186 +1,186 @@ -# PDrAW - Parrot Drones Awesome Video Viewer - -_PDrAW_ (pronounced like the name "Pedro") is a video player for medias created -by Parrot drones such as _Anafi_ or _Anafi Ai_. The player supports both -streamed (RTP/RTSP) and recorded (MP4) videos. - -PDrAW was originally written by AurĂ©lien Barre as a personal project and is -now officially maintained by Parrot Drones SAS. _PDrAW_ is the main video -pipeline implementation of _GroundSDK_. We are using it in our _FreeFlight6_ -and _FreeFlight7_ applications, both on iOS and Android. It is also usable on -desktop platforms. - -## Supported platforms - -* Linux PC -* macOS -* Android (5.0 minimum) (note: for sample code, see _GroundSDK Android_) -* iOS (8.0 minimum) (note: for sample code, see _GroundSDK iOS_) - -## Features - -* Demuxing - * Record demuxer - * Playback of local replays - * MP4 file format (ISO/IEC 14496-12, ISO Base Media File Format / - ISO/IEC 14496-14, MP4 File Format) - * Multi-track support on the video with user selection - * Stream demuxer - * Live video and streamed replays playback - * RTP/AVP/UDP streams (RFC 3550, RFC 3551) - * RTP/AVP/MUX streams with SkyController remotes (using Parrot _libmux_) - * Unicast only - * RTSP 1.0 protocol (RFC 2326) - * Multi-track support on the video in RTSP with user selection - * Playback control - * Play/pause - * Seeking (except for live streams) - * Playback speed control (except for live streams) - * Negative speeds for playing backward (except for live streams) - * Frame-by-frame forward and backward on local replays (MP4 records) - * Support of Parrot session and frame metadata (see _libvideo_metadata_) -* Decoding - * H.264 video decoding (ITU-T H.264 / ISO/IEC 14496-10), baseline, main - and high profiles - * H.265 video decoding (ITU-T H.265 / ISO/IEC 23008-2), main and main 10 - profiles - * Frame output API for application-side processing on the video (either - H.264 frames before decoding or YUV frames after decoding) -* Rendering - * OpenGL ES 2.0 video rendering - * Imaging features: - * Overexposure zebras - * RGB and luminance histograms computation - * User rendering callback functions - * Custom texture loading with provided video metadata - * Video overlay with provided video metadata - * HMD distortion correction - * Support for Parrot Cockpitglasses 1 & 2 - * Device settings for screen pixel density and mechanical margins - * User settings for video scale, placement and IPD - -## Software architecture and APIs - -### Main library: _libpdraw_ - -PDrAW is mainly _libpdraw_. - -#### Available APIs - -Available APIs for *libpdraw* are: - -* C -* C++ - -#### Dependencies - -The library depends on the following Alchemy modules: - -* eigen -* libfutils -* libh264 -* libh265 -* libmp4 -* libpomp -* librtsp -* libsdp -* libulog -* libvideo-buffers -* libvideo-buffers-generic -* libvideo-decode -* libvideo-metadata -* libvideo-streaming -* json (optional) -* libmux (optional) -* glfw3 (only on Linux and macOS) -* opengl (only on Linux) - -#### Threading model - -The library is designed to run on a _libpomp_ event loop (_pomp_loop_, see -_libpomp_ documentation), except for rendering functions. All API functions -must be called from the _pomp_loop_ thread and all callback functions are -called from the _pomp_loop_ thread, except for rendering function which must -be called on the rendering (OpenGL) thread and rendering callback functions -which are called also on the rendering thread. - -### Helper libraries - -#### libpdraw-backend - -_libpdraw-backend_ is a wrapper for _libpdraw_'s API that allows calling -functions from any thread, with the exception of rendering functions which -must still be called from the rendering thread. - -Available APIs for _libpdraw-backend_ are: - -* C -* C++ - -The library depends on the following Alchemy modules: - -* libfutils -* libpdraw -* libpomp -* libulog - -#### libpdraw-vsink - -_libpdraw-vsink_ is a helper library to easily create a _libpdraw_ instance -on a stream or record without rendering and get YUV frames, for example to -process them with OpenCV in an application. - -The API is available in C. The functions can be called from any thread but -are not thread-safe: they should always be called from the same thread, -or it is the caller's responsibility to synchronize calls if multiple threads -are used. - -The library depends on the following Alchemy modules: - -* libfutils -* libpdraw -* libpomp -* libulog -* libvideo-buffers -* libvideo-buffers-generic -* libvideo-metadata - -#### libpdraw-gles2hud - -_libpdraw-gles2hud_ is a helper library to draw an OpenGL ES 2.0 example HUD -(Head-Up Display) on top of the rendered video using _libpdraw_'s rendering -overlay callback function. - -The API is available in C. All functions must be called from the rendering -thread. - -The library depends on the following Alchemy modules: - -* eigen -* libpdraw -* libulog -* libvideo-metadata -* glfw3 (only on Linux and macOS) -* opengl (only on Linux) - -### Applications - -Applications are built on top of _libpdraw_ or _libpdraw-backend_. - -#### pdraw-desktop - -_pdraw-desktop_ is Linux PC and macOS application written in C using SDL2 for -windowed display and the UI. - -The application depends on the following Alchemy modules: - -* eigen -* libpdraw -* libpdraw-backend -* libpdraw-gles2hud -* libpomp -* libulog -* libvideo-metadata -* sdl2 -* glfw3 -* opengl (only on Linux) +# PDrAW - Parrot Drones Awesome Video Viewer + +_PDrAW_ (pronounced like the name "Pedro") is a video player for medias created +by Parrot drones such as _Anafi_ or _Anafi Ai_. The player supports both +streamed (RTP/RTSP) and recorded (MP4) videos. + +PDrAW was originally written by AurĂ©lien Barre as a personal project and is +now officially maintained by Parrot Drones SAS. _PDrAW_ is the main video +pipeline implementation of _GroundSDK_. We are using it in our _FreeFlight6_ +and _FreeFlight7_ applications, both on iOS and Android. It is also usable on +desktop platforms. + +## Supported platforms + +* Linux PC +* macOS +* Android (5.0 minimum) (note: for sample code, see _GroundSDK Android_) +* iOS (8.0 minimum) (note: for sample code, see _GroundSDK iOS_) + +## Features + +* Demuxing + * Record demuxer + * Playback of local replays + * MP4 file format (ISO/IEC 14496-12, ISO Base Media File Format / + ISO/IEC 14496-14, MP4 File Format) + * Multi-track support on the video with user selection + * Stream demuxer + * Live video and streamed replays playback + * RTP/AVP/UDP streams (RFC 3550, RFC 3551) + * RTP/AVP/MUX streams with SkyController remotes (using Parrot _libmux_) + * Unicast only + * RTSP 1.0 protocol (RFC 2326) + * Multi-track support on the video in RTSP with user selection + * Playback control + * Play/pause + * Seeking (except for live streams) + * Playback speed control (except for live streams) + * Negative speeds for playing backward (except for live streams) + * Frame-by-frame forward and backward on local replays (MP4 records) + * Support of Parrot session and frame metadata (see _libvideo_metadata_) +* Decoding + * H.264 video decoding (ITU-T H.264 / ISO/IEC 14496-10), baseline, main + and high profiles + * H.265 video decoding (ITU-T H.265 / ISO/IEC 23008-2), main and main 10 + profiles + * Frame output API for application-side processing on the video (either + H.264 frames before decoding or YUV frames after decoding) +* Rendering + * OpenGL ES 2.0 video rendering + * Imaging features: + * Overexposure zebras + * RGB and luminance histograms computation + * User rendering callback functions + * Custom texture loading with provided video metadata + * Video overlay with provided video metadata + * HMD distortion correction + * Support for Parrot Cockpitglasses 1 & 2 + * Device settings for screen pixel density and mechanical margins + * User settings for video scale, placement and IPD + +## Software architecture and APIs + +### Main library: _libpdraw_ + +PDrAW is mainly _libpdraw_. + +#### Available APIs + +Available APIs for *libpdraw* are: + +* C +* C++ + +#### Dependencies + +The library depends on the following Alchemy modules: + +* eigen +* libfutils +* libh264 +* libh265 +* libmp4 +* libpomp +* librtsp +* libsdp +* libulog +* libvideo-buffers +* libvideo-buffers-generic +* libvideo-decode +* libvideo-metadata +* libvideo-streaming +* json (optional) +* libmux (optional) +* glfw3 (only on Linux and macOS) +* opengl (only on Linux) + +#### Threading model + +The library is designed to run on a _libpomp_ event loop (_pomp_loop_, see +_libpomp_ documentation), except for rendering functions. All API functions +must be called from the _pomp_loop_ thread and all callback functions are +called from the _pomp_loop_ thread, except for rendering function which must +be called on the rendering (OpenGL) thread and rendering callback functions +which are called also on the rendering thread. + +### Helper libraries + +#### libpdraw-backend + +_libpdraw-backend_ is a wrapper for _libpdraw_'s API that allows calling +functions from any thread, with the exception of rendering functions which +must still be called from the rendering thread. + +Available APIs for _libpdraw-backend_ are: + +* C +* C++ + +The library depends on the following Alchemy modules: + +* libfutils +* libpdraw +* libpomp +* libulog + +#### libpdraw-vsink + +_libpdraw-vsink_ is a helper library to easily create a _libpdraw_ instance +on a stream or record without rendering and get YUV frames, for example to +process them with OpenCV in an application. + +The API is available in C. The functions can be called from any thread but +are not thread-safe: they should always be called from the same thread, +or it is the caller's responsibility to synchronize calls if multiple threads +are used. + +The library depends on the following Alchemy modules: + +* libfutils +* libpdraw +* libpomp +* libulog +* libvideo-buffers +* libvideo-buffers-generic +* libvideo-metadata + +#### libpdraw-gles2hud + +_libpdraw-gles2hud_ is a helper library to draw an OpenGL ES 2.0 example HUD +(Head-Up Display) on top of the rendered video using _libpdraw_'s rendering +overlay callback function. + +The API is available in C. All functions must be called from the rendering +thread. + +The library depends on the following Alchemy modules: + +* eigen +* libpdraw +* libulog +* libvideo-metadata +* glfw3 (only on Linux and macOS) +* opengl (only on Linux) + +### Applications + +Applications are built on top of _libpdraw_ or _libpdraw-backend_. + +#### pdraw-desktop + +_pdraw-desktop_ is Linux PC and macOS application written in C using SDL2 for +windowed display and the UI. + +The application depends on the following Alchemy modules: + +* eigen +* libpdraw +* libpdraw-backend +* libpdraw-gles2hud +* libpomp +* libulog +* libvideo-metadata +* sdl2 +* glfw3 +* opengl (only on Linux) diff --git a/apps/pdraw_desktop/atom.mk b/apps/pdraw_desktop/atom.mk index e245ffe..06fc7a6 100644 --- a/apps/pdraw_desktop/atom.mk +++ b/apps/pdraw_desktop/atom.mk @@ -1,42 +1,42 @@ - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := pdraw -LOCAL_DESCRIPTION := Parrot Drones Awesome Video Viewer desktop application -LOCAL_CATEGORY_PATH := multimedia -LOCAL_CFLAGS := -D_USE_MATH_DEFINES -D_GNU_SOURCE -LOCAL_SRC_FILES := \ - pdraw_desktop.c \ - pdraw_desktop_ext_tex.c \ - pdraw_desktop_ui.c \ - pdraw_desktop_view.cpp -LOCAL_LIBRARIES := \ - eigen \ - libfutils \ - libmedia-buffers \ - libpdraw \ - libpdraw-backend \ - libpdraw-gles2hud \ - libpomp \ - libulog \ - libvideo-defs \ - libvideo-metadata \ - sdl2 - -ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","linux-native") - LOCAL_LIBRARIES += \ - glfw3 \ - opengl -else ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","darwin-native") - LOCAL_LDLIBS += \ - -framework OpenGL - LOCAL_LIBRARIES += \ - glfw3 -else ifeq ("$(TARGET_OS)","windows") - LOCAL_CFLAGS += -D_WIN32_WINNT=0x0600 -DEPOXY_SHARED - LOCAL_LDLIBS += -lws2_32 -lepoxy -endif - -include $(BUILD_EXECUTABLE) + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := pdraw +LOCAL_DESCRIPTION := Parrot Drones Awesome Video Viewer desktop application +LOCAL_CATEGORY_PATH := multimedia +LOCAL_CFLAGS := -D_USE_MATH_DEFINES -D_GNU_SOURCE +LOCAL_SRC_FILES := \ + pdraw_desktop.c \ + pdraw_desktop_ext_tex.c \ + pdraw_desktop_ui.c \ + pdraw_desktop_view.cpp +LOCAL_LIBRARIES := \ + eigen \ + libfutils \ + libmedia-buffers \ + libpdraw \ + libpdraw-backend \ + libpdraw-gles2hud \ + libpomp \ + libulog \ + libvideo-defs \ + libvideo-metadata \ + sdl2 + +ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","linux-native") + LOCAL_LIBRARIES += \ + glfw3 \ + opengl +else ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","darwin-native") + LOCAL_LDLIBS += \ + -framework OpenGL + LOCAL_LIBRARIES += \ + glfw3 +else ifeq ("$(TARGET_OS)","windows") + LOCAL_CFLAGS += -D_WIN32_WINNT=0x0600 -DEPOXY_SHARED + LOCAL_LDLIBS += -lws2_32 -lepoxy +endif + +include $(BUILD_EXECUTABLE) diff --git a/apps/pdraw_desktop/pdraw_desktop.c b/apps/pdraw_desktop/pdraw_desktop.c index 9617fba..54aaba7 100644 --- a/apps/pdraw_desktop/pdraw_desktop.c +++ b/apps/pdraw_desktop/pdraw_desktop.c @@ -1,1075 +1,1075 @@ -/** - * Parrot Drones Awesome Video Viewer - * Desktop application - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "pdraw_desktop.h" - -ULOG_DECLARE_TAG(pdraw_desktop); - - -static const struct pdraw_backend_demuxer_cbs demuxer_cbs; - - -void pdraw_desktop_open(struct pdraw_desktop *self) -{ - int res; - if (self->url != NULL) { - res = pdraw_be_demuxer_new_from_url(self->pdraw, - self->url, - &demuxer_cbs, - self, - &self->demuxer); - if (res < 0) - ULOG_ERRNO("pdraw_be_demuxer_new_from_url", -res); - } else { - res = pdraw_be_demuxer_new_single_stream( - self->pdraw, - self->local_addr, - self->local_stream_port, - self->local_control_port, - self->remote_addr, - self->remote_stream_port, - self->remote_control_port, - &demuxer_cbs, - self, - &self->demuxer); - if (res < 0) { - ULOG_ERRNO("pdraw_be_demuxer_new_single_stream", -res); - return; - } - } -} - - -void pdraw_desktop_close(struct pdraw_desktop *self) -{ - int res; - for (unsigned int i = 0; i < self->renderer_count; i++) { - if (self->gles2hud[i] != NULL) { - res = pdraw_gles2hud_destroy(self->gles2hud[i]); - if (res < 0) - ULOG_ERRNO("pdraw_gles2hud_destroy", -res); - self->gles2hud[i] = NULL; - } - if (self->renderer[i] != NULL) { - res = pdraw_be_video_renderer_destroy( - self->pdraw, self->renderer[i]); - if (res < 0) - ULOG_ERRNO("pdraw_be_video_renderer_destroy", - -res); - self->renderer[i] = NULL; - } - } - if (self->demuxer != NULL) { - res = pdraw_be_demuxer_close(self->pdraw, self->demuxer); - if (res < 0) - ULOG_ERRNO("pdraw_be_demuxer_close", -res); - } -} - - -void pdraw_desktop_toggle_play_pause(struct pdraw_desktop *self) -{ - int res; - if (pdraw_be_demuxer_is_paused(self->pdraw, self->demuxer) != 0) { - res = pdraw_be_demuxer_play_with_speed( - self->pdraw, - self->demuxer, - self->speed * self->speed_sign); - if (res < 0) - ULOG_ERRNO("pdraw_be_demuxer_play_with_speed", -res); - } else { - res = pdraw_be_demuxer_pause(self->pdraw, self->demuxer); - if (res < 0) - ULOG_ERRNO("pdraw_be_demuxer_pause", -res); - } -} - - -void pdraw_desktop_toggle_speed_sign(struct pdraw_desktop *self) -{ - int res; - self->speed_sign *= -1; - if (pdraw_be_demuxer_is_paused(self->pdraw, self->demuxer) == 0) { - res = pdraw_be_demuxer_play_with_speed( - self->pdraw, - self->demuxer, - self->speed * self->speed_sign); - if (res < 0) - ULOG_ERRNO("pdraw_be_demuxer_play_with_speed", -res); - } -} - - -void pdraw_desktop_speed_down(struct pdraw_desktop *self) -{ - int res; - self->speed /= 2; - if (pdraw_be_demuxer_is_paused(self->pdraw, self->demuxer) == 0) { - res = pdraw_be_demuxer_play_with_speed( - self->pdraw, - self->demuxer, - self->speed * self->speed_sign); - if (res < 0) - ULOG_ERRNO("pdraw_be_demuxer_play_with_speed", -res); - } -} - - -void pdraw_desktop_speed_up(struct pdraw_desktop *self) -{ - int res; - self->speed *= 2; - if (pdraw_be_demuxer_is_paused(self->pdraw, self->demuxer) == 0) { - res = pdraw_be_demuxer_play_with_speed( - self->pdraw, - self->demuxer, - self->speed * self->speed_sign); - if (res < 0) - ULOG_ERRNO("pdraw_be_demuxer_play_with_speed", -res); - } -} - - -void pdraw_desktop_previous_frame(struct pdraw_desktop *self) -{ - int res = pdraw_be_demuxer_previous_frame(self->pdraw, self->demuxer); - if (res < 0) - ULOG_ERRNO("pdraw_be_demuxer_previous_frame", -res); -} - - -void pdraw_desktop_next_frame(struct pdraw_desktop *self) -{ - int res = pdraw_be_demuxer_next_frame(self->pdraw, self->demuxer); - if (res < 0) - ULOG_ERRNO("pdraw_be_demuxer_next_frame", -res); -} - - -void pdraw_desktop_seek_back_10s(struct pdraw_desktop *self) -{ - int res = pdraw_be_demuxer_seek_back( - self->pdraw, self->demuxer, 10000000, 0); - if (res < 0) - ULOG_ERRNO("pdraw_be_demuxer_seek_back", -res); -} - - -void pdraw_desktop_seek_forward_10s(struct pdraw_desktop *self) -{ - int res = pdraw_be_demuxer_seek_forward( - self->pdraw, self->demuxer, 10000000, 0); - if (res < 0) - ULOG_ERRNO("pdraw_be_demuxer_seek_forward", -res); -} - - -void pdraw_desktop_goto_beginning(struct pdraw_desktop *self) -{ - int res = pdraw_be_demuxer_seek_to(self->pdraw, self->demuxer, 0, 1); - if (res < 0) - ULOG_ERRNO("pdraw_be_demuxer_seek_to", -res); -} - - -void pdraw_desktop_goto_end(struct pdraw_desktop *self) -{ - int res = pdraw_be_demuxer_seek_to( - self->pdraw, self->demuxer, (uint64_t)-1, 1); - if (res < 0) - ULOG_ERRNO("pdraw_be_demuxer_seek_to", -res); -} - - -void pdraw_desktop_dump_pipeline(struct pdraw_desktop *self) -{ - int res; - uint64_t epoch_sec = 0; - int32_t utc_offset_sec = 0; - struct tm tm; - char *file_path = NULL; - - time_local_get(&epoch_sec, &utc_offset_sec); - time_local_to_tm(epoch_sec, utc_offset_sec, &tm); - - res = asprintf(&file_path, - "pdraw_pipeline_%04d%02d%02d_%02d%02d%02d_%d.dot", - tm.tm_year + 1900, - tm.tm_mon + 1, - tm.tm_mday, - tm.tm_hour, - tm.tm_min, - tm.tm_sec, - getpid()); - if (res <= 0) { - ULOG_ERRNO("asprintf", ENOMEM); - return; - } - - res = pdraw_be_dump_pipeline(self->pdraw, file_path); - if (res < 0) - ULOG_ERRNO("pdraw_be_dump_pipeline", -res); - - free(file_path); -} - - -void pdraw_desktop_change_scheduling_mode(struct pdraw_desktop *self) -{ - int err; - unsigned int i; - struct pdraw_video_renderer_params params; - - for (i = 0; i < self->renderer_count; i++) { - err = pdraw_be_video_renderer_get_params( - self->pdraw, self->renderer[i], ¶ms); - if (err < 0) { - ULOG_ERRNO("pdraw_be_video_renderer_get_params(%u)", - -err, - i); - return; - } - params.scheduling_mode++; - if (params.scheduling_mode >= - PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_MAX) { - params.scheduling_mode = - PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ASAP; - } - err = pdraw_be_video_renderer_set_params( - self->pdraw, self->renderer[i], ¶ms); - if (err < 0) { - ULOG_ERRNO("pdraw_be_video_renderer_set_params(%u)", - -err, - i); - return; - } - ULOGI("set renderer %u mode to %s", - i, - pdraw_video_renderer_scheduling_mode_str( - params.scheduling_mode)); - } -} - - -void pdraw_desktop_change_fill_mode(struct pdraw_desktop *self) -{ - int err; - unsigned int i; - struct pdraw_video_renderer_params params; - - for (i = 0; i < self->renderer_count; i++) { - err = pdraw_be_video_renderer_get_params( - self->pdraw, self->renderer[i], ¶ms); - if (err < 0) { - ULOG_ERRNO("pdraw_be_video_renderer_get_params(%u)", - -err, - i); - return; - } - params.fill_mode++; - if (params.fill_mode >= PDRAW_VIDEO_RENDERER_FILL_MODE_MAX) - params.fill_mode = PDRAW_VIDEO_RENDERER_FILL_MODE_FIT; - err = pdraw_be_video_renderer_set_params( - self->pdraw, self->renderer[i], ¶ms); - if (err < 0) { - ULOG_ERRNO("pdraw_be_video_renderer_set_params(%u)", - -err, - i); - return; - } - ULOGI("set renderer %u mode to %s", - i, - pdraw_video_renderer_fill_mode_str(params.fill_mode)); - } -} - - -static void -stop_resp_cb(struct pdraw_backend *pdraw, int status, void *userdata) -{ - struct pdraw_desktop *self = userdata; - - ULOGI("%s status=%d(%s)", __func__, status, strerror(-status)); - - pdraw_desktop_ui_send_user_event(self, - PDRAW_DESKTOP_EVENT_STOP_RESP, - (void *)(intptr_t)status, - NULL); -} - - -static void media_added_cb(struct pdraw_backend *pdraw, - const struct pdraw_media_info *info, - void *userdata) -{ - struct pdraw_desktop *self = userdata; - - ULOGI("%s id=%d path=%s", __func__, info->id, info->path); - - if ((info->type == PDRAW_MEDIA_TYPE_VIDEO) && - (info->video.format == VDEF_FRAME_TYPE_RAW)) { - pdraw_desktop_ui_send_user_event( - self, - PDRAW_DESKTOP_EVENT_ADD_RENDERER, - (void *)(intptr_t)info->id, - NULL); - } -} - - -static void media_removed_cb(struct pdraw_backend *pdraw, - const struct pdraw_media_info *info, - void *userdata) -{ - ULOGI("%s id=%d path=%s", __func__, info->id, info->path); -} - - -static void -socket_created_cb(struct pdraw_backend *pdraw, int fd, void *userdata) -{ - ULOGI("%s fd=%d", __func__, fd); -} - - -static void open_resp_cb(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - int status, - void *userdata) -{ - struct pdraw_desktop *self = userdata; - - ULOGI("%s status=%d(%s)", __func__, status, strerror(-status)); - - pdraw_desktop_ui_send_user_event(self, - PDRAW_DESKTOP_EVENT_OPEN_RESP, - (void *)(intptr_t)status, - NULL); -} - - -static void close_resp_cb(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - int status, - void *userdata) -{ - int res; - struct pdraw_desktop *self = userdata; - - ULOGI("%s status=%d(%s)", __func__, status, strerror(-status)); - - if (self->demuxer != NULL) { - res = pdraw_be_demuxer_destroy(self->pdraw, self->demuxer); - if (res < 0) - ULOG_ERRNO("pdraw_be_demuxer_destroy", -res); - self->demuxer = NULL; - } - res = pdraw_be_stop(self->pdraw); - if (res < 0) - ULOG_ERRNO("pdraw_be_stop", -res); -} - - -static void unrecoverable_error_cb(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - void *userdata) -{ - struct pdraw_desktop *self = userdata; - - ULOGI("%s", __func__); - - pdraw_desktop_ui_send_user_event( - self, PDRAW_DESKTOP_EVENT_UNRECOVERABLE_ERROR, NULL, NULL); -} - - -static int select_media_cb(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - const struct pdraw_demuxer_media *medias, - size_t count, - void *userdata) -{ - struct pdraw_desktop *self = userdata; - int id, ids = 0; - char s[100]; - char *str = s, *id_str = NULL, *temp = NULL; - unsigned int default_media_count = 0; - size_t slen; - if (count == 0) - return -ENOENT; - if (count == 1) { - self->demuxer_media_count = 1; - return 1 << medias[0].media_id; - } - - for (size_t i = 0; i < count; i++) { - if (medias[i].is_default) - default_media_count++; - } - - if (self->demuxer_media_list) { - str = self->demuxer_media_list; - goto parse; - } - - printf("Select demuxer media id(s);\n" - "either emtpy/zero for the default media set," - "a single media id (e.g. \"1\")\n" - "or a comma-separated list of media ids (e.g. \"1,3\"):\n"); - for (size_t i = 0; i < count; i++) { - printf(" %c %d: [%s] %s\n", - medias[i].is_default ? '*' : '-', - medias[i].media_id, - vmeta_camera_type_to_str( - medias[i].session_meta.camera_type), - medias[i].name); - } - printf(" > "); - if (!fgets(s, sizeof(s), stdin)) { - printf("Unable to read input, using default media\n"); - self->demuxer_media_count = default_media_count; - return 0; - } - slen = strlen(s); - if (slen > 0 && s[slen - 1] == '\n') - s[slen - 1] = '\0'; - slen = strlen(s); - if (slen == 0) { - self->demuxer_media_count = default_media_count; - return 0; - } - -parse: - if (strchr(str, ',')) { - /* The string has multiple parts */ - id_str = strtok_r(str, ",", &temp); - if (id_str == NULL) { - /* strtok_r returning NULL means that the string is - * empty */ - printf("Unable to read input, using default media\n"); - self->demuxer_media_count = default_media_count; - return 0; - } else { - while (id_str) { - id = atoi(id_str); - ids |= (1 << id); - self->demuxer_media_count++; - id_str = strtok_r(NULL, ",", &temp); - } - } - } else { - /* Only a single number */ - id = atoi(str); - if (id == 0) { - ids = 0; - self->demuxer_media_count = default_media_count; - } else { - ids = (1 << id); - self->demuxer_media_count = 1; - } - } - return ids; -} - - -static void ready_to_play_cb(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - int ready, - void *userdata) -{ - struct pdraw_desktop *self = userdata; - - ULOGI("%s ready=%d", __func__, ready); - - pdraw_desktop_ui_send_user_event(self, - PDRAW_DESKTOP_EVENT_READY_TO_PLAY, - (void *)(intptr_t)ready, - NULL); -} - - -static void end_of_range_cb(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - uint64_t timestamp, - void *userdata) -{ - ULOGI("%s timestamp=%" PRIu64, __func__, timestamp); -} - - -static void play_resp_cb(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - int status, - uint64_t timestamp, - float speed, - void *userdata) -{ - struct pdraw_desktop *self = userdata; - - ULOGI("%s status=%d(%s) timestamp=%" PRIu64 " speed=%f", - __func__, - status, - strerror(-status), - timestamp, - speed); - - pdraw_desktop_ui_send_user_event(self, - PDRAW_DESKTOP_EVENT_PLAY_RESP, - (void *)(intptr_t)status, - (void *)(intptr_t)timestamp); -} - - -static void pause_resp_cb(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - int status, - uint64_t timestamp, - void *userdata) -{ - struct pdraw_desktop *self = userdata; - - ULOGI("%s status=%d(%s) timestamp=%" PRIu64, - __func__, - status, - strerror(-status), - timestamp); - - pdraw_desktop_ui_send_user_event(self, - PDRAW_DESKTOP_EVENT_PAUSE_RESP, - (void *)(intptr_t)status, - (void *)(intptr_t)timestamp); -} - - -static void seek_resp_cb(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - int status, - uint64_t timestamp, - float speed, - void *userdata) -{ - struct pdraw_desktop *self = userdata; - - ULOGI("%s status=%d(%s) timestamp=%" PRIu64 " speed=%f", - __func__, - status, - strerror(-status), - timestamp, - speed); - - pdraw_desktop_ui_send_user_event(self, - PDRAW_DESKTOP_EVENT_SEEK_RESP, - (void *)(intptr_t)status, - (void *)(intptr_t)timestamp); -} - - -static void renderer_media_removed_cb(struct pdraw_backend *pdraw, - struct pdraw_video_renderer *renderer, - const struct pdraw_media_info *info, - void *userdata) -{ - struct pdraw_desktop *self = userdata; - for (unsigned int i = 0; i < self->renderer_count; i++) { - if (self->renderer_media_id[i] == info->id) { - self->renderer_media_id[i] = 0; - break; - } - } - return; -} - - -static int load_texture_cb(struct pdraw_backend *pdraw, - struct pdraw_video_renderer *renderer, - unsigned int texture_width, - unsigned int texture_height, - const struct pdraw_media_info *media_info, - struct mbuf_raw_video_frame *frame, - const void *frame_userdata, - size_t frame_userdata_len, - void *userdata) -{ - struct pdraw_desktop *self = userdata; - return pdraw_desktop_ext_tex_load(self, - pdraw, - renderer, - media_info, - frame, - frame_userdata, - frame_userdata_len); -} - - -static void render_overlay_cb(struct pdraw_backend *pdraw, - struct pdraw_video_renderer *renderer, - const struct pdraw_rect *render_pos, - const struct pdraw_rect *content_pos, - const float *view_mat, - const float *proj_mat, - const struct pdraw_media_info *media_info, - struct vmeta_frame *frame_meta, - const struct pdraw_video_frame_extra *frame_extra, - void *userdata) -{ - int res; - struct pdraw_desktop *self = userdata; - struct pdraw_gles2hud *gles2hud = NULL; - float view_proj_mat[16] = { - 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1.}; - - if ((frame_meta == NULL) || (frame_extra == NULL)) - return; - - /* Find the corresponding gles2hud instance */ - for (unsigned int i = 0; i < self->renderer_count; i++) { - if (self->renderer[i] != renderer) - continue; - gles2hud = self->gles2hud[i]; - break; - } - - if (gles2hud == NULL) - return; - - struct pdraw_gles2hud_controller_meta ctrl_meta; - memset(&ctrl_meta, 0, sizeof(ctrl_meta)); - ctrl_meta.battery_percentage = self->skyctrl_battery_percentage; - ctrl_meta.location = self->skyctrl_location; - struct pdraw_gles2hud_drone_meta drone_meta; - memset(&drone_meta, 0, sizeof(drone_meta)); - if ((self->rec_start != 0) && (self->rec_stop == 0)) { - time_t tm; - time(&tm); - uint64_t rec_dur = - ((uint64_t)tm * 1000 > self->rec_start) - ? (uint64_t)tm * 1000 - self->rec_start - : 0; - drone_meta.recording_duration = rec_dur * 1000; - } else { - drone_meta.recording_duration = 0; - } - - res = pdraw_gles2hud_render(gles2hud, - self->hud_type, - render_pos, - content_pos, - view_proj_mat, - media_info, - frame_meta, - frame_extra, - &ctrl_meta, - &drone_meta); - if (res < 0) - ULOG_ERRNO("pdraw_gles2hud_render", -res); -} - - -static const struct pdraw_backend_cbs be_cbs = { - .stop_resp = &stop_resp_cb, - .media_added = &media_added_cb, - .media_removed = &media_removed_cb, - .socket_created = &socket_created_cb, -}; - - -static const struct pdraw_backend_demuxer_cbs demuxer_cbs = { - .open_resp = &open_resp_cb, - .close_resp = &close_resp_cb, - .unrecoverable_error = &unrecoverable_error_cb, - .select_media = &select_media_cb, - .ready_to_play = &ready_to_play_cb, - .end_of_range = &end_of_range_cb, - .play_resp = &play_resp_cb, - .pause_resp = &pause_resp_cb, - .seek_resp = &seek_resp_cb, -}; - - -const struct pdraw_backend_video_renderer_cbs render_cbs = { - .media_removed = &renderer_media_removed_cb, - .load_texture = &load_texture_cb, - .render_overlay = &render_overlay_cb, -}; - - -static void sighandler(int signum) -{ - ULOGI("stopping..."); - - pdraw_desktop_ui_send_quit_event(); - - signal(SIGINT, SIG_DFL); -} - - -static void welcome(void) -{ - printf(" ___ ___ ___ __\n" - " | _ \\ \\ _ _ /_\\ \\ / /\n" - " | _/ |) | '_/ _ \\ \\/\\/ /\n" - " |_| |___/|_|/_/ \\_\\_/\\_/\n\n"); - printf("Parrot Drones Awesome Video Viewer\n" - "Copyright (c) 2016 Aurelien Barre\n" - "Copyright (c) 2017 Parrot Drones SAS\n\n"); -} - - -enum args_id { - ARGS_ID_HMD = 256, - ARGS_ID_ZEBRAS, - ARGS_ID_EXT_TEX, - ARGS_ID_DEMUX, - ARGS_ID_SCHEDMODE, - ARGS_ID_FILLMODE, -}; - - -static const char short_options[] = "hu:i:s:c:S:C:FTH:"; - - -static const struct option long_options[] = { - {"help", no_argument, NULL, 'h'}, - {"url", required_argument, NULL, 'u'}, - {"ip", required_argument, NULL, 'i'}, - {"lstrmp", required_argument, NULL, 's'}, - {"lctrlp", required_argument, NULL, 'c'}, - {"rstrmp", required_argument, NULL, 'S'}, - {"rctrlp", required_argument, NULL, 'C'}, - {"demux", required_argument, NULL, ARGS_ID_DEMUX}, - {"fullscreen", no_argument, NULL, 'F'}, - {"always-on-top", no_argument, NULL, 'T'}, - {"hud", required_argument, NULL, 'H'}, - {"hmd", required_argument, NULL, ARGS_ID_HMD}, - {"zebras", required_argument, NULL, ARGS_ID_ZEBRAS}, - {"ext-tex", no_argument, NULL, ARGS_ID_EXT_TEX}, - {"tex-mex", no_argument, NULL, ARGS_ID_EXT_TEX}, - {"sched-mode", required_argument, NULL, ARGS_ID_SCHEDMODE}, - {"fill-mode", required_argument, NULL, ARGS_ID_FILLMODE}, - {0, 0, 0, 0}, -}; - - -static void usage(char *prog_name) -{ - printf("Usage: %s [options]\n\n" - "Options:\n\n" - " -h | --help " - "Print this message\n\n" - " -u | --url " - "Stream URL (rtsp://*) or filename (*.mp4)\n\n" - " -i | --ip " - "Direct RTP/AVP H.264 reception with a remote IP address\n" - " " - "(optional; local ports must also be configured)\n\n" - " -s | --lstrmp " - "Local stream port for direct RTP/AVP reception (required)\n\n" - " -c | --lctrlp " - "Local control port for direct RTP/AVP reception (required)\n\n" - " -S | --rstrmp " - "Remote stream port for direct RTP/AVP reception (optional)\n\n" - " -C | --rctrlp " - "Remote control port for direct RTP/AVP reception (optional)\n\n" - " --demux " - "Optional demuxer media selection list, 0 for default " - "(e.g. \"0\" or \"1,3,5\");\n" - " " - "if not specified the user will be prompted if necessary\n\n" - " -F | --fullscreen " - "Start in full-screen mode\n\n" - " -T | --always-on-top " - "Force window to stay on top\n\n" - " -H | --hud " - "Enable the HUD (type: 0=piloting, 1=imaging)\n\n" - " --hmd " - "HMD distortion correction with model id\n" - " " - "(0='Parrot Cockpitglasses', 1='Parrot Cockpitglasses 2')\n\n" - " --zebras " - "Enable overexposure zebras (threshold: [0.0 .. 1.0],\n" - " " - "default: 0.95, out of range means default)\n\n" - " --ext-tex " - "Enable testing the external texture loading callback\n\n" - " --sched-mode " - "Set default renderer scheduling mode (ASAP, ADAPTIVE)\n\n" - " --fill-mode " - "Set default renderer fill-mode (FIT, CROP, FIT_PAD_BLUR_CROP," - "FIT_PAD_BLUR_EXTEND)\n\n", - prog_name); -} - - -static int summary(struct pdraw_desktop *self) -{ - if (self->is_file) { - printf("Offline playing of file '%s'\n\n", self->url); - } else if (self->url != NULL) { - printf("Streaming from URL '%s'\n\n", self->url); - } else if ((self->local_stream_port != 0) && - (self->local_control_port != 0)) { - printf("Direct RTP/AVP H.264 reception: %s->%s " - "RTP:%d->%d RTCP:%d<->%d\n\n", - (self->remote_addr) ? self->remote_addr : "(any)", - (self->local_addr) ? self->local_addr : "(any)", - self->remote_stream_port, - self->local_stream_port, - self->remote_control_port, - self->local_control_port); - } else { - printf("Nothing to do...\n\n"); - return -EINVAL; - } - return 0; -} - - -static enum pdraw_video_renderer_scheduling_mode -parse_scheduling_mode(const char *value) -{ - enum pdraw_video_renderer_scheduling_mode fm; - for (fm = 0; fm < PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_MAX; fm++) { - if (strcasecmp(value, - pdraw_video_renderer_scheduling_mode_str(fm)) == - 0) - return fm; - } - fm = PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ASAP; - printf("Invalid fill mode value '%s', using '%s' instead", - value, - pdraw_video_renderer_scheduling_mode_str(fm)); - return fm; -} - - -static enum pdraw_video_renderer_fill_mode parse_fill_mode(const char *value) -{ - enum pdraw_video_renderer_fill_mode fm; - for (fm = 0; fm < PDRAW_VIDEO_RENDERER_FILL_MODE_MAX; fm++) { - if (strcasecmp(value, pdraw_video_renderer_fill_mode_str(fm)) == - 0) - return fm; - } - fm = PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_EXTEND; - printf("Invalid fill mode value '%s', using '%s' instead", - value, - pdraw_video_renderer_fill_mode_str(fm)); - return fm; -} - - -int main(int argc, char **argv) -{ - int res, status = EXIT_SUCCESS; - int idx, c; - struct pdraw_desktop *self = NULL; - - welcome(); - -#ifdef _WIN32 - /* Initialize winsock API */ - WSADATA wsadata; - WSAStartup(MAKEWORD(2, 0), &wsadata); -#endif /* _WIN32 */ - - self = calloc(1, sizeof(*self)); - if (self == NULL) { - ULOG_ERRNO("calloc", ENOMEM); - status = EXIT_FAILURE; - goto out; - } - self->speed = 1.0; - self->speed_sign = 1; - self->skyctrl_battery_percentage = 255; - self->default_scheduling_mode = - PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ASAP; - self->default_fill_mode = - PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_EXTEND; - - /* Command-line parameters */ - while ((c = getopt_long( - argc, argv, short_options, long_options, &idx)) != -1) { - switch (c) { - case 0: - break; - - case 'h': - usage(argv[0]); - goto out; - - case 'u': - free(self->url); - self->url = strdup(optarg); - if ((self->url) && - ((strlen(self->url) <= 7) || - (strncmp(self->url, "rtsp://", 7) != 0))) - self->is_file = 1; - break; - - case 'i': - free(self->remote_addr); - self->remote_addr = strdup(optarg); - break; - - case 's': - self->local_stream_port = atoi(optarg); - break; - - case 'c': - self->local_control_port = atoi(optarg); - break; - - case 'S': - self->remote_stream_port = atoi(optarg); - break; - - case 'C': - self->remote_control_port = atoi(optarg); - break; - - case ARGS_ID_DEMUX: - free(self->demuxer_media_list); - self->demuxer_media_list = strdup(optarg); - break; - - case 'F': - self->fullscreen = 1; - break; - - case 'T': - self->always_on_top = 1; - break; - - case 'H': - self->hud_type = atoi(optarg); - self->enable_hud = 1; - break; - - case ARGS_ID_HMD: - self->hmd_model = atoi(optarg); - self->enable_hmd = 1; - break; - - case ARGS_ID_ZEBRAS: - self->enable_zebras = 1; - self->zebras_threshold = atof(optarg); - if ((self->zebras_threshold < 0.f) || - (self->zebras_threshold > 1.f)) - self->zebras_threshold = DEFAULT_ZEBRAS_THRES; - break; - - case ARGS_ID_EXT_TEX: - self->ext_tex = 1; - break; - - case ARGS_ID_SCHEDMODE: - self->default_scheduling_mode = - parse_scheduling_mode(optarg); - break; - - case ARGS_ID_FILLMODE: - self->default_fill_mode = parse_fill_mode(optarg); - break; - - default: - usage(argv[0]); - status = EXIT_FAILURE; - goto out; - } - } - - res = summary(self); - if (res < 0) { - usage(argv[0]); - status = EXIT_FAILURE; - goto out; - } - - /* Create the UI */ - res = pdraw_desktop_ui_init(self); - if (res < 0) { - status = EXIT_FAILURE; - goto out; - } - - signal(SIGINT, sighandler); - - /* Create PDrAW instance */ - res = pdraw_be_new(&be_cbs, self, &self->pdraw); - if (res < 0) { - ULOG_ERRNO("pdraw_be_new", -res); - status = EXIT_FAILURE; - goto out; - } - res = pdraw_be_set_friendly_name_setting(self->pdraw, APP_NAME); - if (res < 0) - ULOG_ERRNO("pdraw_be_set_friendly_name_setting", -res); - res = pdraw_be_set_serial_number_setting(self->pdraw, - "00000000"); /* TODO */ - if (res < 0) - ULOG_ERRNO("pdraw_be_set_serial_number_setting", -res); - res = pdraw_be_set_software_version_setting(self->pdraw, - "0.0.0"); /* TODO */ - if (res < 0) - ULOG_ERRNO("pdraw_be_set_software_version_setting", -res); - res = pdraw_be_set_hmd_model_setting(self->pdraw, self->hmd_model); - if (res < 0) - ULOG_ERRNO("pdraw_be_set_hmd_model_setting", -res); - - pdraw_desktop_ui_send_user_event( - self, PDRAW_DESKTOP_EVENT_OPEN, NULL, NULL); - - /* Run the UI loop */ - pdraw_desktop_ui_loop(self); - -out: - if (self != NULL) { - pdraw_desktop_ext_tex_cleanup(self); - pdraw_desktop_ui_destroy(self); - if (self->pdraw != NULL) { - res = pdraw_be_destroy(self->pdraw); - if (res < 0) - ULOG_ERRNO("pdraw_be_destroy", -res); - } - free(self->url); - free(self->local_addr); - free(self->remote_addr); - free(self->demuxer_media_list); - free(self); - } - -#ifdef _WIN32 - /* Cleanup winsock API */ - WSACleanup(); -#endif /* _WIN32 */ - - printf("\nHasta la vista, PDrAW!\n\n"); - exit(status); -} +/** + * Parrot Drones Awesome Video Viewer + * Desktop application + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 "pdraw_desktop.h" + +ULOG_DECLARE_TAG(pdraw_desktop); + + +static const struct pdraw_backend_demuxer_cbs demuxer_cbs; + + +void pdraw_desktop_open(struct pdraw_desktop *self) +{ + int res; + if (self->url != NULL) { + res = pdraw_be_demuxer_new_from_url(self->pdraw, + self->url, + &demuxer_cbs, + self, + &self->demuxer); + if (res < 0) + ULOG_ERRNO("pdraw_be_demuxer_new_from_url", -res); + } else { + res = pdraw_be_demuxer_new_single_stream( + self->pdraw, + self->local_addr, + self->local_stream_port, + self->local_control_port, + self->remote_addr, + self->remote_stream_port, + self->remote_control_port, + &demuxer_cbs, + self, + &self->demuxer); + if (res < 0) { + ULOG_ERRNO("pdraw_be_demuxer_new_single_stream", -res); + return; + } + } +} + + +void pdraw_desktop_close(struct pdraw_desktop *self) +{ + int res; + for (unsigned int i = 0; i < self->renderer_count; i++) { + if (self->gles2hud[i] != NULL) { + res = pdraw_gles2hud_destroy(self->gles2hud[i]); + if (res < 0) + ULOG_ERRNO("pdraw_gles2hud_destroy", -res); + self->gles2hud[i] = NULL; + } + if (self->renderer[i] != NULL) { + res = pdraw_be_video_renderer_destroy( + self->pdraw, self->renderer[i]); + if (res < 0) + ULOG_ERRNO("pdraw_be_video_renderer_destroy", + -res); + self->renderer[i] = NULL; + } + } + if (self->demuxer != NULL) { + res = pdraw_be_demuxer_close(self->pdraw, self->demuxer); + if (res < 0) + ULOG_ERRNO("pdraw_be_demuxer_close", -res); + } +} + + +void pdraw_desktop_toggle_play_pause(struct pdraw_desktop *self) +{ + int res; + if (pdraw_be_demuxer_is_paused(self->pdraw, self->demuxer) != 0) { + res = pdraw_be_demuxer_play_with_speed( + self->pdraw, + self->demuxer, + self->speed * self->speed_sign); + if (res < 0) + ULOG_ERRNO("pdraw_be_demuxer_play_with_speed", -res); + } else { + res = pdraw_be_demuxer_pause(self->pdraw, self->demuxer); + if (res < 0) + ULOG_ERRNO("pdraw_be_demuxer_pause", -res); + } +} + + +void pdraw_desktop_toggle_speed_sign(struct pdraw_desktop *self) +{ + int res; + self->speed_sign *= -1; + if (pdraw_be_demuxer_is_paused(self->pdraw, self->demuxer) == 0) { + res = pdraw_be_demuxer_play_with_speed( + self->pdraw, + self->demuxer, + self->speed * self->speed_sign); + if (res < 0) + ULOG_ERRNO("pdraw_be_demuxer_play_with_speed", -res); + } +} + + +void pdraw_desktop_speed_down(struct pdraw_desktop *self) +{ + int res; + self->speed /= 2; + if (pdraw_be_demuxer_is_paused(self->pdraw, self->demuxer) == 0) { + res = pdraw_be_demuxer_play_with_speed( + self->pdraw, + self->demuxer, + self->speed * self->speed_sign); + if (res < 0) + ULOG_ERRNO("pdraw_be_demuxer_play_with_speed", -res); + } +} + + +void pdraw_desktop_speed_up(struct pdraw_desktop *self) +{ + int res; + self->speed *= 2; + if (pdraw_be_demuxer_is_paused(self->pdraw, self->demuxer) == 0) { + res = pdraw_be_demuxer_play_with_speed( + self->pdraw, + self->demuxer, + self->speed * self->speed_sign); + if (res < 0) + ULOG_ERRNO("pdraw_be_demuxer_play_with_speed", -res); + } +} + + +void pdraw_desktop_previous_frame(struct pdraw_desktop *self) +{ + int res = pdraw_be_demuxer_previous_frame(self->pdraw, self->demuxer); + if (res < 0) + ULOG_ERRNO("pdraw_be_demuxer_previous_frame", -res); +} + + +void pdraw_desktop_next_frame(struct pdraw_desktop *self) +{ + int res = pdraw_be_demuxer_next_frame(self->pdraw, self->demuxer); + if (res < 0) + ULOG_ERRNO("pdraw_be_demuxer_next_frame", -res); +} + + +void pdraw_desktop_seek_back_10s(struct pdraw_desktop *self) +{ + int res = pdraw_be_demuxer_seek_back( + self->pdraw, self->demuxer, 10000000, 0); + if (res < 0) + ULOG_ERRNO("pdraw_be_demuxer_seek_back", -res); +} + + +void pdraw_desktop_seek_forward_10s(struct pdraw_desktop *self) +{ + int res = pdraw_be_demuxer_seek_forward( + self->pdraw, self->demuxer, 10000000, 0); + if (res < 0) + ULOG_ERRNO("pdraw_be_demuxer_seek_forward", -res); +} + + +void pdraw_desktop_goto_beginning(struct pdraw_desktop *self) +{ + int res = pdraw_be_demuxer_seek_to(self->pdraw, self->demuxer, 0, 1); + if (res < 0) + ULOG_ERRNO("pdraw_be_demuxer_seek_to", -res); +} + + +void pdraw_desktop_goto_end(struct pdraw_desktop *self) +{ + int res = pdraw_be_demuxer_seek_to( + self->pdraw, self->demuxer, (uint64_t)-1, 1); + if (res < 0) + ULOG_ERRNO("pdraw_be_demuxer_seek_to", -res); +} + + +void pdraw_desktop_dump_pipeline(struct pdraw_desktop *self) +{ + int res; + uint64_t epoch_sec = 0; + int32_t utc_offset_sec = 0; + struct tm tm; + char *file_path = NULL; + + time_local_get(&epoch_sec, &utc_offset_sec); + time_local_to_tm(epoch_sec, utc_offset_sec, &tm); + + res = asprintf(&file_path, + "pdraw_pipeline_%04d%02d%02d_%02d%02d%02d_%d.dot", + tm.tm_year + 1900, + tm.tm_mon + 1, + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + getpid()); + if (res <= 0) { + ULOG_ERRNO("asprintf", ENOMEM); + return; + } + + res = pdraw_be_dump_pipeline(self->pdraw, file_path); + if (res < 0) + ULOG_ERRNO("pdraw_be_dump_pipeline", -res); + + free(file_path); +} + + +void pdraw_desktop_change_scheduling_mode(struct pdraw_desktop *self) +{ + int err; + unsigned int i; + struct pdraw_video_renderer_params params; + + for (i = 0; i < self->renderer_count; i++) { + err = pdraw_be_video_renderer_get_params( + self->pdraw, self->renderer[i], ¶ms); + if (err < 0) { + ULOG_ERRNO("pdraw_be_video_renderer_get_params(%u)", + -err, + i); + return; + } + params.scheduling_mode++; + if (params.scheduling_mode >= + PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_MAX) { + params.scheduling_mode = + PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ASAP; + } + err = pdraw_be_video_renderer_set_params( + self->pdraw, self->renderer[i], ¶ms); + if (err < 0) { + ULOG_ERRNO("pdraw_be_video_renderer_set_params(%u)", + -err, + i); + return; + } + ULOGI("set renderer %u mode to %s", + i, + pdraw_video_renderer_scheduling_mode_str( + params.scheduling_mode)); + } +} + + +void pdraw_desktop_change_fill_mode(struct pdraw_desktop *self) +{ + int err; + unsigned int i; + struct pdraw_video_renderer_params params; + + for (i = 0; i < self->renderer_count; i++) { + err = pdraw_be_video_renderer_get_params( + self->pdraw, self->renderer[i], ¶ms); + if (err < 0) { + ULOG_ERRNO("pdraw_be_video_renderer_get_params(%u)", + -err, + i); + return; + } + params.fill_mode++; + if (params.fill_mode >= PDRAW_VIDEO_RENDERER_FILL_MODE_MAX) + params.fill_mode = PDRAW_VIDEO_RENDERER_FILL_MODE_FIT; + err = pdraw_be_video_renderer_set_params( + self->pdraw, self->renderer[i], ¶ms); + if (err < 0) { + ULOG_ERRNO("pdraw_be_video_renderer_set_params(%u)", + -err, + i); + return; + } + ULOGI("set renderer %u mode to %s", + i, + pdraw_video_renderer_fill_mode_str(params.fill_mode)); + } +} + + +static void +stop_resp_cb(struct pdraw_backend *pdraw, int status, void *userdata) +{ + struct pdraw_desktop *self = userdata; + + ULOGI("%s status=%d(%s)", __func__, status, strerror(-status)); + + pdraw_desktop_ui_send_user_event(self, + PDRAW_DESKTOP_EVENT_STOP_RESP, + (void *)(intptr_t)status, + NULL); +} + + +static void media_added_cb(struct pdraw_backend *pdraw, + const struct pdraw_media_info *info, + void *userdata) +{ + struct pdraw_desktop *self = userdata; + + ULOGI("%s id=%d path=%s", __func__, info->id, info->path); + + if ((info->type == PDRAW_MEDIA_TYPE_VIDEO) && + (info->video.format == VDEF_FRAME_TYPE_RAW)) { + pdraw_desktop_ui_send_user_event( + self, + PDRAW_DESKTOP_EVENT_ADD_RENDERER, + (void *)(intptr_t)info->id, + NULL); + } +} + + +static void media_removed_cb(struct pdraw_backend *pdraw, + const struct pdraw_media_info *info, + void *userdata) +{ + ULOGI("%s id=%d path=%s", __func__, info->id, info->path); +} + + +static void +socket_created_cb(struct pdraw_backend *pdraw, int fd, void *userdata) +{ + ULOGI("%s fd=%d", __func__, fd); +} + + +static void open_resp_cb(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + int status, + void *userdata) +{ + struct pdraw_desktop *self = userdata; + + ULOGI("%s status=%d(%s)", __func__, status, strerror(-status)); + + pdraw_desktop_ui_send_user_event(self, + PDRAW_DESKTOP_EVENT_OPEN_RESP, + (void *)(intptr_t)status, + NULL); +} + + +static void close_resp_cb(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + int status, + void *userdata) +{ + int res; + struct pdraw_desktop *self = userdata; + + ULOGI("%s status=%d(%s)", __func__, status, strerror(-status)); + + if (self->demuxer != NULL) { + res = pdraw_be_demuxer_destroy(self->pdraw, self->demuxer); + if (res < 0) + ULOG_ERRNO("pdraw_be_demuxer_destroy", -res); + self->demuxer = NULL; + } + res = pdraw_be_stop(self->pdraw); + if (res < 0) + ULOG_ERRNO("pdraw_be_stop", -res); +} + + +static void unrecoverable_error_cb(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + void *userdata) +{ + struct pdraw_desktop *self = userdata; + + ULOGI("%s", __func__); + + pdraw_desktop_ui_send_user_event( + self, PDRAW_DESKTOP_EVENT_UNRECOVERABLE_ERROR, NULL, NULL); +} + + +static int select_media_cb(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + const struct pdraw_demuxer_media *medias, + size_t count, + void *userdata) +{ + struct pdraw_desktop *self = userdata; + int id, ids = 0; + char s[100]; + char *str = s, *id_str = NULL, *temp = NULL; + unsigned int default_media_count = 0; + size_t slen; + if (count == 0) + return -ENOENT; + if (count == 1) { + self->demuxer_media_count = 1; + return 1 << medias[0].media_id; + } + + for (size_t i = 0; i < count; i++) { + if (medias[i].is_default) + default_media_count++; + } + + if (self->demuxer_media_list) { + str = self->demuxer_media_list; + goto parse; + } + + printf("Select demuxer media id(s);\n" + "either emtpy/zero for the default media set," + "a single media id (e.g. \"1\")\n" + "or a comma-separated list of media ids (e.g. \"1,3\"):\n"); + for (size_t i = 0; i < count; i++) { + printf(" %c %d: [%s] %s\n", + medias[i].is_default ? '*' : '-', + medias[i].media_id, + vmeta_camera_type_to_str( + medias[i].session_meta.camera_type), + medias[i].name); + } + printf(" > "); + if (!fgets(s, sizeof(s), stdin)) { + printf("Unable to read input, using default media\n"); + self->demuxer_media_count = default_media_count; + return 0; + } + slen = strlen(s); + if (slen > 0 && s[slen - 1] == '\n') + s[slen - 1] = '\0'; + slen = strlen(s); + if (slen == 0) { + self->demuxer_media_count = default_media_count; + return 0; + } + +parse: + if (strchr(str, ',')) { + /* The string has multiple parts */ + id_str = strtok_r(str, ",", &temp); + if (id_str == NULL) { + /* strtok_r returning NULL means that the string is + * empty */ + printf("Unable to read input, using default media\n"); + self->demuxer_media_count = default_media_count; + return 0; + } else { + while (id_str) { + id = atoi(id_str); + ids |= (1 << id); + self->demuxer_media_count++; + id_str = strtok_r(NULL, ",", &temp); + } + } + } else { + /* Only a single number */ + id = atoi(str); + if (id == 0) { + ids = 0; + self->demuxer_media_count = default_media_count; + } else { + ids = (1 << id); + self->demuxer_media_count = 1; + } + } + return ids; +} + + +static void ready_to_play_cb(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + int ready, + void *userdata) +{ + struct pdraw_desktop *self = userdata; + + ULOGI("%s ready=%d", __func__, ready); + + pdraw_desktop_ui_send_user_event(self, + PDRAW_DESKTOP_EVENT_READY_TO_PLAY, + (void *)(intptr_t)ready, + NULL); +} + + +static void end_of_range_cb(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + uint64_t timestamp, + void *userdata) +{ + ULOGI("%s timestamp=%" PRIu64, __func__, timestamp); +} + + +static void play_resp_cb(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + int status, + uint64_t timestamp, + float speed, + void *userdata) +{ + struct pdraw_desktop *self = userdata; + + ULOGI("%s status=%d(%s) timestamp=%" PRIu64 " speed=%f", + __func__, + status, + strerror(-status), + timestamp, + speed); + + pdraw_desktop_ui_send_user_event(self, + PDRAW_DESKTOP_EVENT_PLAY_RESP, + (void *)(intptr_t)status, + (void *)(intptr_t)timestamp); +} + + +static void pause_resp_cb(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + int status, + uint64_t timestamp, + void *userdata) +{ + struct pdraw_desktop *self = userdata; + + ULOGI("%s status=%d(%s) timestamp=%" PRIu64, + __func__, + status, + strerror(-status), + timestamp); + + pdraw_desktop_ui_send_user_event(self, + PDRAW_DESKTOP_EVENT_PAUSE_RESP, + (void *)(intptr_t)status, + (void *)(intptr_t)timestamp); +} + + +static void seek_resp_cb(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + int status, + uint64_t timestamp, + float speed, + void *userdata) +{ + struct pdraw_desktop *self = userdata; + + ULOGI("%s status=%d(%s) timestamp=%" PRIu64 " speed=%f", + __func__, + status, + strerror(-status), + timestamp, + speed); + + pdraw_desktop_ui_send_user_event(self, + PDRAW_DESKTOP_EVENT_SEEK_RESP, + (void *)(intptr_t)status, + (void *)(intptr_t)timestamp); +} + + +static void renderer_media_removed_cb(struct pdraw_backend *pdraw, + struct pdraw_video_renderer *renderer, + const struct pdraw_media_info *info, + void *userdata) +{ + struct pdraw_desktop *self = userdata; + for (unsigned int i = 0; i < self->renderer_count; i++) { + if (self->renderer_media_id[i] == info->id) { + self->renderer_media_id[i] = 0; + break; + } + } + return; +} + + +static int load_texture_cb(struct pdraw_backend *pdraw, + struct pdraw_video_renderer *renderer, + unsigned int texture_width, + unsigned int texture_height, + const struct pdraw_media_info *media_info, + struct mbuf_raw_video_frame *frame, + const void *frame_userdata, + size_t frame_userdata_len, + void *userdata) +{ + struct pdraw_desktop *self = userdata; + return pdraw_desktop_ext_tex_load(self, + pdraw, + renderer, + media_info, + frame, + frame_userdata, + frame_userdata_len); +} + + +static void render_overlay_cb(struct pdraw_backend *pdraw, + struct pdraw_video_renderer *renderer, + const struct pdraw_rect *render_pos, + const struct pdraw_rect *content_pos, + const float *view_mat, + const float *proj_mat, + const struct pdraw_media_info *media_info, + struct vmeta_frame *frame_meta, + const struct pdraw_video_frame_extra *frame_extra, + void *userdata) +{ + int res; + struct pdraw_desktop *self = userdata; + struct pdraw_gles2hud *gles2hud = NULL; + float view_proj_mat[16] = { + 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1.}; + + if ((frame_meta == NULL) || (frame_extra == NULL)) + return; + + /* Find the corresponding gles2hud instance */ + for (unsigned int i = 0; i < self->renderer_count; i++) { + if (self->renderer[i] != renderer) + continue; + gles2hud = self->gles2hud[i]; + break; + } + + if (gles2hud == NULL) + return; + + struct pdraw_gles2hud_controller_meta ctrl_meta; + memset(&ctrl_meta, 0, sizeof(ctrl_meta)); + ctrl_meta.battery_percentage = self->skyctrl_battery_percentage; + ctrl_meta.location = self->skyctrl_location; + struct pdraw_gles2hud_drone_meta drone_meta; + memset(&drone_meta, 0, sizeof(drone_meta)); + if ((self->rec_start != 0) && (self->rec_stop == 0)) { + time_t tm; + time(&tm); + uint64_t rec_dur = + ((uint64_t)tm * 1000 > self->rec_start) + ? (uint64_t)tm * 1000 - self->rec_start + : 0; + drone_meta.recording_duration = rec_dur * 1000; + } else { + drone_meta.recording_duration = 0; + } + + res = pdraw_gles2hud_render(gles2hud, + self->hud_type, + render_pos, + content_pos, + view_proj_mat, + media_info, + frame_meta, + frame_extra, + &ctrl_meta, + &drone_meta); + if (res < 0) + ULOG_ERRNO("pdraw_gles2hud_render", -res); +} + + +static const struct pdraw_backend_cbs be_cbs = { + .stop_resp = &stop_resp_cb, + .media_added = &media_added_cb, + .media_removed = &media_removed_cb, + .socket_created = &socket_created_cb, +}; + + +static const struct pdraw_backend_demuxer_cbs demuxer_cbs = { + .open_resp = &open_resp_cb, + .close_resp = &close_resp_cb, + .unrecoverable_error = &unrecoverable_error_cb, + .select_media = &select_media_cb, + .ready_to_play = &ready_to_play_cb, + .end_of_range = &end_of_range_cb, + .play_resp = &play_resp_cb, + .pause_resp = &pause_resp_cb, + .seek_resp = &seek_resp_cb, +}; + + +const struct pdraw_backend_video_renderer_cbs render_cbs = { + .media_removed = &renderer_media_removed_cb, + .load_texture = &load_texture_cb, + .render_overlay = &render_overlay_cb, +}; + + +static void sighandler(int signum) +{ + ULOGI("stopping..."); + + pdraw_desktop_ui_send_quit_event(); + + signal(SIGINT, SIG_DFL); +} + + +static void welcome(void) +{ + printf(" ___ ___ ___ __\n" + " | _ \\ \\ _ _ /_\\ \\ / /\n" + " | _/ |) | '_/ _ \\ \\/\\/ /\n" + " |_| |___/|_|/_/ \\_\\_/\\_/\n\n"); + printf("Parrot Drones Awesome Video Viewer\n" + "Copyright (c) 2016 Aurelien Barre\n" + "Copyright (c) 2017 Parrot Drones SAS\n\n"); +} + + +enum args_id { + ARGS_ID_HMD = 256, + ARGS_ID_ZEBRAS, + ARGS_ID_EXT_TEX, + ARGS_ID_DEMUX, + ARGS_ID_SCHEDMODE, + ARGS_ID_FILLMODE, +}; + + +static const char short_options[] = "hu:i:s:c:S:C:FTH:"; + + +static const struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"url", required_argument, NULL, 'u'}, + {"ip", required_argument, NULL, 'i'}, + {"lstrmp", required_argument, NULL, 's'}, + {"lctrlp", required_argument, NULL, 'c'}, + {"rstrmp", required_argument, NULL, 'S'}, + {"rctrlp", required_argument, NULL, 'C'}, + {"demux", required_argument, NULL, ARGS_ID_DEMUX}, + {"fullscreen", no_argument, NULL, 'F'}, + {"always-on-top", no_argument, NULL, 'T'}, + {"hud", required_argument, NULL, 'H'}, + {"hmd", required_argument, NULL, ARGS_ID_HMD}, + {"zebras", required_argument, NULL, ARGS_ID_ZEBRAS}, + {"ext-tex", no_argument, NULL, ARGS_ID_EXT_TEX}, + {"tex-mex", no_argument, NULL, ARGS_ID_EXT_TEX}, + {"sched-mode", required_argument, NULL, ARGS_ID_SCHEDMODE}, + {"fill-mode", required_argument, NULL, ARGS_ID_FILLMODE}, + {0, 0, 0, 0}, +}; + + +static void usage(char *prog_name) +{ + printf("Usage: %s [options]\n\n" + "Options:\n\n" + " -h | --help " + "Print this message\n\n" + " -u | --url " + "Stream URL (rtsp://*) or filename (*.mp4)\n\n" + " -i | --ip " + "Direct RTP/AVP H.264 reception with a remote IP address\n" + " " + "(optional; local ports must also be configured)\n\n" + " -s | --lstrmp " + "Local stream port for direct RTP/AVP reception (required)\n\n" + " -c | --lctrlp " + "Local control port for direct RTP/AVP reception (required)\n\n" + " -S | --rstrmp " + "Remote stream port for direct RTP/AVP reception (optional)\n\n" + " -C | --rctrlp " + "Remote control port for direct RTP/AVP reception (optional)\n\n" + " --demux " + "Optional demuxer media selection list, 0 for default " + "(e.g. \"0\" or \"1,3,5\");\n" + " " + "if not specified the user will be prompted if necessary\n\n" + " -F | --fullscreen " + "Start in full-screen mode\n\n" + " -T | --always-on-top " + "Force window to stay on top\n\n" + " -H | --hud " + "Enable the HUD (type: 0=piloting, 1=imaging)\n\n" + " --hmd " + "HMD distortion correction with model id\n" + " " + "(0='Parrot Cockpitglasses', 1='Parrot Cockpitglasses 2')\n\n" + " --zebras " + "Enable overexposure zebras (threshold: [0.0 .. 1.0],\n" + " " + "default: 0.95, out of range means default)\n\n" + " --ext-tex " + "Enable testing the external texture loading callback\n\n" + " --sched-mode " + "Set default renderer scheduling mode (ASAP, ADAPTIVE)\n\n" + " --fill-mode " + "Set default renderer fill-mode (FIT, CROP, FIT_PAD_BLUR_CROP," + "FIT_PAD_BLUR_EXTEND)\n\n", + prog_name); +} + + +static int summary(struct pdraw_desktop *self) +{ + if (self->is_file) { + printf("Offline playing of file '%s'\n\n", self->url); + } else if (self->url != NULL) { + printf("Streaming from URL '%s'\n\n", self->url); + } else if ((self->local_stream_port != 0) && + (self->local_control_port != 0)) { + printf("Direct RTP/AVP H.264 reception: %s->%s " + "RTP:%d->%d RTCP:%d<->%d\n\n", + (self->remote_addr) ? self->remote_addr : "(any)", + (self->local_addr) ? self->local_addr : "(any)", + self->remote_stream_port, + self->local_stream_port, + self->remote_control_port, + self->local_control_port); + } else { + printf("Nothing to do...\n\n"); + return -EINVAL; + } + return 0; +} + + +static enum pdraw_video_renderer_scheduling_mode +parse_scheduling_mode(const char *value) +{ + enum pdraw_video_renderer_scheduling_mode fm; + for (fm = 0; fm < PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_MAX; fm++) { + if (strcasecmp(value, + pdraw_video_renderer_scheduling_mode_str(fm)) == + 0) + return fm; + } + fm = PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ASAP; + printf("Invalid fill mode value '%s', using '%s' instead", + value, + pdraw_video_renderer_scheduling_mode_str(fm)); + return fm; +} + + +static enum pdraw_video_renderer_fill_mode parse_fill_mode(const char *value) +{ + enum pdraw_video_renderer_fill_mode fm; + for (fm = 0; fm < PDRAW_VIDEO_RENDERER_FILL_MODE_MAX; fm++) { + if (strcasecmp(value, pdraw_video_renderer_fill_mode_str(fm)) == + 0) + return fm; + } + fm = PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_EXTEND; + printf("Invalid fill mode value '%s', using '%s' instead", + value, + pdraw_video_renderer_fill_mode_str(fm)); + return fm; +} + + +int main(int argc, char **argv) +{ + int res, status = EXIT_SUCCESS; + int idx, c; + struct pdraw_desktop *self = NULL; + + welcome(); + +#ifdef _WIN32 + /* Initialize winsock API */ + WSADATA wsadata; + WSAStartup(MAKEWORD(2, 0), &wsadata); +#endif /* _WIN32 */ + + self = calloc(1, sizeof(*self)); + if (self == NULL) { + ULOG_ERRNO("calloc", ENOMEM); + status = EXIT_FAILURE; + goto out; + } + self->speed = 1.0; + self->speed_sign = 1; + self->skyctrl_battery_percentage = 255; + self->default_scheduling_mode = + PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ASAP; + self->default_fill_mode = + PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_EXTEND; + + /* Command-line parameters */ + while ((c = getopt_long( + argc, argv, short_options, long_options, &idx)) != -1) { + switch (c) { + case 0: + break; + + case 'h': + usage(argv[0]); + goto out; + + case 'u': + free(self->url); + self->url = strdup(optarg); + if ((self->url) && + ((strlen(self->url) <= 7) || + (strncmp(self->url, "rtsp://", 7) != 0))) + self->is_file = 1; + break; + + case 'i': + free(self->remote_addr); + self->remote_addr = strdup(optarg); + break; + + case 's': + self->local_stream_port = atoi(optarg); + break; + + case 'c': + self->local_control_port = atoi(optarg); + break; + + case 'S': + self->remote_stream_port = atoi(optarg); + break; + + case 'C': + self->remote_control_port = atoi(optarg); + break; + + case ARGS_ID_DEMUX: + free(self->demuxer_media_list); + self->demuxer_media_list = strdup(optarg); + break; + + case 'F': + self->fullscreen = 1; + break; + + case 'T': + self->always_on_top = 1; + break; + + case 'H': + self->hud_type = atoi(optarg); + self->enable_hud = 1; + break; + + case ARGS_ID_HMD: + self->hmd_model = atoi(optarg); + self->enable_hmd = 1; + break; + + case ARGS_ID_ZEBRAS: + self->enable_zebras = 1; + self->zebras_threshold = atof(optarg); + if ((self->zebras_threshold < 0.f) || + (self->zebras_threshold > 1.f)) + self->zebras_threshold = DEFAULT_ZEBRAS_THRES; + break; + + case ARGS_ID_EXT_TEX: + self->ext_tex = 1; + break; + + case ARGS_ID_SCHEDMODE: + self->default_scheduling_mode = + parse_scheduling_mode(optarg); + break; + + case ARGS_ID_FILLMODE: + self->default_fill_mode = parse_fill_mode(optarg); + break; + + default: + usage(argv[0]); + status = EXIT_FAILURE; + goto out; + } + } + + res = summary(self); + if (res < 0) { + usage(argv[0]); + status = EXIT_FAILURE; + goto out; + } + + /* Create the UI */ + res = pdraw_desktop_ui_init(self); + if (res < 0) { + status = EXIT_FAILURE; + goto out; + } + + signal(SIGINT, sighandler); + + /* Create PDrAW instance */ + res = pdraw_be_new(&be_cbs, self, &self->pdraw); + if (res < 0) { + ULOG_ERRNO("pdraw_be_new", -res); + status = EXIT_FAILURE; + goto out; + } + res = pdraw_be_set_friendly_name_setting(self->pdraw, APP_NAME); + if (res < 0) + ULOG_ERRNO("pdraw_be_set_friendly_name_setting", -res); + res = pdraw_be_set_serial_number_setting(self->pdraw, + "00000000"); /* TODO */ + if (res < 0) + ULOG_ERRNO("pdraw_be_set_serial_number_setting", -res); + res = pdraw_be_set_software_version_setting(self->pdraw, + "0.0.0"); /* TODO */ + if (res < 0) + ULOG_ERRNO("pdraw_be_set_software_version_setting", -res); + res = pdraw_be_set_hmd_model_setting(self->pdraw, self->hmd_model); + if (res < 0) + ULOG_ERRNO("pdraw_be_set_hmd_model_setting", -res); + + pdraw_desktop_ui_send_user_event( + self, PDRAW_DESKTOP_EVENT_OPEN, NULL, NULL); + + /* Run the UI loop */ + pdraw_desktop_ui_loop(self); + +out: + if (self != NULL) { + pdraw_desktop_ext_tex_cleanup(self); + pdraw_desktop_ui_destroy(self); + if (self->pdraw != NULL) { + res = pdraw_be_destroy(self->pdraw); + if (res < 0) + ULOG_ERRNO("pdraw_be_destroy", -res); + } + free(self->url); + free(self->local_addr); + free(self->remote_addr); + free(self->demuxer_media_list); + free(self); + } + +#ifdef _WIN32 + /* Cleanup winsock API */ + WSACleanup(); +#endif /* _WIN32 */ + + printf("\nHasta la vista, PDrAW!\n\n"); + exit(status); +} diff --git a/apps/pdraw_desktop/pdraw_desktop.h b/apps/pdraw_desktop/pdraw_desktop.h index 83c3fec..1212680 100644 --- a/apps/pdraw_desktop/pdraw_desktop.h +++ b/apps/pdraw_desktop/pdraw_desktop.h @@ -1,250 +1,250 @@ -/** - * Parrot Drones Awesome Video Viewer - * Desktop application - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_DESKTOP_H_ -#define _PDRAW_DESKTOP_H_ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define ULOG_TAG pdraw_desktop -#include - -#ifdef _WIN32 -# include -# undef near -# undef far -#endif /* !_WIN32 */ - -#ifndef __APPLE__ -# define GLFW_INCLUDE_ES2 -#endif -#ifdef _WIN32 -# include -#else -# include -#endif - -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - -#define APP_NAME "PDrAW" -#define RTSP_LIVE_URI "live" -#define DEFAULT_ZEBRAS_THRES (0.95f) -#define MAX_RENDERERS 9 - - -enum pdraw_desktop_event { - PDRAW_DESKTOP_EVENT_OPEN = 0, - PDRAW_DESKTOP_EVENT_OPEN_RESP, - PDRAW_DESKTOP_EVENT_UNRECOVERABLE_ERROR, - PDRAW_DESKTOP_EVENT_READY_TO_PLAY, - PDRAW_DESKTOP_EVENT_PLAY_RESP, - PDRAW_DESKTOP_EVENT_PAUSE_RESP, - PDRAW_DESKTOP_EVENT_SEEK_RESP, - PDRAW_DESKTOP_EVENT_STOP_RESP, - PDRAW_DESKTOP_EVENT_ADD_RENDERER, -}; - - -struct pdraw_desktop { - char *url; - int is_file; - char *local_addr; - uint16_t local_stream_port; - uint16_t local_control_port; - char *remote_addr; - uint16_t remote_stream_port; - uint16_t remote_control_port; - - SDL_Window *window; - SDL_GLContext gl_context; - uint32_t user_event; - unsigned int window_width; - unsigned int window_height; - int fullscreen; - int always_on_top; - - int enable_hud; - enum pdraw_gles2hud_type hud_type; - float hud_view_proj_mat[16]; - int enable_hmd; - enum pdraw_hmd_model hmd_model; - int enable_zebras; - float zebras_threshold; - - int stopped; - struct pdraw_backend *pdraw; - struct pdraw_demuxer *demuxer; - char *demuxer_media_list; - unsigned int demuxer_media_count; - unsigned int renderer_media_id[MAX_RENDERERS]; - struct pdraw_video_renderer *renderer[MAX_RENDERERS]; - struct pdraw_gles2hud *gles2hud[MAX_RENDERERS]; - struct pdraw_rect render_pos[MAX_RENDERERS]; - unsigned int renderer_count; - float speed; - int speed_sign; - - uint64_t rec_start; - uint64_t rec_stop; - uint8_t skyctrl_battery_percentage; - struct vmeta_location skyctrl_location; - - int ext_tex; - GLint ext_tex_program; - GLint ext_tex_yuv2rgb_matrix; - GLint ext_tex_yuv2rgb_offset; - GLuint ext_tex_textures[3]; - GLint ext_tex_uniform_samplers[3]; - GLint ext_tex_position_handle; - GLint ext_tex_texcoord_handle; - - enum pdraw_video_renderer_scheduling_mode default_scheduling_mode; - enum pdraw_video_renderer_fill_mode default_fill_mode; -}; - - -extern const struct pdraw_backend_video_renderer_cbs render_cbs; - - -void pdraw_desktop_open(struct pdraw_desktop *self); - - -void pdraw_desktop_close(struct pdraw_desktop *self); - - -void pdraw_desktop_toggle_play_pause(struct pdraw_desktop *self); - - -void pdraw_desktop_toggle_speed_sign(struct pdraw_desktop *self); - - -void pdraw_desktop_speed_down(struct pdraw_desktop *self); - - -void pdraw_desktop_speed_up(struct pdraw_desktop *self); - - -void pdraw_desktop_previous_frame(struct pdraw_desktop *self); - - -void pdraw_desktop_next_frame(struct pdraw_desktop *self); - - -void pdraw_desktop_seek_back_10s(struct pdraw_desktop *self); - - -void pdraw_desktop_seek_forward_10s(struct pdraw_desktop *self); - - -void pdraw_desktop_goto_beginning(struct pdraw_desktop *self); - - -void pdraw_desktop_goto_end(struct pdraw_desktop *self); - - -void pdraw_desktop_dump_pipeline(struct pdraw_desktop *self); - - -void pdraw_desktop_change_scheduling_mode(struct pdraw_desktop *self); - - -void pdraw_desktop_change_fill_mode(struct pdraw_desktop *self); - - -int pdraw_desktop_ui_init(struct pdraw_desktop *self); - - -int pdraw_desktop_ui_destroy(struct pdraw_desktop *self); - - -void pdraw_desktop_ui_send_quit_event(void); - - -void pdraw_desktop_ui_send_user_event(struct pdraw_desktop *self, - enum pdraw_desktop_event event, - void *data1, - void *data2); - - -void pdraw_desktop_ui_add_media(struct pdraw_desktop *self, - unsigned int media_id); - - -void pdraw_desktop_ui_resize(struct pdraw_desktop *self); - - -int pdraw_desktop_ui_loop(struct pdraw_desktop *self); - - -void pdraw_desktop_view_create_matrices(struct pdraw_desktop *self, - unsigned int width, - unsigned int height, - float *view_mat, - float *proj_mat, - float near, - float far); - - -int pdraw_desktop_ext_tex_setup(struct pdraw_desktop *self); - - -int pdraw_desktop_ext_tex_cleanup(struct pdraw_desktop *self); - - -int pdraw_desktop_ext_tex_load(struct pdraw_desktop *self, - struct pdraw_backend *pdraw, - struct pdraw_video_renderer *renderer, - const struct pdraw_media_info *media_info, - struct mbuf_raw_video_frame *frame, - const void *frame_userdata, - size_t frame_userdata_len); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* !_PDRAW_DESKTOP_H_ */ +/** + * Parrot Drones Awesome Video Viewer + * Desktop application + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_DESKTOP_H_ +#define _PDRAW_DESKTOP_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ULOG_TAG pdraw_desktop +#include + +#ifdef _WIN32 +# include +# undef near +# undef far +#endif /* !_WIN32 */ + +#ifndef __APPLE__ +# define GLFW_INCLUDE_ES2 +#endif +#ifdef _WIN32 +# include +#else +# include +#endif + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define APP_NAME "PDrAW" +#define RTSP_LIVE_URI "live" +#define DEFAULT_ZEBRAS_THRES (0.95f) +#define MAX_RENDERERS 9 + + +enum pdraw_desktop_event { + PDRAW_DESKTOP_EVENT_OPEN = 0, + PDRAW_DESKTOP_EVENT_OPEN_RESP, + PDRAW_DESKTOP_EVENT_UNRECOVERABLE_ERROR, + PDRAW_DESKTOP_EVENT_READY_TO_PLAY, + PDRAW_DESKTOP_EVENT_PLAY_RESP, + PDRAW_DESKTOP_EVENT_PAUSE_RESP, + PDRAW_DESKTOP_EVENT_SEEK_RESP, + PDRAW_DESKTOP_EVENT_STOP_RESP, + PDRAW_DESKTOP_EVENT_ADD_RENDERER, +}; + + +struct pdraw_desktop { + char *url; + int is_file; + char *local_addr; + uint16_t local_stream_port; + uint16_t local_control_port; + char *remote_addr; + uint16_t remote_stream_port; + uint16_t remote_control_port; + + SDL_Window *window; + SDL_GLContext gl_context; + uint32_t user_event; + unsigned int window_width; + unsigned int window_height; + int fullscreen; + int always_on_top; + + int enable_hud; + enum pdraw_gles2hud_type hud_type; + float hud_view_proj_mat[16]; + int enable_hmd; + enum pdraw_hmd_model hmd_model; + int enable_zebras; + float zebras_threshold; + + int stopped; + struct pdraw_backend *pdraw; + struct pdraw_demuxer *demuxer; + char *demuxer_media_list; + unsigned int demuxer_media_count; + unsigned int renderer_media_id[MAX_RENDERERS]; + struct pdraw_video_renderer *renderer[MAX_RENDERERS]; + struct pdraw_gles2hud *gles2hud[MAX_RENDERERS]; + struct pdraw_rect render_pos[MAX_RENDERERS]; + unsigned int renderer_count; + float speed; + int speed_sign; + + uint64_t rec_start; + uint64_t rec_stop; + uint8_t skyctrl_battery_percentage; + struct vmeta_location skyctrl_location; + + int ext_tex; + GLint ext_tex_program; + GLint ext_tex_yuv2rgb_matrix; + GLint ext_tex_yuv2rgb_offset; + GLuint ext_tex_textures[3]; + GLint ext_tex_uniform_samplers[3]; + GLint ext_tex_position_handle; + GLint ext_tex_texcoord_handle; + + enum pdraw_video_renderer_scheduling_mode default_scheduling_mode; + enum pdraw_video_renderer_fill_mode default_fill_mode; +}; + + +extern const struct pdraw_backend_video_renderer_cbs render_cbs; + + +void pdraw_desktop_open(struct pdraw_desktop *self); + + +void pdraw_desktop_close(struct pdraw_desktop *self); + + +void pdraw_desktop_toggle_play_pause(struct pdraw_desktop *self); + + +void pdraw_desktop_toggle_speed_sign(struct pdraw_desktop *self); + + +void pdraw_desktop_speed_down(struct pdraw_desktop *self); + + +void pdraw_desktop_speed_up(struct pdraw_desktop *self); + + +void pdraw_desktop_previous_frame(struct pdraw_desktop *self); + + +void pdraw_desktop_next_frame(struct pdraw_desktop *self); + + +void pdraw_desktop_seek_back_10s(struct pdraw_desktop *self); + + +void pdraw_desktop_seek_forward_10s(struct pdraw_desktop *self); + + +void pdraw_desktop_goto_beginning(struct pdraw_desktop *self); + + +void pdraw_desktop_goto_end(struct pdraw_desktop *self); + + +void pdraw_desktop_dump_pipeline(struct pdraw_desktop *self); + + +void pdraw_desktop_change_scheduling_mode(struct pdraw_desktop *self); + + +void pdraw_desktop_change_fill_mode(struct pdraw_desktop *self); + + +int pdraw_desktop_ui_init(struct pdraw_desktop *self); + + +int pdraw_desktop_ui_destroy(struct pdraw_desktop *self); + + +void pdraw_desktop_ui_send_quit_event(void); + + +void pdraw_desktop_ui_send_user_event(struct pdraw_desktop *self, + enum pdraw_desktop_event event, + void *data1, + void *data2); + + +void pdraw_desktop_ui_add_media(struct pdraw_desktop *self, + unsigned int media_id); + + +void pdraw_desktop_ui_resize(struct pdraw_desktop *self); + + +int pdraw_desktop_ui_loop(struct pdraw_desktop *self); + + +void pdraw_desktop_view_create_matrices(struct pdraw_desktop *self, + unsigned int width, + unsigned int height, + float *view_mat, + float *proj_mat, + float near, + float far); + + +int pdraw_desktop_ext_tex_setup(struct pdraw_desktop *self); + + +int pdraw_desktop_ext_tex_cleanup(struct pdraw_desktop *self); + + +int pdraw_desktop_ext_tex_load(struct pdraw_desktop *self, + struct pdraw_backend *pdraw, + struct pdraw_video_renderer *renderer, + const struct pdraw_media_info *media_info, + struct mbuf_raw_video_frame *frame, + const void *frame_userdata, + size_t frame_userdata_len); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* !_PDRAW_DESKTOP_H_ */ diff --git a/apps/pdraw_desktop/pdraw_desktop_ext_tex.c b/apps/pdraw_desktop/pdraw_desktop_ext_tex.c index fc88c04..e5bacef 100644 --- a/apps/pdraw_desktop/pdraw_desktop_ext_tex.c +++ b/apps/pdraw_desktop/pdraw_desktop_ext_tex.c @@ -1,346 +1,346 @@ -/** - * Parrot Drones Awesome Video Viewer - * Desktop application - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "pdraw_desktop.h" - - -static const GLchar *vertex_shader = - "attribute vec4 position;\n" - "attribute vec2 texcoord;\n" - "varying vec2 v_texcoord;\n" - "\n" - "void main()\n" - "{\n" - " gl_Position = position;\n" - " v_texcoord = texcoord;\n" - "}\n"; - -static const GLchar *fragment_shader = - "varying vec2 v_texcoord;\n" - "uniform sampler2D s_texture_0;\n" - "uniform sampler2D s_texture_1;\n" - "uniform sampler2D s_texture_2;\n" - "uniform mat3 yuv2rgb_mat;\n" - "uniform vec3 yuv2rgb_offset;\n" - "\n" - "void main()\n" - "{\n" - " vec3 yuv;\n" - " vec3 rgb;\n" - " yuv.r = texture2D(s_texture_0, v_texcoord).r;\n" - " yuv.g = texture2D(s_texture_1, v_texcoord).r - 0.5;\n" - " yuv.b = texture2D(s_texture_2, v_texcoord).r - 0.5;\n" - " rgb = yuv2rgb_mat * (yuv.rgb + yuv2rgb_offset);\n" - " rgb = vec3(rgb.b, rgb.g, rgb.r);\n" - " gl_FragColor = vec4(rgb, 1.0);\n" - "}\n"; - - -static const GLfloat yuv2rgb_mat[9] = { - 1.164f, - 1.164f, - 1.164f, - 0.f, - -0.392f, - 2.017f, - 1.596f, - -0.813f, - 0.f, -}; - -static const GLfloat yuv2rgb_offset[3] = { - -0.0625f, - 0.f, - 0.f, -}; - - -int pdraw_desktop_ext_tex_setup(struct pdraw_desktop *self) -{ - int ret = 0; - GLint v_shader = 0, f_shader = 0; - GLint success = 0; - unsigned int i; - - if (!self->ext_tex) - return 0; - - v_shader = glCreateShader(GL_VERTEX_SHADER); - if ((v_shader == 0) || (v_shader == GL_INVALID_ENUM)) { - ULOGE("failed to create vertex shader"); - ret = -ENOMEM; - goto err; - } - - glShaderSource(v_shader, 1, &vertex_shader, NULL); - glCompileShader(v_shader); - glGetShaderiv(v_shader, GL_COMPILE_STATUS, &success); - if (!success) { - GLchar info_log[512]; - glGetShaderInfoLog(v_shader, 512, NULL, info_log); - ULOGE("vertex shader compilation failed '%s'", info_log); - ret = -EPROTO; - goto err; - } - - f_shader = glCreateShader(GL_FRAGMENT_SHADER); - if ((f_shader == 0) || (f_shader == GL_INVALID_ENUM)) { - ULOGE("failed to create fragment shader"); - ret = -ENOMEM; - goto err; - } - - glShaderSource(f_shader, 1, &fragment_shader, NULL); - glCompileShader(f_shader); - glGetShaderiv(f_shader, GL_COMPILE_STATUS, &success); - if (!success) { - GLchar info_log[512]; - glGetShaderInfoLog(f_shader, 512, NULL, info_log); - ULOGE("fragment shader compilation failed '%s'", info_log); - ret = -EPROTO; - goto err; - } - - self->ext_tex_program = glCreateProgram(); - glAttachShader(self->ext_tex_program, v_shader); - glAttachShader(self->ext_tex_program, f_shader); - glLinkProgram(self->ext_tex_program); - glGetProgramiv(self->ext_tex_program, GL_LINK_STATUS, &success); - if (!success) { - GLchar info_log[512]; - glGetShaderInfoLog(self->ext_tex_program, 512, NULL, info_log); - ULOGE("program link failed '%s'", info_log); - ret = -EPROTO; - goto err; - } - - glDeleteShader(v_shader); - v_shader = 0; - glDeleteShader(f_shader); - f_shader = 0; - - self->ext_tex_yuv2rgb_matrix = - glGetUniformLocation(self->ext_tex_program, "yuv2rgb_mat"); - self->ext_tex_yuv2rgb_offset = - glGetUniformLocation(self->ext_tex_program, "yuv2rgb_offset"); - self->ext_tex_uniform_samplers[0] = - glGetUniformLocation(self->ext_tex_program, "s_texture_0"); - self->ext_tex_uniform_samplers[1] = - glGetUniformLocation(self->ext_tex_program, "s_texture_1"); - self->ext_tex_uniform_samplers[2] = - glGetUniformLocation(self->ext_tex_program, "s_texture_2"); - self->ext_tex_position_handle = - glGetAttribLocation(self->ext_tex_program, "position"); - self->ext_tex_texcoord_handle = - glGetAttribLocation(self->ext_tex_program, "texcoord"); - - glGenTextures(3, self->ext_tex_textures); - for (i = 0; i < 3; i++) { - glActiveTexture(GL_TEXTURE0 + i); - glBindTexture(GL_TEXTURE_2D, self->ext_tex_textures[i]); - - glTexParameteri( - GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri( - GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - - return 0; - -err: - if (v_shader > 0) - glDeleteShader(v_shader); - if (f_shader > 0) - glDeleteShader(f_shader); - pdraw_desktop_ext_tex_cleanup(self); - return ret; -} - - -int pdraw_desktop_ext_tex_cleanup(struct pdraw_desktop *self) -{ - if (self->ext_tex_textures[0] > 0) { - glDeleteTextures(3, self->ext_tex_textures); - memset(self->ext_tex_textures, - 0, - sizeof(self->ext_tex_textures)); - } - if (self->ext_tex_program > 0) { - glDeleteProgram(self->ext_tex_program); - self->ext_tex_program = 0; - } - - return 0; -} - - -int pdraw_desktop_ext_tex_load(struct pdraw_desktop *self, - struct pdraw_backend *pdraw, - struct pdraw_video_renderer *renderer, - const struct pdraw_media_info *media_info, - struct mbuf_raw_video_frame *frame, - const void *frame_userdata, - size_t frame_userdata_len) -{ - int ret; - unsigned int i, nplanes; - float vertices[8]; - float texcoords[8]; - const void *planes[VDEF_RAW_MAX_PLANE_COUNT] = {0}; - float ar1, ar2, h, v; - - struct vdef_raw_frame frame_info; - - if (!self->ext_tex) - return -ENOSYS; - - if ((pdraw == NULL) || (renderer == NULL) || (media_info == NULL) || - (frame == NULL)) - return -EINVAL; - - ret = mbuf_raw_video_frame_get_frame_info(frame, &frame_info); - if (ret < 0) { - ULOG_ERRNO("mbuf_raw_video_frame_get_frame_info", -ret); - return ret; - } - if (!vdef_raw_format_cmp(&frame_info.format, &vdef_i420)) { - ULOGE("unsupported video format"); - return -ENOSYS; - } - if (vdef_dim_is_null(&frame_info.info.resolution)) { - ULOGE("invalid frame dimensions"); - return -EINVAL; - } - nplanes = vdef_get_raw_frame_plane_count(&frame_info.format); - for (i = 0; i < nplanes; i++) { - size_t dummyLen; - ret = mbuf_raw_video_frame_get_plane( - frame, i, &planes[i], &dummyLen); - if (ret < 0) { - ULOG_ERRNO("mbuf_raw_video_frame_get_plane", -ret); - goto out; - } - } - - glUseProgram(self->ext_tex_program); - glClear(GL_COLOR_BUFFER_BIT); - - for (i = 0; i < 3; i++) { - unsigned int height = - frame_info.info.resolution.height / ((i > 0) ? 2 : 1); - glActiveTexture(GL_TEXTURE0 + i); - glBindTexture(GL_TEXTURE_2D, self->ext_tex_textures[i]); - glTexImage2D(GL_TEXTURE_2D, - 0, - GL_LUMINANCE, - frame_info.plane_stride[i], - height, - 0, - GL_LUMINANCE, - GL_UNSIGNED_BYTE, - planes[i]); - glUniform1i(self->ext_tex_uniform_samplers[i], i); - } - - glUniform3f(self->ext_tex_yuv2rgb_offset, - yuv2rgb_offset[0], - yuv2rgb_offset[1], - yuv2rgb_offset[2]); - glUniformMatrix3fv( - self->ext_tex_yuv2rgb_matrix, 1, GL_FALSE, &yuv2rgb_mat[0]); - - vertices[0] = -1.; - vertices[1] = -1.; - vertices[2] = 1.; - vertices[3] = -1.; - vertices[4] = -1.; - vertices[5] = 1.; - vertices[6] = 1.; - vertices[7] = 1.; - - glVertexAttribPointer(self->ext_tex_position_handle, - 2, - GL_FLOAT, - GL_FALSE, - 0, - vertices); - glEnableVertexAttribArray(self->ext_tex_position_handle); - - /* Test: take a 2:3 crop at the center */ - ar1 = (float)frame_info.info.resolution.width / - (float)frame_info.info.resolution.height; - ar2 = 2.f / 3.f; - if (ar1 < ar2) { - h = 1.f; - v = ar2 / ar1; - } else { - h = ar2 / ar1; - v = 1.f; - } - h = (1.f - h) / 2.f; - v = (1.f - v) / 2.f; - texcoords[0] = h * (float)frame_info.info.resolution.width / - (float)frame_info.plane_stride[0]; - texcoords[1] = 1.f - v; - texcoords[2] = (1.f - h) * (float)frame_info.info.resolution.width / - (float)frame_info.plane_stride[0]; - texcoords[3] = 1.f - v; - texcoords[4] = h * (float)frame_info.info.resolution.width / - (float)frame_info.plane_stride[0]; - texcoords[5] = v; - texcoords[6] = (1.f - h) * (float)frame_info.info.resolution.width / - (float)frame_info.plane_stride[0]; - texcoords[7] = v; - - glVertexAttribPointer(self->ext_tex_texcoord_handle, - 2, - GL_FLOAT, - GL_FALSE, - 0, - texcoords); - glEnableVertexAttribArray(self->ext_tex_texcoord_handle); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(self->ext_tex_position_handle); - glDisableVertexAttribArray(self->ext_tex_texcoord_handle); - -out: - for (i = 0; i < nplanes; i++) { - if (planes[i] == NULL) - continue; - mbuf_raw_video_frame_release_plane(frame, i, planes[i]); - } - return 0; -} +/** + * Parrot Drones Awesome Video Viewer + * Desktop application + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 "pdraw_desktop.h" + + +static const GLchar *vertex_shader = + "attribute vec4 position;\n" + "attribute vec2 texcoord;\n" + "varying vec2 v_texcoord;\n" + "\n" + "void main()\n" + "{\n" + " gl_Position = position;\n" + " v_texcoord = texcoord;\n" + "}\n"; + +static const GLchar *fragment_shader = + "varying vec2 v_texcoord;\n" + "uniform sampler2D s_texture_0;\n" + "uniform sampler2D s_texture_1;\n" + "uniform sampler2D s_texture_2;\n" + "uniform mat3 yuv2rgb_mat;\n" + "uniform vec3 yuv2rgb_offset;\n" + "\n" + "void main()\n" + "{\n" + " vec3 yuv;\n" + " vec3 rgb;\n" + " yuv.r = texture2D(s_texture_0, v_texcoord).r;\n" + " yuv.g = texture2D(s_texture_1, v_texcoord).r - 0.5;\n" + " yuv.b = texture2D(s_texture_2, v_texcoord).r - 0.5;\n" + " rgb = yuv2rgb_mat * (yuv.rgb + yuv2rgb_offset);\n" + " rgb = vec3(rgb.b, rgb.g, rgb.r);\n" + " gl_FragColor = vec4(rgb, 1.0);\n" + "}\n"; + + +static const GLfloat yuv2rgb_mat[9] = { + 1.164f, + 1.164f, + 1.164f, + 0.f, + -0.392f, + 2.017f, + 1.596f, + -0.813f, + 0.f, +}; + +static const GLfloat yuv2rgb_offset[3] = { + -0.0625f, + 0.f, + 0.f, +}; + + +int pdraw_desktop_ext_tex_setup(struct pdraw_desktop *self) +{ + int ret = 0; + GLint v_shader = 0, f_shader = 0; + GLint success = 0; + unsigned int i; + + if (!self->ext_tex) + return 0; + + v_shader = glCreateShader(GL_VERTEX_SHADER); + if ((v_shader == 0) || (v_shader == GL_INVALID_ENUM)) { + ULOGE("failed to create vertex shader"); + ret = -ENOMEM; + goto err; + } + + glShaderSource(v_shader, 1, &vertex_shader, NULL); + glCompileShader(v_shader); + glGetShaderiv(v_shader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar info_log[512]; + glGetShaderInfoLog(v_shader, 512, NULL, info_log); + ULOGE("vertex shader compilation failed '%s'", info_log); + ret = -EPROTO; + goto err; + } + + f_shader = glCreateShader(GL_FRAGMENT_SHADER); + if ((f_shader == 0) || (f_shader == GL_INVALID_ENUM)) { + ULOGE("failed to create fragment shader"); + ret = -ENOMEM; + goto err; + } + + glShaderSource(f_shader, 1, &fragment_shader, NULL); + glCompileShader(f_shader); + glGetShaderiv(f_shader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar info_log[512]; + glGetShaderInfoLog(f_shader, 512, NULL, info_log); + ULOGE("fragment shader compilation failed '%s'", info_log); + ret = -EPROTO; + goto err; + } + + self->ext_tex_program = glCreateProgram(); + glAttachShader(self->ext_tex_program, v_shader); + glAttachShader(self->ext_tex_program, f_shader); + glLinkProgram(self->ext_tex_program); + glGetProgramiv(self->ext_tex_program, GL_LINK_STATUS, &success); + if (!success) { + GLchar info_log[512]; + glGetShaderInfoLog(self->ext_tex_program, 512, NULL, info_log); + ULOGE("program link failed '%s'", info_log); + ret = -EPROTO; + goto err; + } + + glDeleteShader(v_shader); + v_shader = 0; + glDeleteShader(f_shader); + f_shader = 0; + + self->ext_tex_yuv2rgb_matrix = + glGetUniformLocation(self->ext_tex_program, "yuv2rgb_mat"); + self->ext_tex_yuv2rgb_offset = + glGetUniformLocation(self->ext_tex_program, "yuv2rgb_offset"); + self->ext_tex_uniform_samplers[0] = + glGetUniformLocation(self->ext_tex_program, "s_texture_0"); + self->ext_tex_uniform_samplers[1] = + glGetUniformLocation(self->ext_tex_program, "s_texture_1"); + self->ext_tex_uniform_samplers[2] = + glGetUniformLocation(self->ext_tex_program, "s_texture_2"); + self->ext_tex_position_handle = + glGetAttribLocation(self->ext_tex_program, "position"); + self->ext_tex_texcoord_handle = + glGetAttribLocation(self->ext_tex_program, "texcoord"); + + glGenTextures(3, self->ext_tex_textures); + for (i = 0; i < 3; i++) { + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(GL_TEXTURE_2D, self->ext_tex_textures[i]); + + glTexParameteri( + GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri( + GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + + return 0; + +err: + if (v_shader > 0) + glDeleteShader(v_shader); + if (f_shader > 0) + glDeleteShader(f_shader); + pdraw_desktop_ext_tex_cleanup(self); + return ret; +} + + +int pdraw_desktop_ext_tex_cleanup(struct pdraw_desktop *self) +{ + if (self->ext_tex_textures[0] > 0) { + glDeleteTextures(3, self->ext_tex_textures); + memset(self->ext_tex_textures, + 0, + sizeof(self->ext_tex_textures)); + } + if (self->ext_tex_program > 0) { + glDeleteProgram(self->ext_tex_program); + self->ext_tex_program = 0; + } + + return 0; +} + + +int pdraw_desktop_ext_tex_load(struct pdraw_desktop *self, + struct pdraw_backend *pdraw, + struct pdraw_video_renderer *renderer, + const struct pdraw_media_info *media_info, + struct mbuf_raw_video_frame *frame, + const void *frame_userdata, + size_t frame_userdata_len) +{ + int ret; + unsigned int i, nplanes; + float vertices[8]; + float texcoords[8]; + const void *planes[VDEF_RAW_MAX_PLANE_COUNT] = {0}; + float ar1, ar2, h, v; + + struct vdef_raw_frame frame_info; + + if (!self->ext_tex) + return -ENOSYS; + + if ((pdraw == NULL) || (renderer == NULL) || (media_info == NULL) || + (frame == NULL)) + return -EINVAL; + + ret = mbuf_raw_video_frame_get_frame_info(frame, &frame_info); + if (ret < 0) { + ULOG_ERRNO("mbuf_raw_video_frame_get_frame_info", -ret); + return ret; + } + if (!vdef_raw_format_cmp(&frame_info.format, &vdef_i420)) { + ULOGE("unsupported video format"); + return -ENOSYS; + } + if (vdef_dim_is_null(&frame_info.info.resolution)) { + ULOGE("invalid frame dimensions"); + return -EINVAL; + } + nplanes = vdef_get_raw_frame_plane_count(&frame_info.format); + for (i = 0; i < nplanes; i++) { + size_t dummyLen; + ret = mbuf_raw_video_frame_get_plane( + frame, i, &planes[i], &dummyLen); + if (ret < 0) { + ULOG_ERRNO("mbuf_raw_video_frame_get_plane", -ret); + goto out; + } + } + + glUseProgram(self->ext_tex_program); + glClear(GL_COLOR_BUFFER_BIT); + + for (i = 0; i < 3; i++) { + unsigned int height = + frame_info.info.resolution.height / ((i > 0) ? 2 : 1); + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(GL_TEXTURE_2D, self->ext_tex_textures[i]); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_LUMINANCE, + frame_info.plane_stride[i], + height, + 0, + GL_LUMINANCE, + GL_UNSIGNED_BYTE, + planes[i]); + glUniform1i(self->ext_tex_uniform_samplers[i], i); + } + + glUniform3f(self->ext_tex_yuv2rgb_offset, + yuv2rgb_offset[0], + yuv2rgb_offset[1], + yuv2rgb_offset[2]); + glUniformMatrix3fv( + self->ext_tex_yuv2rgb_matrix, 1, GL_FALSE, &yuv2rgb_mat[0]); + + vertices[0] = -1.; + vertices[1] = -1.; + vertices[2] = 1.; + vertices[3] = -1.; + vertices[4] = -1.; + vertices[5] = 1.; + vertices[6] = 1.; + vertices[7] = 1.; + + glVertexAttribPointer(self->ext_tex_position_handle, + 2, + GL_FLOAT, + GL_FALSE, + 0, + vertices); + glEnableVertexAttribArray(self->ext_tex_position_handle); + + /* Test: take a 2:3 crop at the center */ + ar1 = (float)frame_info.info.resolution.width / + (float)frame_info.info.resolution.height; + ar2 = 2.f / 3.f; + if (ar1 < ar2) { + h = 1.f; + v = ar2 / ar1; + } else { + h = ar2 / ar1; + v = 1.f; + } + h = (1.f - h) / 2.f; + v = (1.f - v) / 2.f; + texcoords[0] = h * (float)frame_info.info.resolution.width / + (float)frame_info.plane_stride[0]; + texcoords[1] = 1.f - v; + texcoords[2] = (1.f - h) * (float)frame_info.info.resolution.width / + (float)frame_info.plane_stride[0]; + texcoords[3] = 1.f - v; + texcoords[4] = h * (float)frame_info.info.resolution.width / + (float)frame_info.plane_stride[0]; + texcoords[5] = v; + texcoords[6] = (1.f - h) * (float)frame_info.info.resolution.width / + (float)frame_info.plane_stride[0]; + texcoords[7] = v; + + glVertexAttribPointer(self->ext_tex_texcoord_handle, + 2, + GL_FLOAT, + GL_FALSE, + 0, + texcoords); + glEnableVertexAttribArray(self->ext_tex_texcoord_handle); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableVertexAttribArray(self->ext_tex_position_handle); + glDisableVertexAttribArray(self->ext_tex_texcoord_handle); + +out: + for (i = 0; i < nplanes; i++) { + if (planes[i] == NULL) + continue; + mbuf_raw_video_frame_release_plane(frame, i, planes[i]); + } + return 0; +} diff --git a/apps/pdraw_desktop/pdraw_desktop_ui.c b/apps/pdraw_desktop/pdraw_desktop_ui.c index 47dd2f5..ce0a0c8 100644 --- a/apps/pdraw_desktop/pdraw_desktop_ui.c +++ b/apps/pdraw_desktop/pdraw_desktop_ui.c @@ -1,463 +1,463 @@ -/** - * Parrot Drones Awesome Video Viewer - * Desktop application - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "pdraw_desktop.h" - - -static const struct { - unsigned int h; - unsigned int v; -} layout[MAX_RENDERERS] = { - {1, 1}, - {2, 1}, - {2, 2}, - {2, 2}, - {3, 2}, - {3, 2}, - {3, 3}, - {3, 3}, - {3, 3}, -}; - - -static void user_event(struct pdraw_desktop *self, SDL_UserEvent *event) -{ - int status; - unsigned int media_id; - switch (event->code) { - case PDRAW_DESKTOP_EVENT_OPEN: - pdraw_desktop_open(self); - break; - case PDRAW_DESKTOP_EVENT_OPEN_RESP: - status = (int)(intptr_t)event->data1; - if (status < 0) - pdraw_desktop_close(self); - break; - case PDRAW_DESKTOP_EVENT_UNRECOVERABLE_ERROR: - pdraw_desktop_close(self); - break; - case PDRAW_DESKTOP_EVENT_READY_TO_PLAY: - if (event->data1) - pdraw_desktop_toggle_play_pause(self); - break; - case PDRAW_DESKTOP_EVENT_PLAY_RESP: - break; - case PDRAW_DESKTOP_EVENT_PAUSE_RESP: - break; - case PDRAW_DESKTOP_EVENT_SEEK_RESP: - break; - case PDRAW_DESKTOP_EVENT_STOP_RESP: - self->stopped = 1; - break; - case PDRAW_DESKTOP_EVENT_ADD_RENDERER: - media_id = (unsigned int)(intptr_t)event->data1; - pdraw_desktop_ui_add_media(self, media_id); - break; - default: - break; - } -} - - -static void sdl_event(struct pdraw_desktop *self, SDL_Event *event) -{ - int res; - - switch (event->type) { - case SDL_QUIT: - pdraw_desktop_close(self); - break; - case SDL_WINDOWEVENT: - if (event->window.event == SDL_WINDOWEVENT_RESIZED) { - self->window_width = event->window.data1; - self->window_height = event->window.data2; - pdraw_desktop_ui_resize(self); - } - break; - case SDL_KEYDOWN: - switch (event->key.keysym.sym) { - case SDLK_ESCAPE: - pdraw_desktop_close(self); - break; - case SDLK_SPACE: - pdraw_desktop_toggle_play_pause(self); - break; - case SDLK_PAGEUP: - pdraw_desktop_seek_back_10s(self); - break; - case SDLK_PAGEDOWN: - pdraw_desktop_seek_forward_10s(self); - break; - case SDLK_LEFT: - if (pdraw_be_demuxer_is_paused(self->pdraw, - self->demuxer) == 0) - pdraw_desktop_seek_back_10s(self); - else - pdraw_desktop_previous_frame(self); - break; - case SDLK_RIGHT: - if (pdraw_be_demuxer_is_paused(self->pdraw, - self->demuxer) == 0) - pdraw_desktop_seek_forward_10s(self); - else - pdraw_desktop_next_frame(self); - break; - case SDLK_HOME: - pdraw_desktop_goto_beginning(self); - break; - case SDLK_END: - pdraw_desktop_goto_end(self); - break; - case SDLK_BACKSPACE: - case SDLK_KP_ENTER: - pdraw_desktop_toggle_speed_sign(self); - break; - case SDLK_MINUS: - case SDLK_KP_MINUS: - pdraw_desktop_speed_down(self); - break; - case SDLK_EQUALS: - case SDLK_PLUS: - case SDLK_KP_PLUS: - pdraw_desktop_speed_up(self); - break; - case SDLK_RETURN: - self->fullscreen ^= 1; - res = SDL_SetWindowFullscreen( - self->window, - (self->fullscreen) - ? SDL_WINDOW_FULLSCREEN_DESKTOP - : 0); - if (res < 0) { - ULOGW("SDL_SetWindowFullscreen() failed: %d", - res); - } - break; - case SDLK_d: - pdraw_desktop_dump_pipeline(self); - break; - case SDLK_f: - pdraw_desktop_change_fill_mode(self); - break; - case SDLK_s: - pdraw_desktop_change_scheduling_mode(self); - break; - } - break; - default: - break; - } -} - - -int pdraw_desktop_ui_init(struct pdraw_desktop *self) -{ - int res; - - res = SDL_Init(SDL_INIT_VIDEO); - if (res < 0) { - ULOGE("SDL_Init() failed: %d(%s)", res, SDL_GetError()); - res = -EPROTO; - return res; - } - - SDL_DisplayMode dm; - res = SDL_GetDesktopDisplayMode(0, &dm); - if (res < 0) { - ULOGE("SDL_GetDesktopDisplayMode() failed: %d(%s)", - res, - SDL_GetError()); - res = -EPROTO; - return res; - } - ULOGI("display mode: %dx%d %dHz", dm.w, dm.h, dm.refresh_rate); - self->window_width = dm.w / 2; - self->window_height = dm.h / 2; - - self->window = SDL_CreateWindow( - APP_NAME, - SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, - self->window_width, - self->window_height, - SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | - ((self->fullscreen) ? SDL_WINDOW_FULLSCREEN_DESKTOP - : 0) | - ((self->always_on_top) ? SDL_WINDOW_ALWAYS_ON_TOP : 0)); - if (self->window == NULL) { - ULOGE("SDL_CreateWindow() failed: %s", SDL_GetError()); - res = -EPROTO; - return res; - } - - self->gl_context = SDL_GL_CreateContext(self->window); - if (self->gl_context == NULL) { - ULOGE("SDL_GL_CreateContext() failed: %s", SDL_GetError()); - res = -EPROTO; - return res; - } - -#ifndef _WIN32 - res = SDL_GL_SetSwapInterval(1); - if (res < 0) { - ULOGE("SDL_GL_SetSwapInterval() failed: %d(%s)", - res, - SDL_GetError()); - res = -EPROTO; - return res; - } -#endif /* !_WIN32 */ - - self->user_event = SDL_RegisterEvents(1); - if (self->user_event == (uint32_t)-1) { - ULOGE("SDL_RegisterEvents() failed"); - res = -EPROTO; - return res; - } - - return 0; -} - - -int pdraw_desktop_ui_destroy(struct pdraw_desktop *self) -{ - int res; - - for (unsigned int i = 0; i < self->renderer_count; i++) { - if (self->gles2hud[i] != NULL) { - res = pdraw_gles2hud_destroy(self->gles2hud[i]); - if (res < 0) - ULOG_ERRNO("pdraw_gles2hud_destroy", -res); - self->gles2hud[i] = NULL; - } - if (self->renderer[i] != NULL) { - res = pdraw_be_video_renderer_destroy( - self->pdraw, self->renderer[i]); - if (res < 0) - ULOG_ERRNO("pdraw_be_video_renderer_destroy", - -res); - self->renderer[i] = NULL; - } - } - - if (self->gl_context) - SDL_GL_DeleteContext(self->gl_context); - SDL_Quit(); - - return 0; -} - - -void pdraw_desktop_ui_send_quit_event(void) -{ - SDL_Event event; - memset(&event, 0, sizeof(event)); - event.type = SDL_QUIT; - SDL_PushEvent(&event); -} - - -void pdraw_desktop_ui_send_user_event(struct pdraw_desktop *self, - enum pdraw_desktop_event evt, - void *data1, - void *data2) -{ - SDL_Event event; - memset(&event, 0, sizeof(event)); - event.type = self->user_event; - event.user.code = evt; - event.user.data1 = data1; - event.user.data2 = data2; - SDL_PushEvent(&event); -} - - -static void -get_rect(struct pdraw_desktop *self, struct pdraw_rect *rect, unsigned int idx) -{ - unsigned int layout_idx = self->demuxer_media_count - 1; - unsigned int h = layout[layout_idx].h; - unsigned int v = layout[layout_idx].v; - unsigned int x = idx % h; - unsigned int y = v - 1 - idx / h; - if (idx / h == v - 1) { - /* Last line */ - idx -= (self->demuxer_media_count / h) * h; - h = self->demuxer_media_count - - (self->demuxer_media_count / h) * h; - if (h == 0) - h = layout[layout_idx].h; - x = idx % h; - } - rect->x = self->window_width * x / h; - rect->y = self->window_height * y / v; - rect->width = self->window_width / h; - rect->height = self->window_height / v; -} - - -void pdraw_desktop_ui_add_media(struct pdraw_desktop *self, - unsigned int media_id) -{ - int res, inc; - unsigned int i; - - /* Try to reuse an existing renderer without media */ - for (i = 0; i < self->renderer_count; i++) { - if ((self->renderer[i] == NULL) || - (self->renderer_media_id[i] != 0)) - continue; - res = pdraw_be_video_renderer_set_media_id( - self->pdraw, self->renderer[i], media_id); - if (res < 0) { - ULOG_ERRNO("pdraw_be_video_renderer_set_media_id", - -res); - return; - } - self->renderer_media_id[i] = media_id; - return; - } - - /* Find a free renderer slot to use */ - for (i = 0; i < self->renderer_count; i++) { - if (self->renderer[i] == NULL) - break; - } - inc = i == self->renderer_count; - if (self->renderer_count >= MAX_RENDERERS) - return; - if (self->renderer_count >= self->demuxer_media_count) - return; - - /* Create the renderer */ - struct pdraw_video_renderer_params params = {0}; - params.scheduling_mode = self->default_scheduling_mode; - params.fill_mode = self->default_fill_mode; - params.enable_transition_flags = - PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_ALL; - params.enable_hmd_distortion_correction = self->enable_hmd; - params.enable_overexposure_zebras = self->enable_zebras; - params.overexposure_zebras_threshold = self->zebras_threshold; - params.enable_histograms = - ((self->enable_hud) && - (self->hud_type == PDRAW_GLES2HUD_TYPE_IMAGING)) - ? 1 - : 0; - - /* Test: take a 2:3 crop at the center and force a 2:3 DAR and a 1600 - * pixels texture width (only enabled with the '--ext-tex' option) */ - params.video_texture_width = 1600; - params.video_texture_dar_width = 2; - params.video_texture_dar_height = 3; - - get_rect(self, &self->render_pos[i], i); - res = pdraw_be_video_renderer_new(self->pdraw, - media_id, - &self->render_pos[i], - ¶ms, - &render_cbs, - self, - &self->renderer[i]); - if (res < 0) { - ULOG_ERRNO("pdraw_be_video_renderer_new", -res); - return; - } - if (self->enable_hud) { - struct pdraw_gles2hud_config hud_config; - memset(&hud_config, 0, sizeof(hud_config)); - res = pdraw_gles2hud_new(&hud_config, &self->gles2hud[i]); - if (res < 0) - ULOG_ERRNO("pdraw_gles2hud_new", -res); - } - res = pdraw_desktop_ext_tex_setup(self); - if (res < 0) - ULOG_ERRNO("pdraw_desktop_ext_tex_setup", -res); - if (inc) - self->renderer_count++; - self->renderer_media_id[i] = media_id; -} - - -void pdraw_desktop_ui_resize(struct pdraw_desktop *self) -{ - for (unsigned int i = 0; i < self->renderer_count; i++) { - int res; - get_rect(self, &self->render_pos[i], i); - res = pdraw_be_video_renderer_resize( - self->pdraw, self->renderer[i], &self->render_pos[i]); - if (res < 0) - ULOG_ERRNO("pdraw_be_video_renderer_resize", -res); - } -} - - -int pdraw_desktop_ui_loop(struct pdraw_desktop *self) -{ - int res; - SDL_Event event; - float view_mat[16]; - float proj_mat[16]; - - while (!self->stopped) { - while ((!self->stopped) && (SDL_PollEvent(&event))) { - if (event.type == self->user_event) - user_event(self, &event.user); - else - sdl_event(self, &event); - } - glViewport(0, 0, self->window_width, self->window_height); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - for (unsigned int i = 0; i < self->renderer_count; i++) { - if (self->renderer[i] == NULL) - continue; - pdraw_desktop_view_create_matrices( - self, - self->render_pos[i].width, - self->render_pos[i].height, - view_mat, - proj_mat, - 0.1f, - 100.f); - res = pdraw_be_video_renderer_render_mat( - self->pdraw, - self->renderer[i], - NULL, - view_mat, - proj_mat); - if (res < 0) - ULOG_ERRNO("pdraw_be_video_renderer_render_mat", - -res); - } - SDL_GL_SwapWindow(self->window); - } - - return 0; -} +/** + * Parrot Drones Awesome Video Viewer + * Desktop application + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 "pdraw_desktop.h" + + +static const struct { + unsigned int h; + unsigned int v; +} layout[MAX_RENDERERS] = { + {1, 1}, + {2, 1}, + {2, 2}, + {2, 2}, + {3, 2}, + {3, 2}, + {3, 3}, + {3, 3}, + {3, 3}, +}; + + +static void user_event(struct pdraw_desktop *self, SDL_UserEvent *event) +{ + int status; + unsigned int media_id; + switch (event->code) { + case PDRAW_DESKTOP_EVENT_OPEN: + pdraw_desktop_open(self); + break; + case PDRAW_DESKTOP_EVENT_OPEN_RESP: + status = (int)(intptr_t)event->data1; + if (status < 0) + pdraw_desktop_close(self); + break; + case PDRAW_DESKTOP_EVENT_UNRECOVERABLE_ERROR: + pdraw_desktop_close(self); + break; + case PDRAW_DESKTOP_EVENT_READY_TO_PLAY: + if (event->data1) + pdraw_desktop_toggle_play_pause(self); + break; + case PDRAW_DESKTOP_EVENT_PLAY_RESP: + break; + case PDRAW_DESKTOP_EVENT_PAUSE_RESP: + break; + case PDRAW_DESKTOP_EVENT_SEEK_RESP: + break; + case PDRAW_DESKTOP_EVENT_STOP_RESP: + self->stopped = 1; + break; + case PDRAW_DESKTOP_EVENT_ADD_RENDERER: + media_id = (unsigned int)(intptr_t)event->data1; + pdraw_desktop_ui_add_media(self, media_id); + break; + default: + break; + } +} + + +static void sdl_event(struct pdraw_desktop *self, SDL_Event *event) +{ + int res; + + switch (event->type) { + case SDL_QUIT: + pdraw_desktop_close(self); + break; + case SDL_WINDOWEVENT: + if (event->window.event == SDL_WINDOWEVENT_RESIZED) { + self->window_width = event->window.data1; + self->window_height = event->window.data2; + pdraw_desktop_ui_resize(self); + } + break; + case SDL_KEYDOWN: + switch (event->key.keysym.sym) { + case SDLK_ESCAPE: + pdraw_desktop_close(self); + break; + case SDLK_SPACE: + pdraw_desktop_toggle_play_pause(self); + break; + case SDLK_PAGEUP: + pdraw_desktop_seek_back_10s(self); + break; + case SDLK_PAGEDOWN: + pdraw_desktop_seek_forward_10s(self); + break; + case SDLK_LEFT: + if (pdraw_be_demuxer_is_paused(self->pdraw, + self->demuxer) == 0) + pdraw_desktop_seek_back_10s(self); + else + pdraw_desktop_previous_frame(self); + break; + case SDLK_RIGHT: + if (pdraw_be_demuxer_is_paused(self->pdraw, + self->demuxer) == 0) + pdraw_desktop_seek_forward_10s(self); + else + pdraw_desktop_next_frame(self); + break; + case SDLK_HOME: + pdraw_desktop_goto_beginning(self); + break; + case SDLK_END: + pdraw_desktop_goto_end(self); + break; + case SDLK_BACKSPACE: + case SDLK_KP_ENTER: + pdraw_desktop_toggle_speed_sign(self); + break; + case SDLK_MINUS: + case SDLK_KP_MINUS: + pdraw_desktop_speed_down(self); + break; + case SDLK_EQUALS: + case SDLK_PLUS: + case SDLK_KP_PLUS: + pdraw_desktop_speed_up(self); + break; + case SDLK_RETURN: + self->fullscreen ^= 1; + res = SDL_SetWindowFullscreen( + self->window, + (self->fullscreen) + ? SDL_WINDOW_FULLSCREEN_DESKTOP + : 0); + if (res < 0) { + ULOGW("SDL_SetWindowFullscreen() failed: %d", + res); + } + break; + case SDLK_d: + pdraw_desktop_dump_pipeline(self); + break; + case SDLK_f: + pdraw_desktop_change_fill_mode(self); + break; + case SDLK_s: + pdraw_desktop_change_scheduling_mode(self); + break; + } + break; + default: + break; + } +} + + +int pdraw_desktop_ui_init(struct pdraw_desktop *self) +{ + int res; + + res = SDL_Init(SDL_INIT_VIDEO); + if (res < 0) { + ULOGE("SDL_Init() failed: %d(%s)", res, SDL_GetError()); + res = -EPROTO; + return res; + } + + SDL_DisplayMode dm; + res = SDL_GetDesktopDisplayMode(0, &dm); + if (res < 0) { + ULOGE("SDL_GetDesktopDisplayMode() failed: %d(%s)", + res, + SDL_GetError()); + res = -EPROTO; + return res; + } + ULOGI("display mode: %dx%d %dHz", dm.w, dm.h, dm.refresh_rate); + self->window_width = dm.w / 2; + self->window_height = dm.h / 2; + + self->window = SDL_CreateWindow( + APP_NAME, + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + self->window_width, + self->window_height, + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | + ((self->fullscreen) ? SDL_WINDOW_FULLSCREEN_DESKTOP + : 0) | + ((self->always_on_top) ? SDL_WINDOW_ALWAYS_ON_TOP : 0)); + if (self->window == NULL) { + ULOGE("SDL_CreateWindow() failed: %s", SDL_GetError()); + res = -EPROTO; + return res; + } + + self->gl_context = SDL_GL_CreateContext(self->window); + if (self->gl_context == NULL) { + ULOGE("SDL_GL_CreateContext() failed: %s", SDL_GetError()); + res = -EPROTO; + return res; + } + +#ifndef _WIN32 + res = SDL_GL_SetSwapInterval(1); + if (res < 0) { + ULOGE("SDL_GL_SetSwapInterval() failed: %d(%s)", + res, + SDL_GetError()); + res = -EPROTO; + return res; + } +#endif /* !_WIN32 */ + + self->user_event = SDL_RegisterEvents(1); + if (self->user_event == (uint32_t)-1) { + ULOGE("SDL_RegisterEvents() failed"); + res = -EPROTO; + return res; + } + + return 0; +} + + +int pdraw_desktop_ui_destroy(struct pdraw_desktop *self) +{ + int res; + + for (unsigned int i = 0; i < self->renderer_count; i++) { + if (self->gles2hud[i] != NULL) { + res = pdraw_gles2hud_destroy(self->gles2hud[i]); + if (res < 0) + ULOG_ERRNO("pdraw_gles2hud_destroy", -res); + self->gles2hud[i] = NULL; + } + if (self->renderer[i] != NULL) { + res = pdraw_be_video_renderer_destroy( + self->pdraw, self->renderer[i]); + if (res < 0) + ULOG_ERRNO("pdraw_be_video_renderer_destroy", + -res); + self->renderer[i] = NULL; + } + } + + if (self->gl_context) + SDL_GL_DeleteContext(self->gl_context); + SDL_Quit(); + + return 0; +} + + +void pdraw_desktop_ui_send_quit_event(void) +{ + SDL_Event event; + memset(&event, 0, sizeof(event)); + event.type = SDL_QUIT; + SDL_PushEvent(&event); +} + + +void pdraw_desktop_ui_send_user_event(struct pdraw_desktop *self, + enum pdraw_desktop_event evt, + void *data1, + void *data2) +{ + SDL_Event event; + memset(&event, 0, sizeof(event)); + event.type = self->user_event; + event.user.code = evt; + event.user.data1 = data1; + event.user.data2 = data2; + SDL_PushEvent(&event); +} + + +static void +get_rect(struct pdraw_desktop *self, struct pdraw_rect *rect, unsigned int idx) +{ + unsigned int layout_idx = self->demuxer_media_count - 1; + unsigned int h = layout[layout_idx].h; + unsigned int v = layout[layout_idx].v; + unsigned int x = idx % h; + unsigned int y = v - 1 - idx / h; + if (idx / h == v - 1) { + /* Last line */ + idx -= (self->demuxer_media_count / h) * h; + h = self->demuxer_media_count - + (self->demuxer_media_count / h) * h; + if (h == 0) + h = layout[layout_idx].h; + x = idx % h; + } + rect->x = self->window_width * x / h; + rect->y = self->window_height * y / v; + rect->width = self->window_width / h; + rect->height = self->window_height / v; +} + + +void pdraw_desktop_ui_add_media(struct pdraw_desktop *self, + unsigned int media_id) +{ + int res, inc; + unsigned int i; + + /* Try to reuse an existing renderer without media */ + for (i = 0; i < self->renderer_count; i++) { + if ((self->renderer[i] == NULL) || + (self->renderer_media_id[i] != 0)) + continue; + res = pdraw_be_video_renderer_set_media_id( + self->pdraw, self->renderer[i], media_id); + if (res < 0) { + ULOG_ERRNO("pdraw_be_video_renderer_set_media_id", + -res); + return; + } + self->renderer_media_id[i] = media_id; + return; + } + + /* Find a free renderer slot to use */ + for (i = 0; i < self->renderer_count; i++) { + if (self->renderer[i] == NULL) + break; + } + inc = i == self->renderer_count; + if (self->renderer_count >= MAX_RENDERERS) + return; + if (self->renderer_count >= self->demuxer_media_count) + return; + + /* Create the renderer */ + struct pdraw_video_renderer_params params = {0}; + params.scheduling_mode = self->default_scheduling_mode; + params.fill_mode = self->default_fill_mode; + params.enable_transition_flags = + PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_ALL; + params.enable_hmd_distortion_correction = self->enable_hmd; + params.enable_overexposure_zebras = self->enable_zebras; + params.overexposure_zebras_threshold = self->zebras_threshold; + params.enable_histograms = + ((self->enable_hud) && + (self->hud_type == PDRAW_GLES2HUD_TYPE_IMAGING)) + ? 1 + : 0; + + /* Test: take a 2:3 crop at the center and force a 2:3 DAR and a 1600 + * pixels texture width (only enabled with the '--ext-tex' option) */ + params.video_texture_width = 1600; + params.video_texture_dar_width = 2; + params.video_texture_dar_height = 3; + + get_rect(self, &self->render_pos[i], i); + res = pdraw_be_video_renderer_new(self->pdraw, + media_id, + &self->render_pos[i], + ¶ms, + &render_cbs, + self, + &self->renderer[i]); + if (res < 0) { + ULOG_ERRNO("pdraw_be_video_renderer_new", -res); + return; + } + if (self->enable_hud) { + struct pdraw_gles2hud_config hud_config; + memset(&hud_config, 0, sizeof(hud_config)); + res = pdraw_gles2hud_new(&hud_config, &self->gles2hud[i]); + if (res < 0) + ULOG_ERRNO("pdraw_gles2hud_new", -res); + } + res = pdraw_desktop_ext_tex_setup(self); + if (res < 0) + ULOG_ERRNO("pdraw_desktop_ext_tex_setup", -res); + if (inc) + self->renderer_count++; + self->renderer_media_id[i] = media_id; +} + + +void pdraw_desktop_ui_resize(struct pdraw_desktop *self) +{ + for (unsigned int i = 0; i < self->renderer_count; i++) { + int res; + get_rect(self, &self->render_pos[i], i); + res = pdraw_be_video_renderer_resize( + self->pdraw, self->renderer[i], &self->render_pos[i]); + if (res < 0) + ULOG_ERRNO("pdraw_be_video_renderer_resize", -res); + } +} + + +int pdraw_desktop_ui_loop(struct pdraw_desktop *self) +{ + int res; + SDL_Event event; + float view_mat[16]; + float proj_mat[16]; + + while (!self->stopped) { + while ((!self->stopped) && (SDL_PollEvent(&event))) { + if (event.type == self->user_event) + user_event(self, &event.user); + else + sdl_event(self, &event); + } + glViewport(0, 0, self->window_width, self->window_height); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + for (unsigned int i = 0; i < self->renderer_count; i++) { + if (self->renderer[i] == NULL) + continue; + pdraw_desktop_view_create_matrices( + self, + self->render_pos[i].width, + self->render_pos[i].height, + view_mat, + proj_mat, + 0.1f, + 100.f); + res = pdraw_be_video_renderer_render_mat( + self->pdraw, + self->renderer[i], + NULL, + view_mat, + proj_mat); + if (res < 0) + ULOG_ERRNO("pdraw_be_video_renderer_render_mat", + -res); + } + SDL_GL_SwapWindow(self->window); + } + + return 0; +} diff --git a/apps/pdraw_desktop/pdraw_desktop_view.cpp b/apps/pdraw_desktop/pdraw_desktop_view.cpp index 3f01b08..0116788 100644 --- a/apps/pdraw_desktop/pdraw_desktop_view.cpp +++ b/apps/pdraw_desktop/pdraw_desktop_view.cpp @@ -1,63 +1,63 @@ -/** - * Parrot Drones Awesome Video Viewer - * Desktop application - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include - -#include "pdraw_desktop.h" - - -void pdraw_desktop_view_create_matrices(struct pdraw_desktop *self, - unsigned int width, - unsigned int height, - float *view_mat, - float *proj_mat, - float near, - float far) -{ - float w = 1.f; - float h = (float)width / (float)height; - float a = (far + near) / (far - near); - float b = -((2 * far * near) / (far - near)); - - Eigen::Matrix4f p_mat; - p_mat << w, 0, 0, 0, 0, h, 0, 0, 0, 0, a, b, 0, 0, 1, 0; - - Eigen::Matrix4f v_mat = Eigen::Matrix4f::Identity(); - - unsigned int i, j; - for (i = 0; i < 4; i++) { - for (j = 0; j < 4; j++) - view_mat[j * 4 + i] = v_mat(i, j); - } - for (i = 0; i < 4; i++) { - for (j = 0; j < 4; j++) - proj_mat[j * 4 + i] = p_mat(i, j); - } -} +/** + * Parrot Drones Awesome Video Viewer + * Desktop application + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "pdraw_desktop.h" + + +void pdraw_desktop_view_create_matrices(struct pdraw_desktop *self, + unsigned int width, + unsigned int height, + float *view_mat, + float *proj_mat, + float near, + float far) +{ + float w = 1.f; + float h = (float)width / (float)height; + float a = (far + near) / (far - near); + float b = -((2 * far * near) / (far - near)); + + Eigen::Matrix4f p_mat; + p_mat << w, 0, 0, 0, 0, h, 0, 0, 0, 0, a, b, 0, 0, 1, 0; + + Eigen::Matrix4f v_mat = Eigen::Matrix4f::Identity(); + + unsigned int i, j; + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) + view_mat[j * 4 + i] = v_mat(i, j); + } + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) + proj_mat[j * 4 + i] = p_mat(i, j); + } +} diff --git a/doc/class_diagram.graphml b/doc/class_diagram.graphml index b084c6a..50894ab 100644 --- a/doc/class_diagram.graphml +++ b/doc/class_diagram.graphml @@ -1,2116 +1,2116 @@ - - - - - - - - - - - - - - - - - - - - - - - - Channel - - - - - - - - - - + Channel::SinkListener mSinkListener -+ Channel::SourceListener mSourceListener -# vbuf_queue mQueue -# vbuf_pool mPool - + queue(vbuf_buffer) -+ flush() - - - - - - - - - - - - Channel::SinkListener - - - - - - - - - - - + onChannelQueue(Channel, vbuf_buffer) -+ onChannelDownstreamEvent(Channel, pomp_msg) - - - - - - - - - - - - - Sink - - - - - - - - - - - struct { Channel; Media } mInputPorts[] - TODO - - - - - - - - - - - - VideoSink - - - - - - - - - - + IPdraw::VideoSinkListener mVideoSinkListener; -+ Media mInputMedia; -+ RawVideoMedia mRawInputMedia; -+ CodedVideoMedia mCodedInputMedia; - + start() -+ stop() - - - - - - - - - - - - RtmpStreamMuxer - - - - - - - - - - - Media mCurrentMedia - + addInputMedia(Media) - - - - - - - - - - - - Channel::SourceListener - - - - - - - - - - - + onChannelUpstreamEvent(Channel, pomp_msg) - - - - - - - - - - - - - Element - - - - - - - - - - + Element::State mState -# Element::Listener mListener -# Session mSession -- pthread_mutex_t mMutex - + start() -+ stop() -+ lock -+ unlock() -+ getName() string - - - - - - - - - - - - Session - - - - - - - - - - IPdraw::Listener mListener -Settings mSettings -SessionSelfMetadata mSelfMetadata -SessionPeerMetadata mPeerMetadata -Element mElements[] -Demuxer mDemuxer; - - addDecoderForMedia(Source, Media) -- addMediaToRenderer(Source, Media, Renderer) -- addMediaToAllRenderers(Source, Media) -- addAllMediaToRenderer(Renderer) - - - - - - - - - - - - - Pdraw::IPdraw::Listener - - - - - - - - - - - + openResponse(pdraw, status) -+ closeResponse(pdraw, status) -+ onUnrecoverableError(pdraw) -+ selectDemuxerMedia(pdraw, pdraw_demuxer_media[]) int -+ onMediaAdded(pdraw, pdraw_media_info) -+ onMediaRemoved(pdraw, pdraw_media_info) -+ readyToPlay(pdraw, bool) -+ onEndOfRange(pdraw, timestamp) -+ playResponse(pdraw, status, timestamp, speed) -+ pauseResponse(pdraw, status, timestamp) -+ seekResponse(pdraw, status, timestamp, speed) -+ onSocketCreated(pdraw, sockfd) - - - - - - - - - - - - Gles2HmdEye - - - - - - - - - - - bunch of GL stuff - + renderEye(texure, widht, height) - - - - - - - - - - - - PdrawListener - - - - - - - - - - - pdraw mPdraw - - - - - - - - - - - - - CodedVideoMedia - - - - - - - - - - + vdef_coded_format format -+ vdef_format_info info -- sps[] -- pps[] -- vps[] - + setPs(sps[], pps[], vps[]) - - - - - - - - - - - - RawVideoMedia - - - - - - - - - - + vdef_raw_format format -+ vdef_format_info info - - - - - - - - - - - - - Media - - - - - - - - - - - Session mSession - TODO - - - - - - - - - - - - Renderer - - - - - - - - - - # IPdraw::VideoRendererListener mListener - # removeRendererListener() - - - - - - - - - - - - VideoScaler - - - - - - - - - - - vscale_scaler mScaler -- RawVideoMedia mInputMedia -- RawVideoMedia mOutputMedia - + start() -+ stop() - - - - - - - - - - - - RecordMuxer::Track - - - - - - - - - - + int trackId -+ int metadataTrackId - - - - - - - - - - - - - RecordMuxer - - - - - - - - - - - map<Media, RecordMuxer::Track> mTracks - + addInputMedia(Media) - - - - - - - - - - - - Settings - - - - - - - - - - + pdraw_pipeline_mode mPipelineMode -+ pdraw_hmd_model mHmdModel - - - - - - - - - - - - - VideoDecoder - - - - - - - - - - - vdec_decoder mDecoder -- CodedVideoMedia mInputMedia -- RawVideoMedia mOutputMedia - + start() -+ stop() - - - - - - - - - - - - VideoEncoder - - - - - - - - - - - venc_encoder mEncoder -- RawVideoMedia mInputMedia -- CodedVideoMedia mOutputMedia - + start() -+ stop() - - - - - - - - - - - - VideoCoreEglRenderer - - - - - - - - - - - EGLDisplay mDisplay - - - - - - - - - - - - - FilterElement - - - - - - - - - - - + getName() string - - - - - - - - - - - - - SinkElement - - - - - - - - - - - + getName() string - - - - - - - - - - - - SourceElement - - - - - - - - - - - + getName() string - - - - - - - - - - - - Element::Listener - - - - - - - - - - - + onElementStateChanged(Element, Element::State) -+ asyncElementStateChanged(Element, Element::State) - - - - - - - - - - - - Muxer - - - - - - - - - - - + start() -+ stop() - - - - - - - - - - - - StreamDemuxerMux - - - - - - - - - - - mux_ctx mux -- int mux_port - - setMux(mux_ctx) - - - - - - - - - - - - Pdraw::IPdraw::VideoSinkListener - - - - - - - - - - - + onVideoSinkFlush(pdraw, sink) - - - - - - - - - - - - PdrawVideoSinkListener - - - - - - - - - - - pdraw mPdraw -- pdraw_video_sink mVideoSink - - - - - - - - - - - - - Pdraw::IPdraw::VideoRendererListener - - - - - - - - - - - + onVideoRenderReady(pdraw, renderer) -+ loadVideoTexture(pdraw, renderer, ..., frame) -+ renderVideoOverlay(pdraw, renderer, ..., frame) - - - - - - - - - - - - PdrawVideoRendererListener - - - - - - - - - - - pdraw mPdraw -- pdraw_video_renderer mRenderer - - - - - - - - - - - - - Pdraw::IPdraw - - - - - - - - - - - + open(url) -+ open(localAddr, localStreamPort, localControlPort, remoteAddr, remoteStreamPort, remoteControlPort) -+ open(url, mux_ctx) -+ close() -+ getSingleStreamLocalStreamPort() port -+ getSingleStreamLocalControlPort() port -+ isReadyToPlay() bool -+ isPaused() bool -+ play(speed) -+ pause() -+ previousFrame() -+ nextFrame() -+ seek(delta, exact) -+ seekForward(delta, exact) -+ seekBack(delta, exact) -+ seekTo(timestamp, exact) -+ getDuration() timestamp -+ getCurrentTime() timestamp -+ startVideoRenderer(pdraw_rect, pdraw_video_renderer_params, IPdraw::VideoRendererListener, eglDisplay) pdraw_video_renderer -+ stopVideoRenderer(pdraw_video_renderer) -+ resizeVideoRenderer(pdraw_video_renderer, pdraw_rect) -+ setVideoRendererParams(pdraw_video_renderer, pdraw_video_renderer_params) -+ getVideoRendererParams(pdraw_video_renderer) pdraw_video_renderer_params -+ renderVideo(pdraw_video_renderer, pdraw_rect, viewMat[], projMat[]) -+ startVideoSink(mediaId, pdraw_video_sink_params, IPdraw::VideoSinkListener) pdraw_video_sink -+ stopVideoSink(pdraw_video_sink) -+ resyncVideoSink(pdraw_video_sink) -+ getVideoSinkQueue(pdraw_video_sink) vbuf_queue -+ videoSinkQueueFlushed(pdraw_video_sink) -+ getPipelineModeSetting() pdraw_pipeline_mode -+ setPipelineModeSetting(pdraw_pipeline_mode) -+ getDisplayScreenSettings() xdpi, ydpi, deviceMargins[Top/Bottom/Left/Right] -+ setDisplayScreenSettings(xdpi, ydpi, deviceMargins[Top/Bottom/Left/Right]) -+ getHmdModelSetting() pdraw_hmd_model -+ setHmdModelSetting(pdraw_hmd_model) - - - - - - - - - - - - Gles2Renderer - - - - - - - - - - # Media mLastAddedMedia -# Gles2Video mGles2Video -# Gles2Hmd mGles2Hmd - + start() -+ stop() -+ render(pdraw_rect contentPos, viewMat[], projMat[]) -+ resize(pdraw_rect renderPos) -+ setParams(pdraw_video_renderer_params) -+ getParams(pdraw_video_renderer_params*) -+ addInputMedia(Media) -+ removeInputMedia(Media) -+ removeInputMedias() - - - - - - - - - - - - StreamDemuxerNet - - - - - - - - - - - tskt_socket mStreamSock -- tskt_socket mControlSock - - - - - - - - - - - - - Source::Listener - - - - - - - - - - - + onOutputMediaAdded(Source, Media) -+ onOutputMediaRemoved(Source, Media) - - - - - - - - - - - - Source - - - - - - - - - - - struct { Media, Channels[] } mOutputPorts[] - - + queue(vbuf_buffer) -+ flush() - - - - - - - - - - - - SessionPeerMetadata - - - - - - - - - - TODO - TODO - - - - - - - - - - - - SessionSelfMetadata - - - - - - - - - - TODO - TODO - - - - - - - - - - - - StreamDemuxer - - - - - - - - - - - CodedVideoMedia mMedias[] - + start() -+ stop() -+ play(speed) -+ isPaused() bool -+ previous() -+ next() -+ seek(delta, exact) -+ seekTo(timestamp, exact) -+ getDuration() timestamp -+ getCurrentTime() timestamp - - - - - - - - - - - - RecordDemuxer - - - - - - - - - - - CodedVideoMedia mMedias[] - + start() -+ stop() -+ play(speed) -+ isPaused() bool -+ previous() -+ next() -+ seek(delta, exact) -+ seekTo(timestamp, exact) -+ getDuration() timestamp -+ getCurrentTime() timestamp - - - - - - - - - - - - Demuxer::Listener - - - - - - - - - - - + onReadyToPlay(Demuxer, bool) -+ onEndOfRange(Demuxer, timestamp) -+ selectDemuxerMedia(Demuxer, pdraw_demuxer_media[]) int -+ playResp(Demuxer, status, timestamp, speed) -+ pauseResp(Demuxer, status, timestamp) -+ seekResp(Demuxer, status, timestamp, speed) - - - - - - - - - - - - Demuxer - - - - - - - - - - # Demuxer::Listener mListener - - - - - - - - - - - - - Gles2Video - - - - - - - - - - - Session mSession -- bunch of GL stuff - + loadFrame(...) -+ renderFrame(...) - - - - - - - - - - - - Gles2Hmd - - - - - - - - - - - Session mSession -- Gles2HmdEye left -- Gles2HmdEye right - + renderHmd(texture, width, height) - - - - - - - - - - - - CPP API - - - - - - - - - - - - - - - C API - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://web.resource.org/cc/" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="41" - height="68.997391" - id="svg2" - sodipodi:version="0.32" - inkscape:version="0.45.1" - sodipodi:docbase="C:\Daten\alberts\projects\yfx" - sodipodi:docname="uml_actor.svg" - inkscape:output_extension="org.inkscape.output.svg.inkscape" - version="1.0"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="2.934351" - inkscape:cx="144.21983" - inkscape:cy="28.533711" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="true" - inkscape:window-width="1280" - inkscape:window-height="968" - inkscape:window-x="-4" - inkscape:window-y="-4" - width="48px" - height="48px" - showborder="false" - inkscape:showpageshadow="false" /> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Ebene 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(-29.5,-42.959476)"> - <a - id="a3142" - transform="matrix(1.0873906,0,0,1,-4.4741999,0)"> - <path - transform="translate(11.586889,5.2908993)" - d="M 47.02914 47.36993 A 8.5197716 9.2013531 0 1 1 29.989597,47.36993 A 8.5197716 9.2013531 0 1 1 47.02914 47.36993 z" - sodipodi:ry="9.2013531" - sodipodi:rx="8.5197716" - sodipodi:cy="47.36993" - sodipodi:cx="38.509369" - id="path2160" - style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - sodipodi:type="arc" /> - </a> - <path - sodipodi:type="arc" - style="fill:none" - id="path3134" - sodipodi:cx="43.962021" - sodipodi:cy="48.392303" - sodipodi:rx="3.7486994" - sodipodi:ry="0" - d="M 47.71072 48.392303 A 3.7486994 0 0 1 1 40.213321,48.392303 A 3.7486994 0 0 1 1 47.71072 48.392303 z" /> - <path - style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.24319649px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="M 50,61.33709 C 50,91.363211 50,92.247838 50,92.247838" - id="path3136" /> - <path - style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="M 69.760668,72.362183 C 69.760668,72.362183 69.760668,72.362183 50.239332,72.362183 C 30.239332,72.362183 30.239332,72.362183 30.239332,72.362183 L 30.239332,72.362183" - id="path3138" /> - <path - style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="M 30,111.45687 C 30,111.45687 30,111.45687 50,92.013532 C 70,111.45687 70,111.45687 70,111.45687" - id="path3140" /> - </g> -</svg> - - - - + + + + + + + + + + + + + + + + + + + + + + + + Channel + + + + + + + + + + + Channel::SinkListener mSinkListener ++ Channel::SourceListener mSourceListener +# vbuf_queue mQueue +# vbuf_pool mPool + + queue(vbuf_buffer) ++ flush() + + + + + + + + + + + + Channel::SinkListener + + + + + + + + + + + + onChannelQueue(Channel, vbuf_buffer) ++ onChannelDownstreamEvent(Channel, pomp_msg) + + + + + + + + + + + + + Sink + + + + + + + + + + - struct { Channel; Media } mInputPorts[] + TODO + + + + + + + + + + + + VideoSink + + + + + + + + + + + IPdraw::VideoSinkListener mVideoSinkListener; ++ Media mInputMedia; ++ RawVideoMedia mRawInputMedia; ++ CodedVideoMedia mCodedInputMedia; + + start() ++ stop() + + + + + + + + + + + + RtmpStreamMuxer + + + + + + + + + + - Media mCurrentMedia + + addInputMedia(Media) + + + + + + + + + + + + Channel::SourceListener + + + + + + + + + + + + onChannelUpstreamEvent(Channel, pomp_msg) + + + + + + + + + + + + + Element + + + + + + + + + + + Element::State mState +# Element::Listener mListener +# Session mSession +- pthread_mutex_t mMutex + + start() ++ stop() ++ lock ++ unlock() ++ getName() string + + + + + + + + + + + + Session + + + + + + + + + + IPdraw::Listener mListener +Settings mSettings +SessionSelfMetadata mSelfMetadata +SessionPeerMetadata mPeerMetadata +Element mElements[] +Demuxer mDemuxer; + - addDecoderForMedia(Source, Media) +- addMediaToRenderer(Source, Media, Renderer) +- addMediaToAllRenderers(Source, Media) +- addAllMediaToRenderer(Renderer) + + + + + + + + + + + + + Pdraw::IPdraw::Listener + + + + + + + + + + + + openResponse(pdraw, status) ++ closeResponse(pdraw, status) ++ onUnrecoverableError(pdraw) ++ selectDemuxerMedia(pdraw, pdraw_demuxer_media[]) int ++ onMediaAdded(pdraw, pdraw_media_info) ++ onMediaRemoved(pdraw, pdraw_media_info) ++ readyToPlay(pdraw, bool) ++ onEndOfRange(pdraw, timestamp) ++ playResponse(pdraw, status, timestamp, speed) ++ pauseResponse(pdraw, status, timestamp) ++ seekResponse(pdraw, status, timestamp, speed) ++ onSocketCreated(pdraw, sockfd) + + + + + + + + + + + + Gles2HmdEye + + + + + + + + + + - bunch of GL stuff + + renderEye(texure, widht, height) + + + + + + + + + + + + PdrawListener + + + + + + + + + + - pdraw mPdraw + + + + + + + + + + + + + CodedVideoMedia + + + + + + + + + + + vdef_coded_format format ++ vdef_format_info info +- sps[] +- pps[] +- vps[] + + setPs(sps[], pps[], vps[]) + + + + + + + + + + + + RawVideoMedia + + + + + + + + + + + vdef_raw_format format ++ vdef_format_info info + + + + + + + + + + + + + Media + + + + + + + + + + - Session mSession + TODO + + + + + + + + + + + + Renderer + + + + + + + + + + # IPdraw::VideoRendererListener mListener + # removeRendererListener() + + + + + + + + + + + + VideoScaler + + + + + + + + + + - vscale_scaler mScaler +- RawVideoMedia mInputMedia +- RawVideoMedia mOutputMedia + + start() ++ stop() + + + + + + + + + + + + RecordMuxer::Track + + + + + + + + + + + int trackId ++ int metadataTrackId + + + + + + + + + + + + + RecordMuxer + + + + + + + + + + - map<Media, RecordMuxer::Track> mTracks + + addInputMedia(Media) + + + + + + + + + + + + Settings + + + + + + + + + + + pdraw_pipeline_mode mPipelineMode ++ pdraw_hmd_model mHmdModel + + + + + + + + + + + + + VideoDecoder + + + + + + + + + + - vdec_decoder mDecoder +- CodedVideoMedia mInputMedia +- RawVideoMedia mOutputMedia + + start() ++ stop() + + + + + + + + + + + + VideoEncoder + + + + + + + + + + - venc_encoder mEncoder +- RawVideoMedia mInputMedia +- CodedVideoMedia mOutputMedia + + start() ++ stop() + + + + + + + + + + + + VideoCoreEglRenderer + + + + + + + + + + - EGLDisplay mDisplay + + + + + + + + + + + + + FilterElement + + + + + + + + + + + + getName() string + + + + + + + + + + + + + SinkElement + + + + + + + + + + + + getName() string + + + + + + + + + + + + SourceElement + + + + + + + + + + + + getName() string + + + + + + + + + + + + Element::Listener + + + + + + + + + + + + onElementStateChanged(Element, Element::State) ++ asyncElementStateChanged(Element, Element::State) + + + + + + + + + + + + Muxer + + + + + + + + + + + + start() ++ stop() + + + + + + + + + + + + StreamDemuxerMux + + + + + + + + + + - mux_ctx mux +- int mux_port + - setMux(mux_ctx) + + + + + + + + + + + + Pdraw::IPdraw::VideoSinkListener + + + + + + + + + + + + onVideoSinkFlush(pdraw, sink) + + + + + + + + + + + + PdrawVideoSinkListener + + + + + + + + + + - pdraw mPdraw +- pdraw_video_sink mVideoSink + + + + + + + + + + + + + Pdraw::IPdraw::VideoRendererListener + + + + + + + + + + + + onVideoRenderReady(pdraw, renderer) ++ loadVideoTexture(pdraw, renderer, ..., frame) ++ renderVideoOverlay(pdraw, renderer, ..., frame) + + + + + + + + + + + + PdrawVideoRendererListener + + + + + + + + + + - pdraw mPdraw +- pdraw_video_renderer mRenderer + + + + + + + + + + + + + Pdraw::IPdraw + + + + + + + + + + + + open(url) ++ open(localAddr, localStreamPort, localControlPort, remoteAddr, remoteStreamPort, remoteControlPort) ++ open(url, mux_ctx) ++ close() ++ getSingleStreamLocalStreamPort() port ++ getSingleStreamLocalControlPort() port ++ isReadyToPlay() bool ++ isPaused() bool ++ play(speed) ++ pause() ++ previousFrame() ++ nextFrame() ++ seek(delta, exact) ++ seekForward(delta, exact) ++ seekBack(delta, exact) ++ seekTo(timestamp, exact) ++ getDuration() timestamp ++ getCurrentTime() timestamp ++ startVideoRenderer(pdraw_rect, pdraw_video_renderer_params, IPdraw::VideoRendererListener, eglDisplay) pdraw_video_renderer ++ stopVideoRenderer(pdraw_video_renderer) ++ resizeVideoRenderer(pdraw_video_renderer, pdraw_rect) ++ setVideoRendererParams(pdraw_video_renderer, pdraw_video_renderer_params) ++ getVideoRendererParams(pdraw_video_renderer) pdraw_video_renderer_params ++ renderVideo(pdraw_video_renderer, pdraw_rect, viewMat[], projMat[]) ++ startVideoSink(mediaId, pdraw_video_sink_params, IPdraw::VideoSinkListener) pdraw_video_sink ++ stopVideoSink(pdraw_video_sink) ++ resyncVideoSink(pdraw_video_sink) ++ getVideoSinkQueue(pdraw_video_sink) vbuf_queue ++ videoSinkQueueFlushed(pdraw_video_sink) ++ getPipelineModeSetting() pdraw_pipeline_mode ++ setPipelineModeSetting(pdraw_pipeline_mode) ++ getDisplayScreenSettings() xdpi, ydpi, deviceMargins[Top/Bottom/Left/Right] ++ setDisplayScreenSettings(xdpi, ydpi, deviceMargins[Top/Bottom/Left/Right]) ++ getHmdModelSetting() pdraw_hmd_model ++ setHmdModelSetting(pdraw_hmd_model) + + + + + + + + + + + + Gles2Renderer + + + + + + + + + + # Media mLastAddedMedia +# Gles2Video mGles2Video +# Gles2Hmd mGles2Hmd + + start() ++ stop() ++ render(pdraw_rect contentPos, viewMat[], projMat[]) ++ resize(pdraw_rect renderPos) ++ setParams(pdraw_video_renderer_params) ++ getParams(pdraw_video_renderer_params*) ++ addInputMedia(Media) ++ removeInputMedia(Media) ++ removeInputMedias() + + + + + + + + + + + + StreamDemuxerNet + + + + + + + + + + - tskt_socket mStreamSock +- tskt_socket mControlSock + + + + + + + + + + + + + Source::Listener + + + + + + + + + + + + onOutputMediaAdded(Source, Media) ++ onOutputMediaRemoved(Source, Media) + + + + + + + + + + + + Source + + + + + + + + + + - struct { Media, Channels[] } mOutputPorts[] + + + queue(vbuf_buffer) ++ flush() + + + + + + + + + + + + SessionPeerMetadata + + + + + + + + + + TODO + TODO + + + + + + + + + + + + SessionSelfMetadata + + + + + + + + + + TODO + TODO + + + + + + + + + + + + StreamDemuxer + + + + + + + + + + - CodedVideoMedia mMedias[] + + start() ++ stop() ++ play(speed) ++ isPaused() bool ++ previous() ++ next() ++ seek(delta, exact) ++ seekTo(timestamp, exact) ++ getDuration() timestamp ++ getCurrentTime() timestamp + + + + + + + + + + + + RecordDemuxer + + + + + + + + + + - CodedVideoMedia mMedias[] + + start() ++ stop() ++ play(speed) ++ isPaused() bool ++ previous() ++ next() ++ seek(delta, exact) ++ seekTo(timestamp, exact) ++ getDuration() timestamp ++ getCurrentTime() timestamp + + + + + + + + + + + + Demuxer::Listener + + + + + + + + + + + + onReadyToPlay(Demuxer, bool) ++ onEndOfRange(Demuxer, timestamp) ++ selectDemuxerMedia(Demuxer, pdraw_demuxer_media[]) int ++ playResp(Demuxer, status, timestamp, speed) ++ pauseResp(Demuxer, status, timestamp) ++ seekResp(Demuxer, status, timestamp, speed) + + + + + + + + + + + + Demuxer + + + + + + + + + + # Demuxer::Listener mListener + + + + + + + + + + + + + Gles2Video + + + + + + + + + + - Session mSession +- bunch of GL stuff + + loadFrame(...) ++ renderFrame(...) + + + + + + + + + + + + Gles2Hmd + + + + + + + + + + - Session mSession +- Gles2HmdEye left +- Gles2HmdEye right + + renderHmd(texture, width, height) + + + + + + + + + + + + CPP API + + + + + + + + + + + + + + + C API + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="41" + height="68.997391" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.45.1" + sodipodi:docbase="C:\Daten\alberts\projects\yfx" + sodipodi:docname="uml_actor.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + version="1.0"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="2.934351" + inkscape:cx="144.21983" + inkscape:cy="28.533711" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:window-width="1280" + inkscape:window-height="968" + inkscape:window-x="-4" + inkscape:window-y="-4" + width="48px" + height="48px" + showborder="false" + inkscape:showpageshadow="false" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Ebene 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-29.5,-42.959476)"> + <a + id="a3142" + transform="matrix(1.0873906,0,0,1,-4.4741999,0)"> + <path + transform="translate(11.586889,5.2908993)" + d="M 47.02914 47.36993 A 8.5197716 9.2013531 0 1 1 29.989597,47.36993 A 8.5197716 9.2013531 0 1 1 47.02914 47.36993 z" + sodipodi:ry="9.2013531" + sodipodi:rx="8.5197716" + sodipodi:cy="47.36993" + sodipodi:cx="38.509369" + id="path2160" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + sodipodi:type="arc" /> + </a> + <path + sodipodi:type="arc" + style="fill:none" + id="path3134" + sodipodi:cx="43.962021" + sodipodi:cy="48.392303" + sodipodi:rx="3.7486994" + sodipodi:ry="0" + d="M 47.71072 48.392303 A 3.7486994 0 0 1 1 40.213321,48.392303 A 3.7486994 0 0 1 1 47.71072 48.392303 z" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.24319649px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 50,61.33709 C 50,91.363211 50,92.247838 50,92.247838" + id="path3136" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 69.760668,72.362183 C 69.760668,72.362183 69.760668,72.362183 50.239332,72.362183 C 30.239332,72.362183 30.239332,72.362183 30.239332,72.362183 L 30.239332,72.362183" + id="path3138" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 30,111.45687 C 30,111.45687 30,111.45687 50,92.013532 C 70,111.45687 70,111.45687 70,111.45687" + id="path3140" /> + </g> +</svg> + + + + diff --git a/doc/pipeline.graphml b/doc/pipeline.graphml index 9578f71..6bc9919 100644 --- a/doc/pipeline.graphml +++ b/doc/pipeline.graphml @@ -1,806 +1,806 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Pdraw::Session - - - - - - - - - - - Demuxer - - - - - - - - - - - RecordDemuxer -(MP4) - - - - - - - - - - - StreamDemuxer -(RTP/AVP, RTSP) - - - - - - - - - - - VideoDecoder -(H.264/H.265) - - - - - - - - - - - Renderer - - - - - - - - - - - Gles2Renderer -(OpenGL ES 2.0) - - - - - - - - - - - ExternalRawVideoSink - - - - - - - - - - - MP4 file - - - - - - - - - - - RTP/AVP stream -RTSP stream - - - - - - - - - - - Display - - - - - - - - - - - Application - - - - - - - - - - - 0..n - - - - - - - - - - - 0..n - - - - - - - - - - - 0..n - - - - - - - - - - - 0..n - - - - - - - - - - - Muxer - - - - - - - - - - - 0..n - - - - - - - - - - - VideoEncoder -(H.264/H.265) - - - - - - - - - - - VideoScaler - - - - - - - - - - - CodedVideo -(1..1) - - - - - - - - - - - RawVideo -(1..1) - - - - - - - - - - - CodedVideo -(1..1) - - - - - - - - - - - RawVideo -(1..1) - - - - - - - - - - - RawVideo -(1..1) - - - - - - - - - - - RawVideo -(1..1) - - - - - - - - - - - CodedVideo -(0..n) - - - - - - - - - - - libvideo-decode - - - - - - - - - - - libvideo-encode - - - - - - - - - - - libvideo-scale - - - - - - - - - - - 0..n - - - - - - - - - - - 0..n - - - - - - - - - - - 0..n - - - - - - - - - - - ExternalCodedVideoSink - - - - - - - - - - - Application - - - - - - - - - - - 0..n - - - - - - - - - - - CodedVideo -(1..1) - - - - - - - - - - - RawVideo -(1..1) - - - - - - - - - - - MP4 file - - - - - - - - - - - RTMP stream - - - - - - - - - - - StreamDemuxerNet - - - - - - - - - - - - StreamDemuxerMux - - - - - - - - - - - - libtransport-socket - - - - - - - - - - - libmux (mux_ip_proxy) - - - - - - - - - - - libmp4 - - - - - - - - - - - RecordMuxer -(MP4) - - - - - - - - - - - libmp4 - - - - - - - - - - - RtmpStreamMuxer -(RTMP) - - - - - - - - - - - librtmp - - - - - - - - - - - RawVideo -(1..1) - - - - - - - - - - - CodedVideo -(0..n) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Pdraw::Session + + + + + + + + + + + Demuxer + + + + + + + + + + + RecordDemuxer +(MP4) + + + + + + + + + + + StreamDemuxer +(RTP/AVP, RTSP) + + + + + + + + + + + VideoDecoder +(H.264/H.265) + + + + + + + + + + + Renderer + + + + + + + + + + + Gles2Renderer +(OpenGL ES 2.0) + + + + + + + + + + + ExternalRawVideoSink + + + + + + + + + + + MP4 file + + + + + + + + + + + RTP/AVP stream +RTSP stream + + + + + + + + + + + Display + + + + + + + + + + + Application + + + + + + + + + + + 0..n + + + + + + + + + + + 0..n + + + + + + + + + + + 0..n + + + + + + + + + + + 0..n + + + + + + + + + + + Muxer + + + + + + + + + + + 0..n + + + + + + + + + + + VideoEncoder +(H.264/H.265) + + + + + + + + + + + VideoScaler + + + + + + + + + + + CodedVideo +(1..1) + + + + + + + + + + + RawVideo +(1..1) + + + + + + + + + + + CodedVideo +(1..1) + + + + + + + + + + + RawVideo +(1..1) + + + + + + + + + + + RawVideo +(1..1) + + + + + + + + + + + RawVideo +(1..1) + + + + + + + + + + + CodedVideo +(0..n) + + + + + + + + + + + libvideo-decode + + + + + + + + + + + libvideo-encode + + + + + + + + + + + libvideo-scale + + + + + + + + + + + 0..n + + + + + + + + + + + 0..n + + + + + + + + + + + 0..n + + + + + + + + + + + ExternalCodedVideoSink + + + + + + + + + + + Application + + + + + + + + + + + 0..n + + + + + + + + + + + CodedVideo +(1..1) + + + + + + + + + + + RawVideo +(1..1) + + + + + + + + + + + MP4 file + + + + + + + + + + + RTMP stream + + + + + + + + + + + StreamDemuxerNet + + + + + + + + + + + + StreamDemuxerMux + + + + + + + + + + + + libtransport-socket + + + + + + + + + + + libmux (mux_ip_proxy) + + + + + + + + + + + libmp4 + + + + + + + + + + + RecordMuxer +(MP4) + + + + + + + + + + + libmp4 + + + + + + + + + + + RtmpStreamMuxer +(RTMP) + + + + + + + + + + + librtmp + + + + + + + + + + + RawVideo +(1..1) + + + + + + + + + + + CodedVideo +(0..n) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gst-pdraw/atom.mk b/gst-pdraw/atom.mk index 4c48e74..31dd818 100644 --- a/gst-pdraw/atom.mk +++ b/gst-pdraw/atom.mk @@ -1,26 +1,26 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := gst-pdraw -LOCAL_CATEGORY_PATH := multimedia/gstreamer -LOCAL_DESCRIPTION := GStreamer PDrAW plugin -LOCAL_DESTDIR := usr/lib/gstreamer-1.0 -LOCAL_MODULE_FILENAME := libgstpdraw.so -LOCAL_CODECHECK_C := none - -LOCAL_LIBRARIES := \ - glib \ - gstreamer \ - gst-plugins-base \ - libmedia-buffers \ - libmedia-buffers-memory-generic \ - libpdraw \ - libpdraw-backend \ - libulog \ - libvideo-defs - -LOCAL_SRC_FILES := \ - gstpdrawsrc.c - -include $(BUILD_SHARED_LIBRARY) +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := gst-pdraw +LOCAL_CATEGORY_PATH := multimedia/gstreamer +LOCAL_DESCRIPTION := GStreamer PDrAW plugin +LOCAL_DESTDIR := usr/lib/gstreamer-1.0 +LOCAL_MODULE_FILENAME := libgstpdraw.so +LOCAL_CODECHECK_C := none + +LOCAL_LIBRARIES := \ + glib \ + gstreamer \ + gst-plugins-base \ + libmedia-buffers \ + libmedia-buffers-memory-generic \ + libpdraw \ + libpdraw-backend \ + libulog \ + libvideo-defs + +LOCAL_SRC_FILES := \ + gstpdrawsrc.c + +include $(BUILD_SHARED_LIBRARY) diff --git a/gst-pdraw/gstpdrawsrc.c b/gst-pdraw/gstpdrawsrc.c index fc94339..c6de0f1 100644 --- a/gst-pdraw/gstpdrawsrc.c +++ b/gst-pdraw/gstpdrawsrc.c @@ -1,929 +1,929 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * GStreamer PDrAW source plugin - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* clang-format off */ -#include -#include -#include - -#include "gstpdrawsrc.h" - -/** Log error with errno and gstreamer object */ -#define LOG_ERRNO_OBJECT(_obj, _fct, _err) \ - GST_ERROR_OBJECT(_obj, "%s err=%d(%s)", \ - _fct, _err, g_strerror(_err)) - -#define SUPPORTED_RAW_FORMATS "{ I420, NV12 }" - -static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ( - "video/x-h264, " - "width = (int) [16, MAX], " - "height = (int) [16, MAX], " - "framerate = (fraction) [0/1, MAX], " - "stream-format = (string) { byte-stream, avc }" - )); - -struct _GstPdrawSrc -{ - /* parent */ - GstPushSrc parent; - - /* media added condition */ - GMutex media_lock; - GCond media_cond; - struct pdraw_media_info *media_info; - - /* setup condition, i.e. vsink started */ - GMutex setup_lock; - GCond setup_cond; - gboolean started; - - /* PDrAW elements */ - struct pdraw_backend *pdraw; - struct pdraw_demuxer *demuxer; - struct pdraw_coded_video_sink *sink; - struct mbuf_coded_video_frame_queue *queue; - - /* EOS */ - gboolean eos; - - /* Properties */ - gchar *url; - - /* helpers */ - struct mbuf_coded_video_frame *negotiation_frame; -}; - -/* Type definition */ -#define gst_pdraw_src_parent_class parent_class -G_DEFINE_TYPE (GstPdrawSrc, gst_pdraw_src, GST_TYPE_PUSH_SRC); - -/* properties */ -enum -{ - PROP_0, - PROP_URL, -}; - -/* GObject methods */ -static void gst_pdraw_src_finalize (GObject * object); -static void gst_pdraw_src_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_pdraw_src_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -/* GstBaseSrc methods */ -static gboolean gst_pdraw_src_start (GstBaseSrc * bsrc); -static gboolean gst_pdraw_src_stop (GstBaseSrc * bsrc); -static gboolean gst_pdraw_src_negotiate (GstBaseSrc * bsrc); - -/* GstPushSrc methods */ -static GstFlowReturn gst_pdraw_src_create (GstPushSrc * psrc, GstBuffer ** buf); - -/* helpers */ -static struct mbuf_coded_video_frame * get_frame_from_vsink (GstPdrawSrc * pdrawsrc); -static GstBuffer * make_codec_data (const struct pdraw_video_info * info); -static gboolean update_codec_data (GstPdrawSrc * pdrawsrc, GstCaps * caps); - -/* PDrAW callbacks */ -static void stop_resp (struct pdraw_backend * pdraw, int status, - void * userdata); -static void media_added (struct pdraw_backend * pdraw, - const struct pdraw_media_info * info, void * userdata); -static void media_removed (struct pdraw_backend * pdraw, - const struct pdraw_media_info * info, void * userdata); -static void open_resp (struct pdraw_backend * pdraw, - struct pdraw_demuxer * demuxer, int status, void * userdata); -static void close_resp (struct pdraw_backend * pdraw, - struct pdraw_demuxer * demuxer, int status, void * userdata); -static void ready_to_play (struct pdraw_backend * pdraw, - struct pdraw_demuxer * demuxer, int ready, void * userdata); -static void end_of_range (struct pdraw_backend *pdraw, - struct pdraw_demuxer * demuxer, uint64_t timestamp, void * userdata); -static void play_resp (struct pdraw_backend * pdraw, - struct pdraw_demuxer * demuxer, int status, uint64_t timestamp, - float speed, void * userdata); -static void sink_flush (struct pdraw_backend * pdraw, - struct pdraw_coded_video_sink * sink, void * userdata); - -static const struct pdraw_backend_cbs pdraw_cbs = { - .stop_resp = &stop_resp, - .media_added = &media_added, - .media_removed = &media_removed, -}; - -static const struct pdraw_backend_demuxer_cbs demuxer_cbs = { - .open_resp = &open_resp, - .close_resp = &close_resp, - .ready_to_play = &ready_to_play, - .end_of_range = &end_of_range, - .play_resp = &play_resp, -}; - -static const struct pdraw_backend_coded_video_sink_cbs vsink_cbs = { - .flush = &sink_flush, -}; - -/* helpers */ -static struct mbuf_coded_video_frame * -get_frame_from_vsink (GstPdrawSrc * pdrawsrc) -{ - struct mbuf_coded_video_frame *frame = NULL; - int res; - - g_mutex_lock (&pdrawsrc->setup_lock); - - while ((!pdrawsrc->started) || (pdrawsrc->queue == NULL)) - g_cond_wait (&pdrawsrc->setup_cond, &pdrawsrc->setup_lock); - - if (pdrawsrc->queue == NULL) - goto out; - - if (g_atomic_int_get (&pdrawsrc->eos)) - goto out; - - res = mbuf_coded_video_frame_queue_pop (pdrawsrc->queue, &frame); - if (res < 0 && res != -EAGAIN) - LOG_ERRNO_OBJECT (pdrawsrc, "mbuf_coded_video_frame_queue_pop", -res); - -out: - g_mutex_unlock (&pdrawsrc->setup_lock); - return frame; -} - -static GstBuffer * -make_codec_data (const struct pdraw_video_info * info) -{ - GstBuffer *buf = NULL; - - switch (info->format) - { - case VDEF_FRAME_TYPE_CODED: - { - gsize avc_size, sps_size, pps_size; - guint off = 0; - guint8 *data; - const guint8 *sps, *pps; - - sps_size = info->coded.h264.spslen; - pps_size = info->coded.h264.ppslen; - sps = info->coded.h264.sps; - pps = info->coded.h264.pps; - - avc_size = sps_size + pps_size + 11; - data = g_malloc0 (avc_size); - - data[off++] = 0x01; /* AVCDecoderConfiguration version 1 */ - data[off++] = sps[1]; /* profle_idc */ - data[off++] = sps[2]; /* profile_compatibility */ - data[off++] = sps[3]; /* level_idc */ - data[off++] = 0xFC | (4 - 1); /* NALU Length Size Minus One */ - - data[off++] = 0xE0 | 1; /* number of SPS NALUs */ - - /* SPS size and SPS NALU data */ - data[off++] = (sps_size >> 8) & 0xFF; - data[off++] = sps_size & 0xFF; - memcpy (data + off, sps, sps_size); - off += sps_size; - - data[off++] = 0x01; /* number of PPS NALUs */ - - /* PPS size and PPS NALU data */ - data[off++] = (pps_size >> 8) & 0xFF; - data[off++] = pps_size & 0xFF; - memcpy (data + off, pps, pps_size); - off += pps_size; - - buf = gst_buffer_new_allocate (NULL, avc_size, NULL); - if (buf) - gst_buffer_fill (buf, 0, data, avc_size); - - GST_MEMDUMP ("avc-header", data, avc_size); - g_free (data); - - break; - } - case VDEF_FRAME_TYPE_RAW: /* do nothing */ - default: - break; - } - - return buf; -} - -static gboolean -update_codec_data (GstPdrawSrc * pdrawsrc, GstCaps * caps) -{ - gboolean ret = FALSE; - const struct pdraw_video_frame *meta = NULL; - int res; - GstBuffer *codec_data_buf; - struct mbuf_ancillary_data *ancillaryData = NULL; - struct mbuf_coded_video_frame *frame = pdrawsrc->negotiation_frame; - - if (frame == NULL) { - GST_ERROR_OBJECT (pdrawsrc, "no frame to update codec data"); - goto out; - } - - res = mbuf_coded_video_frame_get_ancillary_data (frame, - PDRAW_ANCILLARY_DATA_KEY_VIDEOFRAME, &ancillaryData); - if (res < 0) { - LOG_ERRNO_OBJECT (pdrawsrc, "mbuf_coded_video_frame_get_ancillary_data", - -res); - goto out; - } - meta = mbuf_ancillary_data_get_buffer (ancillaryData, NULL); - - /* TODO: H265 support */ - if ((meta->format != VDEF_FRAME_TYPE_CODED) || - (meta->coded.format.encoding != VDEF_ENCODING_H264)) { - GST_INFO_OBJECT (pdrawsrc, "no codec data needed for video format"); - ret = TRUE; - goto out; - } - - if (meta->coded.format.data_format == VDEF_CODED_DATA_FORMAT_AVCC) - gst_caps_set_simple (caps, "stream-format", G_TYPE_STRING, - "avc", NULL); - else if (meta->coded.format.data_format == - VDEF_CODED_DATA_FORMAT_BYTE_STREAM) - gst_caps_set_simple (caps, "stream-format", G_TYPE_STRING, - "byte-stream", NULL); - - codec_data_buf = make_codec_data (&pdrawsrc->media_info->video); - if (codec_data_buf) { - gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, - codec_data_buf, NULL); - gst_buffer_unref (codec_data_buf); - } - - ret = TRUE; - -out: - if (ancillaryData) - mbuf_ancillary_data_unref (ancillaryData); - return ret; -} - -/* PDrAW callbacks */ -static void -stop_resp (struct pdraw_backend * pdraw, int status, void * userdata) -{ - GstPdrawSrc *pdrawsrc = userdata; - - if (status < 0) { - GST_ERROR_OBJECT (pdrawsrc, "%s: failed status=%d(%s)", - __func__, -status, g_strerror(-status)); - } - - /* TODO */ -} - -static void -media_added (struct pdraw_backend *pdraw, const struct pdraw_media_info *info, - void *userdata) -{ - GstPdrawSrc *pdrawsrc = userdata; - int res; - struct pdraw_video_sink_params params = { - .queue_max_count = 1, - }; - - if (info->type != PDRAW_MEDIA_TYPE_VIDEO || - info->video.format != VDEF_FRAME_TYPE_CODED || - info->video.type != PDRAW_VIDEO_TYPE_DEFAULT_CAMERA) - return; - - g_mutex_lock (&pdrawsrc->setup_lock); - - if (pdrawsrc->sink != NULL) - goto out; - - res = pdraw_be_coded_video_sink_new (pdrawsrc->pdraw, info->id, ¶ms, - &vsink_cbs, pdrawsrc, &pdrawsrc->sink); - if (res < 0) { - LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_coded_video_sink_new", -res); - goto out; - } - - /* save media info for caps negotiation */ - g_mutex_lock (&pdrawsrc->media_lock); - pdrawsrc->media_info = pdraw_media_info_dup (info); - g_cond_signal (&pdrawsrc->media_cond); - g_mutex_unlock (&pdrawsrc->media_lock); - - GST_DEBUG_OBJECT (pdrawsrc, "media added: id=%d, type=%d", - pdrawsrc->media_info->id, pdrawsrc->media_info->type); - - pdrawsrc->queue = pdraw_be_coded_video_sink_get_queue (pdrawsrc->pdraw, - pdrawsrc->sink); - if (pdrawsrc->queue == NULL) { - res = -EPROTO; - LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_coded_video_sink_get_queue", -res); - goto out; - } - -out: - g_cond_signal (&pdrawsrc->setup_cond); - g_mutex_unlock (&pdrawsrc->setup_lock); -} - -static void -media_removed (struct pdraw_backend * pdraw, - const struct pdraw_media_info * info, void * userdata) -{ - GstPdrawSrc *pdrawsrc = userdata; - int res; - - g_mutex_lock (&pdrawsrc->setup_lock); - - if (pdrawsrc->sink == NULL) - goto out; - if (pdrawsrc->media_info->id != info->id) - goto out; - - res = pdraw_be_coded_video_sink_destroy (pdrawsrc->pdraw, pdrawsrc->sink); - if (res < 0) - LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_coded_video_sink_destroy", -res); - pdrawsrc->sink = NULL; - pdrawsrc->queue = NULL; - pdraw_media_info_free (pdrawsrc->media_info); - pdrawsrc->media_info = NULL; - -out: - g_cond_signal (&pdrawsrc->setup_cond); - g_mutex_unlock (&pdrawsrc->setup_lock); -} - -static void -open_resp (struct pdraw_backend * pdraw, struct pdraw_demuxer * demuxer, - int status, void * userdata) -{ - GstPdrawSrc *pdrawsrc = userdata; - - if (status < 0) { - GST_ERROR_OBJECT (pdrawsrc, "%s: failed status=%d(%s)", - __func__, -status, g_strerror(-status)); - } - - g_mutex_lock (&pdrawsrc->setup_lock); - pdrawsrc->started = TRUE; - g_cond_signal (&pdrawsrc->setup_cond); - g_mutex_unlock (&pdrawsrc->setup_lock); -} - -static void -close_resp (struct pdraw_backend * pdraw, struct pdraw_demuxer * demuxer, - int status, void * userdata) -{ - GstPdrawSrc *pdrawsrc = userdata; - - if (status < 0) { - GST_ERROR_OBJECT (pdrawsrc, "%s: failed status=%d(%s)", - __func__, -status, g_strerror(-status)); - } - - g_mutex_lock (&pdrawsrc->setup_lock); - pdrawsrc->started = FALSE; - g_cond_signal (&pdrawsrc->setup_cond); - g_mutex_unlock (&pdrawsrc->setup_lock); -} - -static void -ready_to_play (struct pdraw_backend * pdraw, struct pdraw_demuxer * demuxer, - int ready, void * userdata) -{ - GstPdrawSrc *pdrawsrc = userdata; - int res; - - if (!ready) - return; - - res = pdraw_be_demuxer_play (pdraw, demuxer); - if (res < 0) - LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_demuxer_play", -res); -} - -static void -end_of_range (struct pdraw_backend * pdraw, struct pdraw_demuxer * demuxer, - uint64_t timestamp, void * userdata) -{ - GstPdrawSrc *pdrawsrc = userdata; - - GST_INFO_OBJECT (pdrawsrc, "end of media reached: send EOS upstream"); - g_atomic_int_set (&pdrawsrc->eos, TRUE); -} - -static void -play_resp (struct pdraw_backend *pdraw, struct pdraw_demuxer * demuxer, - int status, uint64_t timestamp, float speed, void *userdata) -{ - GstPdrawSrc *pdrawsrc = userdata; - - if (status < 0) { - GST_ERROR_OBJECT (pdrawsrc, "%s: failed status=%d(%s)", - __func__, -status, g_strerror(-status)); - } -} - -static void -sink_flush (struct pdraw_backend * pdraw, struct pdraw_coded_video_sink * sink, - void *userdata) -{ - GstPdrawSrc *pdrawsrc = userdata; - struct mbuf_coded_video_frame_queue *queue; - int res; - - queue = pdraw_be_coded_video_sink_get_queue (pdraw, sink); - if (queue == NULL) { - res = -EPROTO; - LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_coded_video_sink_get_queue", -res); - return; - } - - res = mbuf_coded_video_frame_queue_flush (queue); - if (res < 0) { - LOG_ERRNO_OBJECT (pdrawsrc, "mbuf_coded_video_frame_queue_flush", -res); - return; - } - - res = pdraw_be_coded_video_sink_queue_flushed (pdraw, sink); - if (res < 0) { - LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_coded_video_sink_queue_flushed", - -res); - return; - } -} - -static void -gst_pdraw_src_class_init (GstPdrawSrcClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); - GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass); - GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass); - - gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_pdraw_src_finalize); - gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_pdraw_src_set_property); - gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_pdraw_src_get_property); - - gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_pdraw_src_start); - gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_pdraw_src_stop); - gstbasesrc_class->negotiate = GST_DEBUG_FUNCPTR (gst_pdraw_src_negotiate); - - gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_pdraw_src_create); - - g_object_class_install_property (gobject_class, PROP_URL, - g_param_spec_string ("url", "URL", - "Either RTSP URL or path to MP4 file", "", - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - gst_element_class_set_static_metadata (gstelement_class, - "GstPdrawSrc", - "Source/Video", - "Parrot PDrAW source", - "Parrot Drones "); - - gst_element_class_add_static_pad_template (gstelement_class, &src_template); -} - -static void -gst_pdraw_src_init (GstPdrawSrc * pdrawsrc) -{ - GstPad *srcpad = GST_BASE_SRC_PAD (pdrawsrc); - - g_mutex_init (&pdrawsrc->media_lock); - g_cond_init (&pdrawsrc->media_cond); - - g_mutex_init (&pdrawsrc->setup_lock); - g_cond_init (&pdrawsrc->setup_cond); - - gst_base_src_set_format (GST_BASE_SRC (pdrawsrc), GST_FORMAT_TIME); - gst_base_src_set_live (GST_BASE_SRC (pdrawsrc), TRUE); - - gst_pad_use_fixed_caps (srcpad); -} - -/* GObject methods */ -static void -gst_pdraw_src_finalize (GObject * object) -{ - GstPdrawSrc *pdrawsrc = GST_PDRAW_SRC (object); - int res; - - if (pdrawsrc->pdraw != NULL) { - g_mutex_lock (&pdrawsrc->setup_lock); - - while ((pdrawsrc->sink != NULL) || (pdrawsrc->started)) - g_cond_wait (&pdrawsrc->setup_cond, &pdrawsrc->setup_lock); - - g_mutex_unlock (&pdrawsrc->setup_lock); - - res = pdraw_be_destroy (pdrawsrc->pdraw); - if (res < 0) - LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_destroy", -res); - pdrawsrc->pdraw = NULL; - pdrawsrc->sink = NULL; - pdrawsrc->queue = NULL; - g_slice_free (struct pdraw_media_info, (void *) pdrawsrc->media_info); - } - - g_mutex_clear (&pdrawsrc->setup_lock); - g_cond_clear (&pdrawsrc->setup_cond); - - g_mutex_clear (&pdrawsrc->media_lock); - g_cond_clear (&pdrawsrc->media_cond); - - g_free (pdrawsrc->url); - pdrawsrc->url = NULL; - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_pdraw_src_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstPdrawSrc *pdrawsrc = GST_PDRAW_SRC (object); - - switch (prop_id) { - case PROP_URL: - g_free (pdrawsrc->url); - pdrawsrc->url = g_value_dup_string (value); - /* TODO: need to reconfigure in case this property changed in - * PLAYING state */ - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (pdrawsrc, prop_id, pspec); - break; - } -} - -static void -gst_pdraw_src_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstPdrawSrc *pdrawsrc = GST_PDRAW_SRC (object); - - switch (prop_id) { - case PROP_URL: - g_value_set_string (value, pdrawsrc->url); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (pdrawsrc, prop_id, pspec); - break; - } -} - -/* GstBaseSrc methods */ -static gboolean -gst_pdraw_src_start (GstBaseSrc * bsrc) -{ - GstPdrawSrc *pdrawsrc = GST_PDRAW_SRC (bsrc); - gboolean ret = FALSE; - int res; - - if (pdrawsrc->pdraw == NULL) { - res = pdraw_be_new (&pdraw_cbs, pdrawsrc, &pdrawsrc->pdraw); - if (res < 0) { - LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_new", -res); - goto out; - } - } - - res = pdraw_be_set_pipeline_mode_setting (pdrawsrc->pdraw, - PDRAW_PIPELINE_MODE_DECODE_NONE); - if (res < 0) { - LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_set_pipeline_mode_setting", -res); - goto out; - } - - res = pdraw_be_demuxer_new_from_url (pdrawsrc->pdraw, pdrawsrc->url, - &demuxer_cbs, pdrawsrc, &pdrawsrc->demuxer); - if (res < 0) { - LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_open_url", -res); - goto out; - } - - ret = TRUE; - -out: - return ret; -} - -static gboolean -gst_pdraw_src_stop (GstBaseSrc * bsrc) -{ - GstPdrawSrc *pdrawsrc = GST_PDRAW_SRC (bsrc); - int res; - - if (pdrawsrc->pdraw == NULL) - return TRUE; - - res = pdraw_be_demuxer_close (pdrawsrc->pdraw, pdrawsrc->demuxer); - if (res < 0) { - LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_demuxer_close", -res); - return FALSE; - } - - return TRUE; -} - -static gboolean -gst_pdraw_src_negotiate (GstBaseSrc * bsrc) -{ - GstPdrawSrc *pdrawsrc = GST_PDRAW_SRC (bsrc); - const struct pdraw_video_info *video_info; - gboolean ret = FALSE; - GstCaps *allowed_caps, *desired_caps = NULL; - GstCaps *our_caps = NULL, *peer_caps = NULL; - - allowed_caps = gst_pad_get_allowed_caps (GST_BASE_SRC_PAD (bsrc)); - GST_DEBUG_OBJECT (pdrawsrc, "allowed caps: %" GST_PTR_FORMAT, allowed_caps); - - if (gst_caps_is_empty (allowed_caps)) { - GST_ERROR_OBJECT (pdrawsrc, "empty caps"); - gst_caps_unref (allowed_caps); - goto out; - } - - g_mutex_lock (&pdrawsrc->media_lock); - GST_DEBUG_OBJECT (pdrawsrc, "waiting for media to be added"); - while (pdrawsrc->media_info == NULL) - g_cond_wait (&pdrawsrc->media_cond, &pdrawsrc->media_lock); - g_mutex_unlock (&pdrawsrc->media_lock); - - - video_info = &pdrawsrc->media_info->video; - - /* get a first frame and store it to be used by _create later */ - pdrawsrc->negotiation_frame = get_frame_from_vsink (pdrawsrc); - if (pdrawsrc->negotiation_frame == NULL) { - GST_ERROR_OBJECT (pdrawsrc, "failed to get first frame for negotiation"); - goto out; - } - - switch (video_info->format) - { - case VDEF_FRAME_TYPE_CODED: - /* TODO: support H.265 */ - desired_caps = gst_caps_new_simple ("video/x-h264", - "width", G_TYPE_INT, video_info->coded.info.resolution.width, - "height", G_TYPE_INT, video_info->coded.info.resolution.height, - NULL); - update_codec_data (pdrawsrc, desired_caps); - break; - case VDEF_FRAME_TYPE_RAW: - GST_FIXME_OBJECT (pdrawsrc, "handle YUV format"); - goto out; - default: - GST_WARNING_OBJECT (pdrawsrc, "cannot handle this video format: %d", - video_info->format); - goto out; - } - - if (!gst_caps_can_intersect (desired_caps, allowed_caps)) { - GST_ERROR_OBJECT (pdrawsrc, "cannot find common caps with upstream"); - goto out; - } - - our_caps = gst_caps_intersect (desired_caps, allowed_caps); - gst_caps_unref (desired_caps); - gst_caps_unref (allowed_caps); - - our_caps = gst_caps_fixate (our_caps); - GST_DEBUG_OBJECT (pdrawsrc, "desired caps %" GST_PTR_FORMAT, our_caps); - - /* get the peer pad caps filtered out with our caps */ - peer_caps = gst_pad_peer_query_caps (GST_BASE_SRC_PAD (bsrc), our_caps); - GST_DEBUG_OBJECT (pdrawsrc, "filtered caps of peer: %" GST_PTR_FORMAT, - peer_caps); - - if (!gst_caps_is_fixed (peer_caps)) { - GST_ERROR_OBJECT (pdrawsrc, "peer caps not fixed: %" GST_PTR_FORMAT, - peer_caps); - goto out; - } - - ret = gst_base_src_set_caps (bsrc, peer_caps); - -out: - if (peer_caps) - gst_caps_unref (peer_caps); - if (our_caps) - gst_caps_unref (our_caps); - return ret; -} - -static void -out_mem_unref (gpointer data) -{ - (void) mbuf_coded_video_frame_unref ((struct mbuf_coded_video_frame *) data); -} - -/* GstPushSrc methods */ -static GstFlowReturn -gst_pdraw_src_create (GstPushSrc * psrc, GstBuffer ** buf) -{ - GstPdrawSrc *pdrawsrc = GST_PDRAW_SRC (psrc); - struct mbuf_coded_video_frame *frame = NULL; - struct mbuf_ancillary_data *ancillarydata = NULL; - const struct pdraw_video_frame *meta = NULL; - const void *data; - struct vdef_coded_frame frameinfo; - size_t size; - gssize gsize; - GstBuffer *newbuf = NULL; - int res; - gboolean silent = FALSE; - - /* loop until a non silent frame can be produced */ - do - { - if (pdrawsrc->negotiation_frame) { - frame = pdrawsrc->negotiation_frame; - pdrawsrc->negotiation_frame = NULL; - } else { - frame = get_frame_from_vsink (pdrawsrc); - } - - if (frame == NULL) { - if (G_UNLIKELY (g_atomic_int_compare_and_exchange (&pdrawsrc->eos, TRUE, - FALSE))) { - goto eos; - } else { - goto error; - } - } - - res = mbuf_coded_video_frame_get_frame_info (frame, &frameinfo); - if (res < 0) { - LOG_ERRNO_OBJECT (pdrawsrc, "mbuf_coded_video_frame_get_frame_info", - -res); - goto error; - } - - res = mbuf_coded_video_frame_get_packed_buffer (frame, &data, &size); - if (res == 0) { - /* Frame is already packed */ - mbuf_coded_video_frame_ref (frame); - } else if (res == -EPROTO) { - /* Frame is not packed, copy & pack it */ - struct mbuf_mem *mem; - struct mbuf_coded_video_frame *new_frame; - res = mbuf_mem_generic_new (size, &mem); - if (res < 0) { - LOG_ERRNO_OBJECT (pdrawsrc, "mbuf_mem_generic_new", -res); - goto error; - } - res = mbuf_coded_video_frame_copy (frame, mem, &new_frame); - if (res < 0) { - LOG_ERRNO_OBJECT (pdrawsrc, "mbuf_coded_video_frame_copy", -res); - goto error; - } - mbuf_coded_video_frame_unref (frame); - frame = new_frame; - res = mbuf_coded_video_frame_get_packed_buffer (frame, &data, &size); - if (res < 0) { - LOG_ERRNO_OBJECT (pdrawsrc, "mbuf_coded_video_frame_get_packed_buffer", - -res); - goto error; - } - } else { - LOG_ERRNO_OBJECT (pdrawsrc, "mbuf_coded_video_frame_get_packed_buffer", - -res); - goto error; - } - gsize = size; - - res = mbuf_coded_video_frame_get_ancillary_data (frame, - PDRAW_ANCILLARY_DATA_KEY_VIDEOFRAME, &ancillarydata); - if (res < 0) { - LOG_ERRNO_OBJECT (pdrawsrc, "mbuf_coded_video_frame_get_ancillary_data", -res); - goto error; - } - meta = mbuf_ancillary_data_get_buffer (ancillarydata, NULL); - - switch (meta->format) - { - case VDEF_FRAME_TYPE_CODED: - GST_DEBUG_OBJECT (pdrawsrc, - "coded video format=" VDEF_CODED_FORMAT_TO_STR_FMT - ", is_silent=%d, is_sync=%d, is_ref=%d, size=%zu, PTS=%lu", - VDEF_CODED_FORMAT_TO_STR_ARG (&meta->coded.format), - !!(meta->coded.info.flags & VDEF_FRAME_FLAG_SILENT), - meta->is_sync, - meta->is_ref, - size, - meta->ntp_raw_timestamp); - break; - case VDEF_FRAME_TYPE_RAW: - GST_FIXME_OBJECT (pdrawsrc, "handle raw formats"); - break; - default: - break; - } - - silent = (meta->coded.info.flags & VDEF_FRAME_FLAG_SILENT); - if (silent) { - mbuf_ancillary_data_unref (ancillarydata); - ancillarydata = NULL; - mbuf_coded_video_frame_release_packed_buffer(frame, data); - mbuf_coded_video_frame_unref(frame); - } - } while (silent); - if (ancillarydata) { - mbuf_ancillary_data_unref (ancillarydata); - ancillarydata = NULL; - } - - newbuf = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, - (gpointer) data, gsize, 0, gsize, frame, out_mem_unref); - if (newbuf == NULL) { - res = -ENOMEM; - LOG_ERRNO_OBJECT (pdrawsrc, "gst_buffer_new_wrapped_full", -res); - goto error; - } - - /* Release the packed buffer here, since gstreamer will only unref the frame. - * This is sketchy, because it means releasing the read-lock, allowing another - * thread to modify the frame while it is being decoded, but we're the only - * consumer in this case, so we can do it. - */ - res = mbuf_coded_video_frame_release_packed_buffer(frame, data); - data = NULL; - - GST_BUFFER_PTS (newbuf) = meta->ntp_raw_timestamp * 1000; /* to nanoseconds */ - - *buf = newbuf; - return GST_FLOW_OK; - -eos: - g_assert (frame == NULL && newbuf == NULL); - return GST_FLOW_EOS; - -error: - if (ancillarydata) - mbuf_ancillary_data_unref (ancillarydata); - if (frame && data) - mbuf_coded_video_frame_release_packed_buffer (frame, data); - if (frame) - mbuf_coded_video_frame_unref (frame); - return GST_FLOW_ERROR; -} - -/* plugin init */ -#define PACKAGE "gst-pdraw" -#define PACKAGE_VERSION "1.0.0" -#define PARROT_LICENSE "Proprietary" -#define PACKAGE_NAME "GstPdraw" -#define PACKAGE_ORIGIN "http://www.parrot.com" - -GST_DEBUG_CATEGORY (gst_pdraw_src_debug); -#define GST_CAT_DEFAULT gst_pdraw_src_debug - -static gboolean -plugin_init (GstPlugin * plugin) -{ - GST_DEBUG_CATEGORY_INIT (gst_pdraw_src_debug, "pdrawsrc", 0, - "PDrAW source debug category"); - - return gst_element_register (plugin, "pdrawsrc", GST_RANK_NONE, - GST_TYPE_PDRAW_SRC); -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, pdraw, - "Parrot PDrAW plugin", plugin_init, PACKAGE_VERSION, - PARROT_LICENSE, PACKAGE_NAME, PACKAGE_ORIGIN); +/** + * Parrot Drones Awesome Video Viewer Library + * GStreamer PDrAW source plugin + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* clang-format off */ +#include +#include +#include + +#include "gstpdrawsrc.h" + +/** Log error with errno and gstreamer object */ +#define LOG_ERRNO_OBJECT(_obj, _fct, _err) \ + GST_ERROR_OBJECT(_obj, "%s err=%d(%s)", \ + _fct, _err, g_strerror(_err)) + +#define SUPPORTED_RAW_FORMATS "{ I420, NV12 }" + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ( + "video/x-h264, " + "width = (int) [16, MAX], " + "height = (int) [16, MAX], " + "framerate = (fraction) [0/1, MAX], " + "stream-format = (string) { byte-stream, avc }" + )); + +struct _GstPdrawSrc +{ + /* parent */ + GstPushSrc parent; + + /* media added condition */ + GMutex media_lock; + GCond media_cond; + struct pdraw_media_info *media_info; + + /* setup condition, i.e. vsink started */ + GMutex setup_lock; + GCond setup_cond; + gboolean started; + + /* PDrAW elements */ + struct pdraw_backend *pdraw; + struct pdraw_demuxer *demuxer; + struct pdraw_coded_video_sink *sink; + struct mbuf_coded_video_frame_queue *queue; + + /* EOS */ + gboolean eos; + + /* Properties */ + gchar *url; + + /* helpers */ + struct mbuf_coded_video_frame *negotiation_frame; +}; + +/* Type definition */ +#define gst_pdraw_src_parent_class parent_class +G_DEFINE_TYPE (GstPdrawSrc, gst_pdraw_src, GST_TYPE_PUSH_SRC); + +/* properties */ +enum +{ + PROP_0, + PROP_URL, +}; + +/* GObject methods */ +static void gst_pdraw_src_finalize (GObject * object); +static void gst_pdraw_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_pdraw_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +/* GstBaseSrc methods */ +static gboolean gst_pdraw_src_start (GstBaseSrc * bsrc); +static gboolean gst_pdraw_src_stop (GstBaseSrc * bsrc); +static gboolean gst_pdraw_src_negotiate (GstBaseSrc * bsrc); + +/* GstPushSrc methods */ +static GstFlowReturn gst_pdraw_src_create (GstPushSrc * psrc, GstBuffer ** buf); + +/* helpers */ +static struct mbuf_coded_video_frame * get_frame_from_vsink (GstPdrawSrc * pdrawsrc); +static GstBuffer * make_codec_data (const struct pdraw_video_info * info); +static gboolean update_codec_data (GstPdrawSrc * pdrawsrc, GstCaps * caps); + +/* PDrAW callbacks */ +static void stop_resp (struct pdraw_backend * pdraw, int status, + void * userdata); +static void media_added (struct pdraw_backend * pdraw, + const struct pdraw_media_info * info, void * userdata); +static void media_removed (struct pdraw_backend * pdraw, + const struct pdraw_media_info * info, void * userdata); +static void open_resp (struct pdraw_backend * pdraw, + struct pdraw_demuxer * demuxer, int status, void * userdata); +static void close_resp (struct pdraw_backend * pdraw, + struct pdraw_demuxer * demuxer, int status, void * userdata); +static void ready_to_play (struct pdraw_backend * pdraw, + struct pdraw_demuxer * demuxer, int ready, void * userdata); +static void end_of_range (struct pdraw_backend *pdraw, + struct pdraw_demuxer * demuxer, uint64_t timestamp, void * userdata); +static void play_resp (struct pdraw_backend * pdraw, + struct pdraw_demuxer * demuxer, int status, uint64_t timestamp, + float speed, void * userdata); +static void sink_flush (struct pdraw_backend * pdraw, + struct pdraw_coded_video_sink * sink, void * userdata); + +static const struct pdraw_backend_cbs pdraw_cbs = { + .stop_resp = &stop_resp, + .media_added = &media_added, + .media_removed = &media_removed, +}; + +static const struct pdraw_backend_demuxer_cbs demuxer_cbs = { + .open_resp = &open_resp, + .close_resp = &close_resp, + .ready_to_play = &ready_to_play, + .end_of_range = &end_of_range, + .play_resp = &play_resp, +}; + +static const struct pdraw_backend_coded_video_sink_cbs vsink_cbs = { + .flush = &sink_flush, +}; + +/* helpers */ +static struct mbuf_coded_video_frame * +get_frame_from_vsink (GstPdrawSrc * pdrawsrc) +{ + struct mbuf_coded_video_frame *frame = NULL; + int res; + + g_mutex_lock (&pdrawsrc->setup_lock); + + while ((!pdrawsrc->started) || (pdrawsrc->queue == NULL)) + g_cond_wait (&pdrawsrc->setup_cond, &pdrawsrc->setup_lock); + + if (pdrawsrc->queue == NULL) + goto out; + + if (g_atomic_int_get (&pdrawsrc->eos)) + goto out; + + res = mbuf_coded_video_frame_queue_pop (pdrawsrc->queue, &frame); + if (res < 0 && res != -EAGAIN) + LOG_ERRNO_OBJECT (pdrawsrc, "mbuf_coded_video_frame_queue_pop", -res); + +out: + g_mutex_unlock (&pdrawsrc->setup_lock); + return frame; +} + +static GstBuffer * +make_codec_data (const struct pdraw_video_info * info) +{ + GstBuffer *buf = NULL; + + switch (info->format) + { + case VDEF_FRAME_TYPE_CODED: + { + gsize avc_size, sps_size, pps_size; + guint off = 0; + guint8 *data; + const guint8 *sps, *pps; + + sps_size = info->coded.h264.spslen; + pps_size = info->coded.h264.ppslen; + sps = info->coded.h264.sps; + pps = info->coded.h264.pps; + + avc_size = sps_size + pps_size + 11; + data = g_malloc0 (avc_size); + + data[off++] = 0x01; /* AVCDecoderConfiguration version 1 */ + data[off++] = sps[1]; /* profle_idc */ + data[off++] = sps[2]; /* profile_compatibility */ + data[off++] = sps[3]; /* level_idc */ + data[off++] = 0xFC | (4 - 1); /* NALU Length Size Minus One */ + + data[off++] = 0xE0 | 1; /* number of SPS NALUs */ + + /* SPS size and SPS NALU data */ + data[off++] = (sps_size >> 8) & 0xFF; + data[off++] = sps_size & 0xFF; + memcpy (data + off, sps, sps_size); + off += sps_size; + + data[off++] = 0x01; /* number of PPS NALUs */ + + /* PPS size and PPS NALU data */ + data[off++] = (pps_size >> 8) & 0xFF; + data[off++] = pps_size & 0xFF; + memcpy (data + off, pps, pps_size); + off += pps_size; + + buf = gst_buffer_new_allocate (NULL, avc_size, NULL); + if (buf) + gst_buffer_fill (buf, 0, data, avc_size); + + GST_MEMDUMP ("avc-header", data, avc_size); + g_free (data); + + break; + } + case VDEF_FRAME_TYPE_RAW: /* do nothing */ + default: + break; + } + + return buf; +} + +static gboolean +update_codec_data (GstPdrawSrc * pdrawsrc, GstCaps * caps) +{ + gboolean ret = FALSE; + const struct pdraw_video_frame *meta = NULL; + int res; + GstBuffer *codec_data_buf; + struct mbuf_ancillary_data *ancillaryData = NULL; + struct mbuf_coded_video_frame *frame = pdrawsrc->negotiation_frame; + + if (frame == NULL) { + GST_ERROR_OBJECT (pdrawsrc, "no frame to update codec data"); + goto out; + } + + res = mbuf_coded_video_frame_get_ancillary_data (frame, + PDRAW_ANCILLARY_DATA_KEY_VIDEOFRAME, &ancillaryData); + if (res < 0) { + LOG_ERRNO_OBJECT (pdrawsrc, "mbuf_coded_video_frame_get_ancillary_data", + -res); + goto out; + } + meta = mbuf_ancillary_data_get_buffer (ancillaryData, NULL); + + /* TODO: H265 support */ + if ((meta->format != VDEF_FRAME_TYPE_CODED) || + (meta->coded.format.encoding != VDEF_ENCODING_H264)) { + GST_INFO_OBJECT (pdrawsrc, "no codec data needed for video format"); + ret = TRUE; + goto out; + } + + if (meta->coded.format.data_format == VDEF_CODED_DATA_FORMAT_AVCC) + gst_caps_set_simple (caps, "stream-format", G_TYPE_STRING, + "avc", NULL); + else if (meta->coded.format.data_format == + VDEF_CODED_DATA_FORMAT_BYTE_STREAM) + gst_caps_set_simple (caps, "stream-format", G_TYPE_STRING, + "byte-stream", NULL); + + codec_data_buf = make_codec_data (&pdrawsrc->media_info->video); + if (codec_data_buf) { + gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, + codec_data_buf, NULL); + gst_buffer_unref (codec_data_buf); + } + + ret = TRUE; + +out: + if (ancillaryData) + mbuf_ancillary_data_unref (ancillaryData); + return ret; +} + +/* PDrAW callbacks */ +static void +stop_resp (struct pdraw_backend * pdraw, int status, void * userdata) +{ + GstPdrawSrc *pdrawsrc = userdata; + + if (status < 0) { + GST_ERROR_OBJECT (pdrawsrc, "%s: failed status=%d(%s)", + __func__, -status, g_strerror(-status)); + } + + /* TODO */ +} + +static void +media_added (struct pdraw_backend *pdraw, const struct pdraw_media_info *info, + void *userdata) +{ + GstPdrawSrc *pdrawsrc = userdata; + int res; + struct pdraw_video_sink_params params = { + .queue_max_count = 1, + }; + + if (info->type != PDRAW_MEDIA_TYPE_VIDEO || + info->video.format != VDEF_FRAME_TYPE_CODED || + info->video.type != PDRAW_VIDEO_TYPE_DEFAULT_CAMERA) + return; + + g_mutex_lock (&pdrawsrc->setup_lock); + + if (pdrawsrc->sink != NULL) + goto out; + + res = pdraw_be_coded_video_sink_new (pdrawsrc->pdraw, info->id, ¶ms, + &vsink_cbs, pdrawsrc, &pdrawsrc->sink); + if (res < 0) { + LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_coded_video_sink_new", -res); + goto out; + } + + /* save media info for caps negotiation */ + g_mutex_lock (&pdrawsrc->media_lock); + pdrawsrc->media_info = pdraw_media_info_dup (info); + g_cond_signal (&pdrawsrc->media_cond); + g_mutex_unlock (&pdrawsrc->media_lock); + + GST_DEBUG_OBJECT (pdrawsrc, "media added: id=%d, type=%d", + pdrawsrc->media_info->id, pdrawsrc->media_info->type); + + pdrawsrc->queue = pdraw_be_coded_video_sink_get_queue (pdrawsrc->pdraw, + pdrawsrc->sink); + if (pdrawsrc->queue == NULL) { + res = -EPROTO; + LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_coded_video_sink_get_queue", -res); + goto out; + } + +out: + g_cond_signal (&pdrawsrc->setup_cond); + g_mutex_unlock (&pdrawsrc->setup_lock); +} + +static void +media_removed (struct pdraw_backend * pdraw, + const struct pdraw_media_info * info, void * userdata) +{ + GstPdrawSrc *pdrawsrc = userdata; + int res; + + g_mutex_lock (&pdrawsrc->setup_lock); + + if (pdrawsrc->sink == NULL) + goto out; + if (pdrawsrc->media_info->id != info->id) + goto out; + + res = pdraw_be_coded_video_sink_destroy (pdrawsrc->pdraw, pdrawsrc->sink); + if (res < 0) + LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_coded_video_sink_destroy", -res); + pdrawsrc->sink = NULL; + pdrawsrc->queue = NULL; + pdraw_media_info_free (pdrawsrc->media_info); + pdrawsrc->media_info = NULL; + +out: + g_cond_signal (&pdrawsrc->setup_cond); + g_mutex_unlock (&pdrawsrc->setup_lock); +} + +static void +open_resp (struct pdraw_backend * pdraw, struct pdraw_demuxer * demuxer, + int status, void * userdata) +{ + GstPdrawSrc *pdrawsrc = userdata; + + if (status < 0) { + GST_ERROR_OBJECT (pdrawsrc, "%s: failed status=%d(%s)", + __func__, -status, g_strerror(-status)); + } + + g_mutex_lock (&pdrawsrc->setup_lock); + pdrawsrc->started = TRUE; + g_cond_signal (&pdrawsrc->setup_cond); + g_mutex_unlock (&pdrawsrc->setup_lock); +} + +static void +close_resp (struct pdraw_backend * pdraw, struct pdraw_demuxer * demuxer, + int status, void * userdata) +{ + GstPdrawSrc *pdrawsrc = userdata; + + if (status < 0) { + GST_ERROR_OBJECT (pdrawsrc, "%s: failed status=%d(%s)", + __func__, -status, g_strerror(-status)); + } + + g_mutex_lock (&pdrawsrc->setup_lock); + pdrawsrc->started = FALSE; + g_cond_signal (&pdrawsrc->setup_cond); + g_mutex_unlock (&pdrawsrc->setup_lock); +} + +static void +ready_to_play (struct pdraw_backend * pdraw, struct pdraw_demuxer * demuxer, + int ready, void * userdata) +{ + GstPdrawSrc *pdrawsrc = userdata; + int res; + + if (!ready) + return; + + res = pdraw_be_demuxer_play (pdraw, demuxer); + if (res < 0) + LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_demuxer_play", -res); +} + +static void +end_of_range (struct pdraw_backend * pdraw, struct pdraw_demuxer * demuxer, + uint64_t timestamp, void * userdata) +{ + GstPdrawSrc *pdrawsrc = userdata; + + GST_INFO_OBJECT (pdrawsrc, "end of media reached: send EOS upstream"); + g_atomic_int_set (&pdrawsrc->eos, TRUE); +} + +static void +play_resp (struct pdraw_backend *pdraw, struct pdraw_demuxer * demuxer, + int status, uint64_t timestamp, float speed, void *userdata) +{ + GstPdrawSrc *pdrawsrc = userdata; + + if (status < 0) { + GST_ERROR_OBJECT (pdrawsrc, "%s: failed status=%d(%s)", + __func__, -status, g_strerror(-status)); + } +} + +static void +sink_flush (struct pdraw_backend * pdraw, struct pdraw_coded_video_sink * sink, + void *userdata) +{ + GstPdrawSrc *pdrawsrc = userdata; + struct mbuf_coded_video_frame_queue *queue; + int res; + + queue = pdraw_be_coded_video_sink_get_queue (pdraw, sink); + if (queue == NULL) { + res = -EPROTO; + LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_coded_video_sink_get_queue", -res); + return; + } + + res = mbuf_coded_video_frame_queue_flush (queue); + if (res < 0) { + LOG_ERRNO_OBJECT (pdrawsrc, "mbuf_coded_video_frame_queue_flush", -res); + return; + } + + res = pdraw_be_coded_video_sink_queue_flushed (pdraw, sink); + if (res < 0) { + LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_coded_video_sink_queue_flushed", + -res); + return; + } +} + +static void +gst_pdraw_src_class_init (GstPdrawSrcClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass); + GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass); + + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_pdraw_src_finalize); + gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_pdraw_src_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_pdraw_src_get_property); + + gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_pdraw_src_start); + gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_pdraw_src_stop); + gstbasesrc_class->negotiate = GST_DEBUG_FUNCPTR (gst_pdraw_src_negotiate); + + gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_pdraw_src_create); + + g_object_class_install_property (gobject_class, PROP_URL, + g_param_spec_string ("url", "URL", + "Either RTSP URL or path to MP4 file", "", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_set_static_metadata (gstelement_class, + "GstPdrawSrc", + "Source/Video", + "Parrot PDrAW source", + "Parrot Drones "); + + gst_element_class_add_static_pad_template (gstelement_class, &src_template); +} + +static void +gst_pdraw_src_init (GstPdrawSrc * pdrawsrc) +{ + GstPad *srcpad = GST_BASE_SRC_PAD (pdrawsrc); + + g_mutex_init (&pdrawsrc->media_lock); + g_cond_init (&pdrawsrc->media_cond); + + g_mutex_init (&pdrawsrc->setup_lock); + g_cond_init (&pdrawsrc->setup_cond); + + gst_base_src_set_format (GST_BASE_SRC (pdrawsrc), GST_FORMAT_TIME); + gst_base_src_set_live (GST_BASE_SRC (pdrawsrc), TRUE); + + gst_pad_use_fixed_caps (srcpad); +} + +/* GObject methods */ +static void +gst_pdraw_src_finalize (GObject * object) +{ + GstPdrawSrc *pdrawsrc = GST_PDRAW_SRC (object); + int res; + + if (pdrawsrc->pdraw != NULL) { + g_mutex_lock (&pdrawsrc->setup_lock); + + while ((pdrawsrc->sink != NULL) || (pdrawsrc->started)) + g_cond_wait (&pdrawsrc->setup_cond, &pdrawsrc->setup_lock); + + g_mutex_unlock (&pdrawsrc->setup_lock); + + res = pdraw_be_destroy (pdrawsrc->pdraw); + if (res < 0) + LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_destroy", -res); + pdrawsrc->pdraw = NULL; + pdrawsrc->sink = NULL; + pdrawsrc->queue = NULL; + g_slice_free (struct pdraw_media_info, (void *) pdrawsrc->media_info); + } + + g_mutex_clear (&pdrawsrc->setup_lock); + g_cond_clear (&pdrawsrc->setup_cond); + + g_mutex_clear (&pdrawsrc->media_lock); + g_cond_clear (&pdrawsrc->media_cond); + + g_free (pdrawsrc->url); + pdrawsrc->url = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_pdraw_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstPdrawSrc *pdrawsrc = GST_PDRAW_SRC (object); + + switch (prop_id) { + case PROP_URL: + g_free (pdrawsrc->url); + pdrawsrc->url = g_value_dup_string (value); + /* TODO: need to reconfigure in case this property changed in + * PLAYING state */ + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (pdrawsrc, prop_id, pspec); + break; + } +} + +static void +gst_pdraw_src_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstPdrawSrc *pdrawsrc = GST_PDRAW_SRC (object); + + switch (prop_id) { + case PROP_URL: + g_value_set_string (value, pdrawsrc->url); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (pdrawsrc, prop_id, pspec); + break; + } +} + +/* GstBaseSrc methods */ +static gboolean +gst_pdraw_src_start (GstBaseSrc * bsrc) +{ + GstPdrawSrc *pdrawsrc = GST_PDRAW_SRC (bsrc); + gboolean ret = FALSE; + int res; + + if (pdrawsrc->pdraw == NULL) { + res = pdraw_be_new (&pdraw_cbs, pdrawsrc, &pdrawsrc->pdraw); + if (res < 0) { + LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_new", -res); + goto out; + } + } + + res = pdraw_be_set_pipeline_mode_setting (pdrawsrc->pdraw, + PDRAW_PIPELINE_MODE_DECODE_NONE); + if (res < 0) { + LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_set_pipeline_mode_setting", -res); + goto out; + } + + res = pdraw_be_demuxer_new_from_url (pdrawsrc->pdraw, pdrawsrc->url, + &demuxer_cbs, pdrawsrc, &pdrawsrc->demuxer); + if (res < 0) { + LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_open_url", -res); + goto out; + } + + ret = TRUE; + +out: + return ret; +} + +static gboolean +gst_pdraw_src_stop (GstBaseSrc * bsrc) +{ + GstPdrawSrc *pdrawsrc = GST_PDRAW_SRC (bsrc); + int res; + + if (pdrawsrc->pdraw == NULL) + return TRUE; + + res = pdraw_be_demuxer_close (pdrawsrc->pdraw, pdrawsrc->demuxer); + if (res < 0) { + LOG_ERRNO_OBJECT (pdrawsrc, "pdraw_be_demuxer_close", -res); + return FALSE; + } + + return TRUE; +} + +static gboolean +gst_pdraw_src_negotiate (GstBaseSrc * bsrc) +{ + GstPdrawSrc *pdrawsrc = GST_PDRAW_SRC (bsrc); + const struct pdraw_video_info *video_info; + gboolean ret = FALSE; + GstCaps *allowed_caps, *desired_caps = NULL; + GstCaps *our_caps = NULL, *peer_caps = NULL; + + allowed_caps = gst_pad_get_allowed_caps (GST_BASE_SRC_PAD (bsrc)); + GST_DEBUG_OBJECT (pdrawsrc, "allowed caps: %" GST_PTR_FORMAT, allowed_caps); + + if (gst_caps_is_empty (allowed_caps)) { + GST_ERROR_OBJECT (pdrawsrc, "empty caps"); + gst_caps_unref (allowed_caps); + goto out; + } + + g_mutex_lock (&pdrawsrc->media_lock); + GST_DEBUG_OBJECT (pdrawsrc, "waiting for media to be added"); + while (pdrawsrc->media_info == NULL) + g_cond_wait (&pdrawsrc->media_cond, &pdrawsrc->media_lock); + g_mutex_unlock (&pdrawsrc->media_lock); + + + video_info = &pdrawsrc->media_info->video; + + /* get a first frame and store it to be used by _create later */ + pdrawsrc->negotiation_frame = get_frame_from_vsink (pdrawsrc); + if (pdrawsrc->negotiation_frame == NULL) { + GST_ERROR_OBJECT (pdrawsrc, "failed to get first frame for negotiation"); + goto out; + } + + switch (video_info->format) + { + case VDEF_FRAME_TYPE_CODED: + /* TODO: support H.265 */ + desired_caps = gst_caps_new_simple ("video/x-h264", + "width", G_TYPE_INT, video_info->coded.info.resolution.width, + "height", G_TYPE_INT, video_info->coded.info.resolution.height, + NULL); + update_codec_data (pdrawsrc, desired_caps); + break; + case VDEF_FRAME_TYPE_RAW: + GST_FIXME_OBJECT (pdrawsrc, "handle YUV format"); + goto out; + default: + GST_WARNING_OBJECT (pdrawsrc, "cannot handle this video format: %d", + video_info->format); + goto out; + } + + if (!gst_caps_can_intersect (desired_caps, allowed_caps)) { + GST_ERROR_OBJECT (pdrawsrc, "cannot find common caps with upstream"); + goto out; + } + + our_caps = gst_caps_intersect (desired_caps, allowed_caps); + gst_caps_unref (desired_caps); + gst_caps_unref (allowed_caps); + + our_caps = gst_caps_fixate (our_caps); + GST_DEBUG_OBJECT (pdrawsrc, "desired caps %" GST_PTR_FORMAT, our_caps); + + /* get the peer pad caps filtered out with our caps */ + peer_caps = gst_pad_peer_query_caps (GST_BASE_SRC_PAD (bsrc), our_caps); + GST_DEBUG_OBJECT (pdrawsrc, "filtered caps of peer: %" GST_PTR_FORMAT, + peer_caps); + + if (!gst_caps_is_fixed (peer_caps)) { + GST_ERROR_OBJECT (pdrawsrc, "peer caps not fixed: %" GST_PTR_FORMAT, + peer_caps); + goto out; + } + + ret = gst_base_src_set_caps (bsrc, peer_caps); + +out: + if (peer_caps) + gst_caps_unref (peer_caps); + if (our_caps) + gst_caps_unref (our_caps); + return ret; +} + +static void +out_mem_unref (gpointer data) +{ + (void) mbuf_coded_video_frame_unref ((struct mbuf_coded_video_frame *) data); +} + +/* GstPushSrc methods */ +static GstFlowReturn +gst_pdraw_src_create (GstPushSrc * psrc, GstBuffer ** buf) +{ + GstPdrawSrc *pdrawsrc = GST_PDRAW_SRC (psrc); + struct mbuf_coded_video_frame *frame = NULL; + struct mbuf_ancillary_data *ancillarydata = NULL; + const struct pdraw_video_frame *meta = NULL; + const void *data; + struct vdef_coded_frame frameinfo; + size_t size; + gssize gsize; + GstBuffer *newbuf = NULL; + int res; + gboolean silent = FALSE; + + /* loop until a non silent frame can be produced */ + do + { + if (pdrawsrc->negotiation_frame) { + frame = pdrawsrc->negotiation_frame; + pdrawsrc->negotiation_frame = NULL; + } else { + frame = get_frame_from_vsink (pdrawsrc); + } + + if (frame == NULL) { + if (G_UNLIKELY (g_atomic_int_compare_and_exchange (&pdrawsrc->eos, TRUE, + FALSE))) { + goto eos; + } else { + goto error; + } + } + + res = mbuf_coded_video_frame_get_frame_info (frame, &frameinfo); + if (res < 0) { + LOG_ERRNO_OBJECT (pdrawsrc, "mbuf_coded_video_frame_get_frame_info", + -res); + goto error; + } + + res = mbuf_coded_video_frame_get_packed_buffer (frame, &data, &size); + if (res == 0) { + /* Frame is already packed */ + mbuf_coded_video_frame_ref (frame); + } else if (res == -EPROTO) { + /* Frame is not packed, copy & pack it */ + struct mbuf_mem *mem; + struct mbuf_coded_video_frame *new_frame; + res = mbuf_mem_generic_new (size, &mem); + if (res < 0) { + LOG_ERRNO_OBJECT (pdrawsrc, "mbuf_mem_generic_new", -res); + goto error; + } + res = mbuf_coded_video_frame_copy (frame, mem, &new_frame); + if (res < 0) { + LOG_ERRNO_OBJECT (pdrawsrc, "mbuf_coded_video_frame_copy", -res); + goto error; + } + mbuf_coded_video_frame_unref (frame); + frame = new_frame; + res = mbuf_coded_video_frame_get_packed_buffer (frame, &data, &size); + if (res < 0) { + LOG_ERRNO_OBJECT (pdrawsrc, "mbuf_coded_video_frame_get_packed_buffer", + -res); + goto error; + } + } else { + LOG_ERRNO_OBJECT (pdrawsrc, "mbuf_coded_video_frame_get_packed_buffer", + -res); + goto error; + } + gsize = size; + + res = mbuf_coded_video_frame_get_ancillary_data (frame, + PDRAW_ANCILLARY_DATA_KEY_VIDEOFRAME, &ancillarydata); + if (res < 0) { + LOG_ERRNO_OBJECT (pdrawsrc, "mbuf_coded_video_frame_get_ancillary_data", -res); + goto error; + } + meta = mbuf_ancillary_data_get_buffer (ancillarydata, NULL); + + switch (meta->format) + { + case VDEF_FRAME_TYPE_CODED: + GST_DEBUG_OBJECT (pdrawsrc, + "coded video format=" VDEF_CODED_FORMAT_TO_STR_FMT + ", is_silent=%d, is_sync=%d, is_ref=%d, size=%zu, PTS=%lu", + VDEF_CODED_FORMAT_TO_STR_ARG (&meta->coded.format), + !!(meta->coded.info.flags & VDEF_FRAME_FLAG_SILENT), + meta->is_sync, + meta->is_ref, + size, + meta->ntp_raw_timestamp); + break; + case VDEF_FRAME_TYPE_RAW: + GST_FIXME_OBJECT (pdrawsrc, "handle raw formats"); + break; + default: + break; + } + + silent = (meta->coded.info.flags & VDEF_FRAME_FLAG_SILENT); + if (silent) { + mbuf_ancillary_data_unref (ancillarydata); + ancillarydata = NULL; + mbuf_coded_video_frame_release_packed_buffer(frame, data); + mbuf_coded_video_frame_unref(frame); + } + } while (silent); + if (ancillarydata) { + mbuf_ancillary_data_unref (ancillarydata); + ancillarydata = NULL; + } + + newbuf = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + (gpointer) data, gsize, 0, gsize, frame, out_mem_unref); + if (newbuf == NULL) { + res = -ENOMEM; + LOG_ERRNO_OBJECT (pdrawsrc, "gst_buffer_new_wrapped_full", -res); + goto error; + } + + /* Release the packed buffer here, since gstreamer will only unref the frame. + * This is sketchy, because it means releasing the read-lock, allowing another + * thread to modify the frame while it is being decoded, but we're the only + * consumer in this case, so we can do it. + */ + res = mbuf_coded_video_frame_release_packed_buffer(frame, data); + data = NULL; + + GST_BUFFER_PTS (newbuf) = meta->ntp_raw_timestamp * 1000; /* to nanoseconds */ + + *buf = newbuf; + return GST_FLOW_OK; + +eos: + g_assert (frame == NULL && newbuf == NULL); + return GST_FLOW_EOS; + +error: + if (ancillarydata) + mbuf_ancillary_data_unref (ancillarydata); + if (frame && data) + mbuf_coded_video_frame_release_packed_buffer (frame, data); + if (frame) + mbuf_coded_video_frame_unref (frame); + return GST_FLOW_ERROR; +} + +/* plugin init */ +#define PACKAGE "gst-pdraw" +#define PACKAGE_VERSION "1.0.0" +#define PARROT_LICENSE "Proprietary" +#define PACKAGE_NAME "GstPdraw" +#define PACKAGE_ORIGIN "http://www.parrot.com" + +GST_DEBUG_CATEGORY (gst_pdraw_src_debug); +#define GST_CAT_DEFAULT gst_pdraw_src_debug + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (gst_pdraw_src_debug, "pdrawsrc", 0, + "PDrAW source debug category"); + + return gst_element_register (plugin, "pdrawsrc", GST_RANK_NONE, + GST_TYPE_PDRAW_SRC); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, pdraw, + "Parrot PDrAW plugin", plugin_init, PACKAGE_VERSION, + PARROT_LICENSE, PACKAGE_NAME, PACKAGE_ORIGIN); diff --git a/gst-pdraw/gstpdrawsrc.h b/gst-pdraw/gstpdrawsrc.h index 0685cff..d328f74 100644 --- a/gst-pdraw/gstpdrawsrc.h +++ b/gst-pdraw/gstpdrawsrc.h @@ -1,52 +1,52 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * GStreamer PDrAW source plugin - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _GSTPDRAWSRC_H_ -#define _GSTPDRAWSRC_H_ - -/* clang-format off */ -#include -#include - -#include -#include -#include -#include - -G_BEGIN_DECLS - -#define GST_TYPE_PDRAW_SRC (gst_pdraw_src_get_type ()) - -G_DECLARE_FINAL_TYPE (GstPdrawSrc, gst_pdraw_src, GST, PDRAW_SRC, - GstPushSrc) - -G_END_DECLS - -#endif /* !_GSTPDRAWSRC_H_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * GStreamer PDrAW source plugin + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _GSTPDRAWSRC_H_ +#define _GSTPDRAWSRC_H_ + +/* clang-format off */ +#include +#include + +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_PDRAW_SRC (gst_pdraw_src_get_type ()) + +G_DECLARE_FINAL_TYPE (GstPdrawSrc, gst_pdraw_src, GST, PDRAW_SRC, + GstPushSrc) + +G_END_DECLS + +#endif /* !_GSTPDRAWSRC_H_ */ diff --git a/libpdraw-backend/atom.mk b/libpdraw-backend/atom.mk index 4ed9010..e6582a1 100644 --- a/libpdraw-backend/atom.mk +++ b/libpdraw-backend/atom.mk @@ -1,35 +1,35 @@ - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := libpdraw-backend -LOCAL_CATEGORY_PATH := libs -LOCAL_DESCRIPTION := Parrot Drones Awesome Video Viewer back-end library -LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include -LOCAL_CFLAGS := -DPDRAW_BACKEND_API_EXPORTS -fvisibility=hidden -std=gnu99 -LOCAL_SRC_FILES := \ - src/pdraw_backend_impl.cpp \ - src/pdraw_backend_wrapper.cpp -LOCAL_LIBRARIES := \ - libfutils \ - libpdraw \ - libpomp \ - libulog - -include $(BUILD_LIBRARY) - - -include $(CLEAR_VARS) - -LOCAL_MODULE := pdraw-backend-test -LOCAL_CATEGORY_PATH := multimedia -LOCAL_DESCRIPTION := Parrot Drones Awesome Video Viewer back-end library test program -LOCAL_SRC_FILES := \ - tests/pdraw_backend_test.c -LOCAL_LIBRARIES := \ - libpdraw \ - libpdraw-backend \ - libulog - -include $(BUILD_EXECUTABLE) + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libpdraw-backend +LOCAL_CATEGORY_PATH := libs +LOCAL_DESCRIPTION := Parrot Drones Awesome Video Viewer back-end library +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_CFLAGS := -DPDRAW_BACKEND_API_EXPORTS -fvisibility=hidden -std=gnu99 +LOCAL_SRC_FILES := \ + src/pdraw_backend_impl.cpp \ + src/pdraw_backend_wrapper.cpp +LOCAL_LIBRARIES := \ + libfutils \ + libpdraw \ + libpomp \ + libulog + +include $(BUILD_LIBRARY) + + +include $(CLEAR_VARS) + +LOCAL_MODULE := pdraw-backend-test +LOCAL_CATEGORY_PATH := multimedia +LOCAL_DESCRIPTION := Parrot Drones Awesome Video Viewer back-end library test program +LOCAL_SRC_FILES := \ + tests/pdraw_backend_test.c +LOCAL_LIBRARIES := \ + libpdraw \ + libpdraw-backend \ + libulog + +include $(BUILD_EXECUTABLE) diff --git a/libpdraw-backend/include/pdraw/pdraw_backend.h b/libpdraw-backend/include/pdraw/pdraw_backend.h index 6713b6b..08ddc28 100644 --- a/libpdraw-backend/include/pdraw/pdraw_backend.h +++ b/libpdraw-backend/include/pdraw/pdraw_backend.h @@ -1,1628 +1,1628 @@ -/** - * Parrot Drones Awesome Video Viewer - * PDrAW back-end library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_BACKEND_H_ -#define _PDRAW_BACKEND_H_ - -#include - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* To be used for all public API */ -#ifdef PDRAW_BACKEND_API_EXPORTS -# ifdef _WIN32 -# define PDRAW_BACKEND_API __declspec(dllexport) -# else /* !_WIN32 */ -# define PDRAW_BACKEND_API __attribute__((visibility("default"))) -# endif /* !_WIN32 */ -#else /* !PDRAW_BACKEND_API_EXPORTS */ -# define PDRAW_BACKEND_API -#endif /* !PDRAW_BACKEND_API_EXPORTS */ - - -/* Forward declarations */ -struct pdraw_backend; - - -/* General callback functions */ -struct pdraw_backend_cbs { - /* Stop response callback function, called when a stop operation is - * complete or has failed (optional, can be null, but highly recommended - * for correct PDrAW session management). - * The status parameter is the stop operation status: 0 on success, - * or a negative errno value in case of error. - * @param pdraw: PDrAW back-end instance handle - * @param status: 0 on success, negative errno value in case of error - * @param userdata: user data pointer */ - void (*stop_resp)(struct pdraw_backend *pdraw, - int status, - void *userdata); - - /* Media added callback function, called when a media has been added - * internally in the PDrAW pipeline. Medias are for example raw or coded - * video medias. The info structure gives the media identifier that can - * be used for example to create a video sink on this media. - * @param pdraw: PDrAW back-end instance handle - * @param info: pointer on the media information - * @param userdata: user data pointer */ - void (*media_added)(struct pdraw_backend *pdraw, - const struct pdraw_media_info *info, - void *userdata); - - /* Media removed callback function, called when a media has been removed - * internally from the PDrAW pipeline. Medias are for example raw or - * coded video medias. When a media is removed, any video sink created - * on this media must then be stopped. - * @param pdraw: PDrAW back-end instance handle - * @param info: pointer on the media information - * @param userdata: user data pointer */ - void (*media_removed)(struct pdraw_backend *pdraw, - const struct pdraw_media_info *info, - void *userdata); - - /* Socket creation callback function, called immediately after a - * socket creation with its file descriptor as parameter (optional, - * can be null). - * @param pdraw: PDrAW back-end instance handle - * @param fd: socket file descriptor - * @param userdata: user data pointer */ - void (*socket_created)(struct pdraw_backend *pdraw, - int fd, - void *userdata); -}; - - -/* Demuxer callback functions */ -struct pdraw_backend_demuxer_cbs { - /* Open response callback function, called when an open operation is - * complete or has failed (optional, can be null, but highly recommended - * for correct PDrAW session management). - * The status parameter is the open operation status: 0 on success, - * or a negative errno value in case of error. - * If this function reports an error, the demuxer still needs to be - * closed: pdraw_be_demuxer_close() must be called and one must wait for - * the close_resp callback function to be issued prior to calling - * pdraw_be_demuxer_destroy(). - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @param status: 0 on success, negative errno value in case of error - * @param userdata: user data pointer */ - void (*open_resp)(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - int status, - void *userdata); - - /* Close response callback function, called when a close operation is - * complete or has failed (optional, can be null, but highly recommended - * for correct PDrAW session management). - * The status parameter is the close operation status: 0 on success, - * or a negative errno value in case of error. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @param status: 0 on success, negative errno value in case of error - * @param userdata: user data pointer */ - void (*close_resp)(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - int status, - void *userdata); - - /* Unrecoverable error callback function, called when a previously - * opened session is no longer running. When this function is called, - * the demuxer is no longer running; pdraw_be_demuxer_close() must be - * called and one must wait for the close_resp callback function to be - * issued prior to calling pdraw_be_demuxer_destroy(). - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @param userdata: user data pointer */ - void (*unrecoverable_error)(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - void *userdata); - - /* Demuxer media selection callback function, called with a list of - * video medias found from which the application must choose one or more - * to process in the pipeline. The return value of the callback function - * must be a bitfield of the identifiers of the chosen medias (from the - * pdraw_demuxer_media structure), or 0 to choose the default medias. If - * the return value is -ENOSYS, the callback is considered not - * implemented and the default medias are chosen. If the return value is - * -ECANCELED no media is chosen and the open operation is aborted. If - * the return value is another negative errno or an invalid bitfield the - * open_resp callback function will be called if an open operation is in - * progress, or the unrecoverable_error callback function otherwise. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @param medias: array of demuxer media - * @param count: demuxer media array element count - * @param userdata: user data pointer - * @return a bitfield of the identifiers of the chosen medias, - * 0 or -ENOSYS to choose the default medias, - * -ECANCELED to choose no media and abort the open operation, - * or another negative errno value in case of error */ - int (*select_media)(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - const struct pdraw_demuxer_media *medias, - size_t count, - void *userdata); - - /* Ready to play callback function, called when the playback is ready - * to start (optional, can be null). This function is called to indicate - * that the PDrAW session is ready to process play operations. - * Generally, the session is ready to play as soon as the open_resp - * callback function has been called with a success status. One case - * when the open operation was successful and the playback is not ready - * is when connected to a SkyController's RTSP server but the - * SkyController itself is not yet connected to a drone. Similarly, - * when connected to a drone's stream through a SkyController's RTSP - * server, if the drone is disconnected from the SkyController, this - * function will be called with a 0 value in the ready parameter. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @param ready: 1 if the session is ready to play, 0 otherwise - * @param userdata: user data pointer */ - void (*ready_to_play)(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - int ready, - void *userdata); - - /* End of range callback function, called when the playback is suspended - * after having reached the end of the playback duration (optional, - * can be null). This function is only called for replays (either local - * or streamed), not for live streams. The timestamp parameter is the - * current play time in microseconds at the moment the playback is - * suspended. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @param timestamp: current playback time in microseconds - * @param userdata: user data pointer */ - void (*end_of_range)(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - uint64_t timestamp, - void *userdata); - - /* Play response callback function, called when a play operation is - * complete (the playback has started) or has failed (optional, can be - * null). The status parameter is the play operation status: 0 on - * success, or a negative errno value in case of error. The timestamp - * parameter is the current play time in microseconds at the moment the - * playback is started. The speed parameter is the current playback - * speed; a negative value means playing backward. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @param status: 0 on success, negative errno value in case of error - * @param timestamp: current playback time in microseconds - * @param speed: current playback speed, negative means backward - * @param userdata: user data pointer */ - void (*play_resp)(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - int status, - uint64_t timestamp, - float speed, - void *userdata); - - /* Pause response callback function, called when a pause operation is - * complete (the playback is suspended) or has failed (optional, can be - * null). The status parameter is the pause operation status: 0 on - * success, or a negative errno value in case of error. The timestamp - * parameter is the current play time in microseconds at the moment the - * playback is paused. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @param status: 0 on success, negative errno value in case of error - * @param timestamp: current playback time in microseconds - * @param userdata: user data pointer */ - void (*pause_resp)(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - int status, - uint64_t timestamp, - void *userdata); - - /* Seek response callback function, called when a seek operation is - * complete or has failed (optional, can be null). - * The status parameter is the seek operation status: 0 on success, - * or a negative errno value in case of error. The timestamp parameter - * is the current play time in microseconds after seeking. The speed - * parameter is the current playback speed; a negative value means - * playing backward. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @param status: 0 on success, negative errno value in case of error - * @param timestamp: current playback time in microseconds - * @param speed: current playback speed, negative means backward - * @param userdata: user data pointer */ - void (*seek_resp)(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - int status, - uint64_t timestamp, - float speed, - void *userdata); -}; - - -/* Video renderer callback functions */ -struct pdraw_backend_video_renderer_cbs { - /* Media added callback function, called when a media has been added - * internally to the renderer. Medias are raw video medias. - * This function is called from the internal pomp_loop thread. - * @param pdraw: PDrAW back-end instance handle - * @param renderer: renderer handle - * @param info: pointer on the media information - * @param userdata: user data pointer */ - void (*media_added)(struct pdraw_backend *pdraw, - struct pdraw_video_renderer *renderer, - const struct pdraw_media_info *info, - void *userdata); - - /* Media removed callback function, called when a media has been removed - * internally from the renderer. Medias are raw video medias. - * This function is called from the internal pomp_loop thread. - * @param pdraw: PDrAW back-end instance handle - * @param renderer: renderer handle - * @param info: pointer on the media information - * @param userdata: user data pointer */ - void (*media_removed)(struct pdraw_backend *pdraw, - struct pdraw_video_renderer *renderer, - const struct pdraw_media_info *info, - void *userdata); - - /* Render ready callback function, called both when a new frame is - * ready for rendering and periodically (optional, can be null). - * This function is called from the internal pomp_loop thread. - * @param pdraw: PDrAW back-end instance handle - * @param renderer: renderer handle - * @param userdata: user data pointer */ - void (*render_ready)(struct pdraw_backend *pdraw, - struct pdraw_video_renderer *renderer, - void *userdata); - - /* External texture loading callback function (optional, can be null). - * This function is called before the rendering of the video frame in - * order to override the frame loading as a texture. This can be used - * to transform the video frames outside of PDrAW before resuming the - * rendering. This function is called from the rendering thread. - * @param pdraw: PDrAW back-end instance handle - * @param renderer: renderer handle - * @param texture_width: texture width in pixels - * @param texture_height: texture height in pixels - * @param media_info: media information - * @param frame: frame information - * @param frame_userdata: frame user data buffer - * @param frame_userdata_len: frame user data buffer size in bytes - * @param userdata: user data pointer - * @return 0 on success, negative errno value in case of error */ - int (*load_texture)(struct pdraw_backend *pdraw, - struct pdraw_video_renderer *renderer, - unsigned int texture_width, - unsigned int texture_height, - const struct pdraw_media_info *media_info, - struct mbuf_raw_video_frame *frame, - const void *frame_userdata, - size_t frame_userdata_len, - void *userdata); - - /* Overlay rendering callback function (optional, can be null). - * This function is called after the rendering of the video frame - * (if one is available) in order to render an application overlay - * on top of the video. When HMD distorsion correction is enabled - * in the renderer, it is applied after the overlay rendering. - * This function is called from the rendering thread. - * When no frame is available for the rendering, the frame_meta - * and frame_extra parameters are NULL. - * @param pdraw: PDrAW back-end instance handle - * @param renderer: renderer handle - * @param render_pos: rendering position - * @param content_pos: video content position - * @param view_mat: 4x4 view matrix - * @param proj_mat: 4x4 projection matrix - * @param media_info: media information - * @param frame_meta: frame metadata (optional, can be NULL) - * @param frame_extra: frame extra information (optional, can be NULL) - * @param userdata: user data pointer */ - void (*render_overlay)( - struct pdraw_backend *pdraw, - struct pdraw_video_renderer *renderer, - const struct pdraw_rect *render_pos, - const struct pdraw_rect *content_pos, - const float *view_mat, - const float *proj_mat, - const struct pdraw_media_info *media_info, - struct vmeta_frame *frame_meta, - const struct pdraw_video_frame_extra *frame_extra, - void *userdata); -}; - - -/* Coded video sink callback functions */ -struct pdraw_backend_coded_video_sink_cbs { - /* Video sink flush callback function, called when flushing is required - * (mandatory). When this function is called, the application must flush - * the sink queue by calling mbuf_coded_video_frame_queue_flush() and - * must return all frames outside of the queue by calling - * mbuf_coded_video_frame_unref(); once the flushing is done, the - * pdraw_be_coded_video_sink_queue_flushed() function must be called. - * @param pdraw: PDrAW back-end instance handle - * @param sink: video sink handle - * @param userdata: user data pointer */ - void (*flush)(struct pdraw_backend *pdraw, - struct pdraw_coded_video_sink *sink, - void *userdata); -}; - - -/* Video sink callback functions */ -struct pdraw_backend_raw_video_sink_cbs { - /* Video sink flush callback function, called when flushing is required - * (mandatory). When this function is called, the application must flush - * the sink queue by calling mbuf_raw_video_frame_queue_flush() and - * must return all frames outside of the queue by calling - * mbuf_raw_video_frame_unref(); once the flushing is done, the - * pdraw_be_raw_video_sink_queue_flushed() function must be called. - * @param pdraw: PDrAW back-end instance handle - * @param sink: video sink handle - * @param userdata: user data pointer */ - void (*flush)(struct pdraw_backend *pdraw, - struct pdraw_raw_video_sink *sink, - void *userdata); -}; - - -/** - * Instance management API - */ - -/** - * Create a PDrAW back-end instance. - * All API functions can be called from any thread, with the exception of - * rendering functions which must still be called from the rendering thread. - * All callback functions with the exception of video renderer callback - * functions are called from the internal pomp_loop thread; synchronization - * is up to the application. - * The callbacks structure must be provided but all callback functions are - * optional. However, for correct management of the session it is highly - * recommended to implement at least the open_resp and close_resp functions. - * The instance handle is returned through the ret_obj parameter. When no - * longer needed, the instance must be freed using the pdraw_be_destroy() - * function. The pdraw_be_stop() function must be called and one must wait - * for the stop_resp callback function to be issued prior to calling - * pdraw_be_destroy(). - * @param cbs: back-end instance callback functions - * @param userdata: callback functions user data pointer - * @param ret_obj: PDrAW back-end instance handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int pdraw_be_new(const struct pdraw_backend_cbs *cbs, - void *userdata, - struct pdraw_backend **ret_obj); - - -/** - * Free a PDrAW back-end instance. - * This function frees all resources associated with a back-end instance. - * If a successful pdraw_be_open_*() was made, the pdraw_be_close() function - * must be called and one must wait for the close_resp callback function to - * be issued prior to calling pdraw_be_destroy(). - * @param self: PDrAW back-end instance handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int pdraw_be_destroy(struct pdraw_backend *self); - - -/** - * Stop a PDrAW back-end instance. - * This function stops a PDrAW back-end instance and all the associated objects. - * The function returns before the actual stopping is done. If the function - * returns 0, the stop_resp callback function will be called once the stopping - * is successful (0 status) or has failed (negative errno status). If the - * function returns a negative errno value (immediate failure), the stop_resp - * callback function will not be called. After a successful stop, the PDrAW - * instance must be destroyed by calling pdraw_destroy(). - * @param self: PDrAW back-end instance handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int pdraw_be_stop(struct pdraw_backend *self); - - -/** - * Get the PDrAW back-end internal event loop. - * This function returns a pointer to the event loop used internally. - * @param self: PDrAW back-end instance handle - * @return a pointer on the internal loop on success, NULL in case of error - */ -PDRAW_BACKEND_API struct pomp_loop * -pdraw_be_get_loop(struct pdraw_backend *self); - - -/** - * Demuxer API - */ - -/** - * Create a demuxer on a URL (stream or local file). - * The URL can be either an RTSP URL (starting with "rtsp://") or a local file - * path (either absolute or relative). - * The function returns before the actual opening is done. If the function - * returns 0, the open_resp callback function will be issued once the open - * operation is successful (0 status) or has failed (negative errno status). - * If the function returns a negative errno value (immediate failure), the - * open_resp callback function will not be issued. - * Once a demuxer is no longer used, it must be closed and then destroyed - * (@see the pdraw_be_demuxer_close() function). - * @param pdraw: PDrAW back-end instance handle - * @param url: URL of the resource to open - * @param cbs: demuxer callback functions - * @param userdata: callback functions user data (optional, can be null) - * @param ret_obj: demuxer handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_demuxer_new_from_url(struct pdraw_backend *self, - const char *url, - const struct pdraw_backend_demuxer_cbs *cbs, - void *userdata, - struct pdraw_demuxer **ret_obj); - - -/** - * Create a demuxer on a single stream. - * This function opens an RTP/AVP stream. No session management is done: it - * is the application's responsibility to handle the ports negociation with - * the sender. If null local ports are given as parameter, the effective port - * numbers used can be retrieved using the - * pdraw_be_get_single_stream_local_stream_port() for the RTP port and - * pdraw_be_get_single_stream_local_control_port() for the RTCP functions. - * If the local_addr parameter is left null, any local network interface will - * be used. The remote_addr, remote_stream_port and remote_control_port - * parameters can be left null if unknown; they will be known once the stream - * is being received. - * The function returns before the actual opening is done. If the function - * returns 0, the open_resp callback function will be issued once the open - * operation is successful (0 status) or has failed (negative errno status). - * If the function returns a negative errno value (immediate failure), the - * open_resp callback function will not be issued. - * Once a demuxer is no longer used, it must be closed and then destroyed - * (@see the pdraw_be_demuxer_close() function). - * @param pdraw: PDrAW back-end instance handle - * @param local_addr: local IP address (optional, can be NULL) - * @param local_stream_port: local stream (RTP) port (optional, can be 0) - * @param local_control_port: local control (RTCP) port (optional, can be 0) - * @param remote_addr: remote IP address (optional, can be NULL) - * @param remote_stream_port: remote stream (RTP) port (optional, can be 0) - * @param remote_control_port: remote control (RTCP) port (optional, can be 0) - * @param cbs: demuxer callback functions - * @param userdata: callback functions user data (optional, can be null) - * @param ret_obj: demuxer handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_demuxer_new_single_stream(struct pdraw_backend *self, - const char *local_addr, - uint16_t local_stream_port, - uint16_t local_control_port, - const char *remote_addr, - uint16_t remote_stream_port, - uint16_t remote_control_port, - const struct pdraw_backend_demuxer_cbs *cbs, - void *userdata, - struct pdraw_demuxer **ret_obj); - - -/** - * Create a demuxer on a stream URL through a mux channel. - * The URL must be an RTSP URL (starting with "rtsp://"). Mux channels are used - * to transfer data between a SkyController remote and a smartphone through USB; - * see Parrot's libmux for more information. - * No concurrent sessions can run on the mux channel; therefore the user must - * take care of limiting the number of PDrAW instances and demuxer objects - * running on the mux channel to only one. - * The function returns before the actual opening is done. If the function - * returns 0, the open_resp callback function will be issued once the open - * operation is successful (0 status) or has failed (negative errno status). - * If the function returns a negative errno value (immediate failure), the - * open_resp callback function will not be issued. - * Once a demuxer is no longer used, it must be closed and then destroyed - * (@see the pdraw_be_demuxer_close() function). - * @param pdraw: PDrAW back-end instance handle - * @param url: URL of the resource to open - * @param mux: mux instance handle - * @param cbs: demuxer callback functions - * @param userdata: callback functions user data (optional, can be null) - * @param ret_obj: demuxer handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int pdraw_be_demuxer_new_from_url_on_mux( - struct pdraw_backend *self, - const char *url, - struct mux_ctx *mux, - const struct pdraw_backend_demuxer_cbs *cbs, - void *userdata, - struct pdraw_demuxer **ret_obj); - - -/** - * Destroy a demuxer. - * This function stops a running demuxer and frees the associated resources. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int pdraw_be_demuxer_destroy(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer); - - -/** - * Close a demuxer. - * This function closes a previously opened demuxer (either record or stream). - * The function returns before the actual closing is done. If the function - * returns 0, the close_resp callback function will be issued once the close is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the close_resp callback - * function will not be issued. After a successful close, the demuxer must be - * destroyed by calling pdraw_be_demuxer_destroy(). - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int pdraw_be_demuxer_close(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer); - - -/** - * Get the single stream local stream port. - * This function returns the local stream (RTP) port currently in use after - * a succesful open operation on a single stream (RTP/AVP) or an RTSP URL. - * If no open operation has been done, or if an open operation has been done - * on a mux channel, 0 is returned. - * This is useful when calling pdraw_be_open_single_stream() with null local - * ports to let PDrAW open sockets on any available port. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @return the stream port on success, 0 in case of error - */ -PDRAW_BACKEND_API uint16_t pdraw_be_demuxer_get_single_stream_local_stream_port( - struct pdraw_backend *self, - struct pdraw_demuxer *demuxer); - - -/** - * Get the single stream local control port. - * This function returns the local control (RTCP) port currently in use after - * a succesful open operation on a single stream (RTP/AVP) or an RTSP URL. - * If no open operation has been done, or if an open operation has been done - * on a mux channel, 0 is returned. - * This is useful when calling pdraw_be_open_single_stream() with null local - * ports to let PDrAW open sockets on any available port. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @return the stream port on success, 0 in case of error - */ -PDRAW_BACKEND_API uint16_t -pdraw_be_demuxer_get_single_stream_local_control_port( - struct pdraw_backend *self, - struct pdraw_demuxer *demuxer); - - -/** - * Get the ready to play status. - * This function returns 1 if a successful open operation has been completed - * and if the playback is ready to start, 0 otherwise. One case when a - * successful open operation is complete but the playback is not ready is when - * connected to a SkyController's RTSP server but the SkyController itself is - * not yet connected to a drone. - * The value returned by this function is identical to the ready parameter - * passed to the ready_to_play callback function when it is issued. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @return the ready to play status on success, 0 in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_demuxer_is_ready_to_play(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer); - - -/** - * Get the pause status. - * This function returns 1 if the playback is currently paused, 0 otherwise. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @return the pause status on success, 0 in case of error - */ -PDRAW_BACKEND_API int pdraw_be_demuxer_is_paused(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer); - - -/** - * Play at normal speed (x1.0). - * This function starts the playback of the video. - * The function returns before the actual operation is done. If the function - * returns 0, the play_resp callback function will be issued once the play is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the play_resp callback - * function will not be issued. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int pdraw_be_demuxer_play(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer); - - -/** - * Play at the given speed. - * This function starts the playback of the video. If the speed parameter is - * negative, the video is played backward. If the speed is greater than or - * equal to PDRAW_PLAY_SPEED_MAX, the speed is ignored and the video is played - * at the maximum speed achievable. If the speed is less than or equal to - * -PDRAW_PLAY_SPEED_MAX, the speed is ignored and the video is played backward - * at the maximum speed achievable. A 0.0 speed has the same effet as calling - * the pdraw_be_pause() function. On a live stream, the speed parameter has - * no effect. - * The function returns before the actual operation is done. If the function - * returns 0, the play_resp callback function will be issued once the play is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the play_resp callback - * function will not be issued. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @param speed: playback speed (0.0 means pause, negative value means - * play backward) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_demuxer_play_with_speed(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer, - float speed); - - -/** - * Pause the playback. - * This function suspends the playback of the video. The session is not closed - * and the playback can be resumed using the play functions. - * The function returns before the actual operation is done. If the function - * returns 0, the pause_resp callback function will be issued once the pause is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the pause_resp callback - * function will not be issued. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int pdraw_be_demuxer_pause(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer); - - -/** - * Go to previous frame in frame-by-frame playback. - * This function plays the previous frame while the playback is paused. If the - * playback is not currently paused an error is returned. Frame-by-frame is - * only available on local replays (MP4 records). - * The function returns before the actual operation is done. If the function - * returns 0, the seek_resp callback function will be issued once the seek is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the seek_resp callback - * function will not be issued. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_demuxer_previous_frame(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer); - - -/** - * Go to next frame in frame-by-frame playback. - * This function plays the next frame while the playback is paused. If the - * playback is not currently paused an error is returned. Frame-by-frame is - * only available on local replays (MP4 records). - * The function returns before the actual operation is done. If the function - * returns 0, the seek_resp callback function will be issued once the seek is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the seek_resp callback - * function will not be issued. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_demuxer_next_frame(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer); - - -/** - * Seek forward or backward. - * This function seeks forward (positive delta) or backward (negative delta). - * The delta parameter is in microseconds. The exact parameter is a boolean - * value; when exact is 0 the seek is done to the nearest synchronization sample - * preceeding the delta, otherwise the seek is done to the sample nearest to the - * delta. Seeking is only available on replays (either local or streamed), - * not on live streams. - * The function returns before the actual operation is done. If the function - * returns 0, the seek_resp callback function will be issued once the seek is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the seek_resp callback - * function will not be issued. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @param delta: time delta in microseconds (positive or negative) - * @param exact: 1 means seek to the sample closest to the delta, 0 means seek - * to the nearest synchronization sample preceeding the delta - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int pdraw_be_demuxer_seek(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer, - int64_t delta, - int exact); - - -/** - * Seek forward. - * This function seeks forward by delta microseconds. It has the same - * behavior as calling pdraw_be_seek() with a positive delta. The exact - * parameter is a boolean value; when exact is 0 the seek is done to the - * nearest synchronization sample preceeding the delta, otherwise the seek - * is done to the sample nearest to the delta. Seeking is only available on - * replays (either local or streamed), not on live streams. - * The function returns before the actual operation is done. If the function - * returns 0, the seek_resp callback function will be issued once the seek is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the seek_resp callback - * function will not be issued. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @param delta: positive time delta forward in microseconds - * @param exact: 1 means seek to the sample closest to the delta, 0 means seek - * to the nearest synchronization sample preceeding the delta - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_demuxer_seek_forward(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer, - uint64_t delta, - int exact); - - -/** - * Seek backward. - * This function seeks backward by delta microseconds (postive). It has the same - * behavior as calling pdraw_be_seek() with a negative delta. The exact - * parameter is a boolean value; when exact is 0 the seek is done to the nearest - * synchronization sample preceeding the delta, otherwise the seek is done to - * the sample nearest to the delta. Seeking is only available on replays - * (either local or streamed), not on live streams. - * The function returns before the actual operation is done. If the function - * returns 0, the seek_resp callback function will be issued once the seek is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the seek_resp callback - * function will not be issued. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @param delta: positive time delta backward in microseconds - * @param exact: 1 means seek to the sample closest to the delta, 0 means seek - * to the nearest synchronization sample preceeding the delta - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int pdraw_be_demuxer_seek_back(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer, - uint64_t delta, - int exact); - - -/** - * Seek to a given time. - * This function seeks to the given play timestamp in microseconds. The exact - * parameter is a boolean value; when exact is 0 the seek is done to the nearest - * synchronization sample preceeding the timestamp, otherwise the seek is done - * to the sample nearest to the timestamp. Seeking is only available on replays - * (either local or streamed), not on live streams. - * The function returns before the actual operation is done. If the function - * returns 0, the seek_resp callback function will be issued once the seek is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the seek_resp callback - * function will not be issued. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @param timestamp: play timestamp in microseconds - * @param exact: 1 means seek to the sample closest to the timestamp, - * 0 means seek to the nearest synchronization sample - * preceeding the timestamp - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int pdraw_be_demuxer_seek_to(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer, - uint64_t timestamp, - int exact); - - -/** - * Get the playback duration. - * This function returns the playback duration in microseconds. The duration is - * only available on replays (either local or streamed), not on live streams. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @return the duration in microseconds on success, 0 in case of error - */ -PDRAW_BACKEND_API uint64_t -pdraw_be_demuxer_get_duration(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer); - - -/** - * Get the playback current time. - * This function returns the current playback position in microseconds. - * On replays (either local or streamed) this is the position between 0 and - * the duration; on live streams this is the time since the start of the - * stream session. - * @param pdraw: PDrAW back-end instance handle - * @param demuxer: demuxer handle - * @return the current time in microseconds on success, 0 in case of error - */ -PDRAW_BACKEND_API uint64_t -pdraw_be_demuxer_get_current_time(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer); - - -/** - * Muxer API - */ - -/** - * Create a muxer (experimental). - * This function creates a muxer with a given URL. - * Once the muxer is created medias can be added by id using the - * pdraw_be_muxer_add_media() function. Once a muxer is no longer used it must - * be destroyed by calling the pdraw_be_muxer_destroy() function. If writing to - * an MP4 file, the file is finalized in the pdraw_be_muxer_destroy() function. - * @note: experimental only, the function returns -ENOSYS - * @param pdraw: PDrAW back-end instance handle - * @param url: destination URL - * @param ret_obj: muxer handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int pdraw_be_muxer_new(struct pdraw_backend *self, - const char *url, - struct pdraw_muxer **ret_obj); - - -/** - * Destroy a muxer. - * This function stops a running muxer and frees the associated resources. - * @param pdraw: PDrAW back-end instance handle - * @param muxer: muxer handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int pdraw_be_muxer_destroy(struct pdraw_backend *self, - struct pdraw_muxer *muxer); - - -/** - * Add a media to a muxer. - * This function adds a media to the muxer by its media_id. The media - * idenfifiers are known when the media_added or media_removed general - * callback functions are called. - * The params structure is only relevant for video medias; the structure must - * then be provided but all parameters are optional and can be left null. - * @param pdraw: PDrAW back-end instance handle - * @param muxer: muxer handle - * @param media_id: identifier of the media to add to the muxer - * @param params: muxer video media parameters - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_muxer_add_media(struct pdraw_backend *self, - struct pdraw_muxer *muxer, - unsigned int media_id, - const struct pdraw_muxer_video_media_params *params); - - -/** - * Video renderer API - * @warning: all functions must be called from the application's - * rendering thread - */ - -/** - * Create a video renderer. - * This function creates a video renderer on a media of the given media id; - * if the media id is zero the first raw media encountered is used. Once - * the renderer is created, the rendering is done by calling the - * pdraw_video_renderer_render*() functions. Once a renderer is no longer used - * it must be destroyed by calling the pdraw_be_video_renderer_destroy() - * function. The render_pos parameter sets the position and size of the - * rendering in the window/view; these coordinates are in pixels from the - * bottom-left corner (OpenGL coordinates). The params structure must be - * provided but all parameters are optional and can be left null. The callbacks - * structure must be provided but all callback functions are optional; all - * callback functions are called from the rendering thread, except the - * render_ready function which is called from the internal pomp_loop thread. - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW back-end instance handle - * @param media_id: identifier of the raw media to render (from a - * pdraw_media_info structure); if zero the first - * raw media found is used for rendering - * @param render_pos: rendering position and size - * @param params: renderer parameters - * @param cbs: renderer callback functions - * @param userdata: callback functions user data (optional, can be null) - * @param ret_obj: renderer handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_video_renderer_new(struct pdraw_backend *self, - unsigned int media_id, - const struct pdraw_rect *render_pos, - const struct pdraw_video_renderer_params *params, - const struct pdraw_backend_video_renderer_cbs *cbs, - void *userdata, - struct pdraw_video_renderer **ret_obj); - - -/** - * Create a video renderer on an EGL display. - * This function creates a video renderer on an active EGL display context on a - * media of the given media id; if the media id is zero the first raw - * media encountered is used. Once the renderer is created, the rendering is - * done by calling the pdraw_video_renderer_render*() functions. Once a renderer - * is no longer used it must be destroyed by calling the - * pdraw_be_video_renderer_destroy() function. The render_pos parameter sets the - * position and size of the rendering in the window/view; these coordinates are - * in pixels from the bottom-left corner (OpenGL coordinates). The params - * structure must be provided but all parameters are optional and can be left - * null. The callbacks structure must be provided but all callback functions are - * optional; all callback functions are called from the rendering thread, - * except the render_ready function which is called from the internal pomp_loop - * thread. - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW back-end instance handle - * @param media_id: identifier of the raw media to render (from a - * pdraw_media_info structure); if zero the first - * raw media found is used for rendering - * @param render_pos: rendering position and size - * @param params: renderer parameters - * @param cbs: renderer callback functions - * @param userdata: callback functions user data (optional, can be null) - * @param egl_display: EGL display context - * @param ret_obj: renderer handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int pdraw_be_video_renderer_new_egl( - struct pdraw_backend *self, - unsigned int media_id, - const struct pdraw_rect *render_pos, - const struct pdraw_video_renderer_params *params, - const struct pdraw_backend_video_renderer_cbs *cbs, - void *userdata, - struct egl_display *egl_display, - struct pdraw_video_renderer **ret_obj); - - -/** - * Stop a video renderer. - * This function stops a running video renderer and frees the associated - * resources. - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW back-end instance handle - * @param renderer: renderer handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_video_renderer_destroy(struct pdraw_backend *self, - struct pdraw_video_renderer *renderer); - - -/** - * Resize a video renderer. - * This function updates the rendering position on a running video renderer. - * The render_pos parameter sets the position and size of the rendering in the - * window/view; these coordinates are in pixels from the bottom-left corner - * (OpenGL coordinates). - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW back-end instance handle - * @param renderer: renderer handle - * @param render_pos: rendering position and size - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_video_renderer_resize(struct pdraw_backend *self, - struct pdraw_video_renderer *renderer, - const struct pdraw_rect *render_pos); - - -/** - * Set the video renderer media identifier. - * This function updates the identifier of the media on which the rendering is - * done; if the media id is zero the first raw media encountered is used. - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW back-end instance handle - * @param renderer: renderer handle - * @param media_id: identifier of the raw media to render (from a - * pdraw_media_info structure); if zero the first raw media - * found is used for rendering - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_video_renderer_set_media_id(struct pdraw_backend *self, - struct pdraw_video_renderer *renderer, - unsigned int media_id); - - -/** - * Get the video renderer media identifier. - * This function retrieves the identifier of the media on which the rendering - * is done. - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW back-end instance handle - * @param renderer: renderer handle - * @return the identifier of the media on success, 0 if no media is being - * renderered or in case of error - */ -PDRAW_BACKEND_API unsigned int -pdraw_be_video_renderer_get_media_id(struct pdraw_backend *self, - struct pdraw_video_renderer *renderer); - - -/** - * Set the video renderer parameters. - * This function updates the rendering parameters on a running video renderer. - * The params structure must be provided but all parameters are optional and - * can be left null. - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW back-end instance handle - * @param renderer: renderer handle - * @param params: renderer parameters - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int pdraw_be_video_renderer_set_params( - struct pdraw_backend *self, - struct pdraw_video_renderer *renderer, - const struct pdraw_video_renderer_params *params); - - -/** - * Get the video renderer parameters. - * This function retrieves the rendering parameters on a running video renderer. - * The provided params structure is filled by the function. - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW back-end instance handle - * @param renderer: renderer handle - * @param params: renderer parameters (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_video_renderer_get_params(struct pdraw_backend *self, - struct pdraw_video_renderer *renderer, - struct pdraw_video_renderer_params *params); - - -/** - * Render the video. - * This function renders the video with the current rendering position and - * rendering parameters. - * For render-on-demand, consider using the render_ready callback function - * which is called both when a new frame is ready for rendering and - * periodically. Note that the render_ready callback function is called from - * the internal pomp_loop thread, not the rendering thread; the synchronization - * is up to the caller. - * If a content_pos structure is provided, it is filled with the actual position - * and size of the video within the rendering position; these coordinates are - * in pixels from the bottom-left corner (OpenGL coordinates). - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW back-end instance handle - * @param renderer: renderer handle - * @param content_pos: video content position (output; optional, can be null) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_video_renderer_render(struct pdraw_backend *self, - struct pdraw_video_renderer *renderer, - struct pdraw_rect *content_pos); - - -/** - * Render the video with provided matrices. - * This function renders the video with the current rendering position and - * rendering parameters, and the provided view and projection matrices. - * The view_mat and proj_mat matrices are 4x4 OpenGL matrices. - * For render-on-demand, consider using the render_ready callback function - * which is called both when a new frame is ready for rendering and - * periodically. Note that the render_ready callback function is called from - * the internal pomp_loop thread, not the rendering thread; the synchronization - * is up to the caller. - * If a content_pos structure is provided, it is filled with the actual position - * and size of the video within the rendering position; these coordinates are - * in pixels from the bottom-left corner (OpenGL coordinates). - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW back-end instance handle - * @param renderer: renderer handle - * @param content_pos: video content position (output; optional, can be null) - * @param view_mat: 4x4 view matrix - * @param proj_mat: 4x4 projection matrix - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_video_renderer_render_mat(struct pdraw_backend *self, - struct pdraw_video_renderer *renderer, - struct pdraw_rect *content_pos, - const float *view_mat, - const float *proj_mat); - - -/** - * Video sink API - */ - -/** - * Create a coded video sink. - * This function creates a video sink on a media of the given media_id. - * The media idenfifiers are known when the media_added or media_removed - * general callback functions are called. - * Once the sink is created, video frames are retrieved by getting buffers - * from the buffer queue returned by the pdraw_be_coded_video_sink_get_queue() - * function. Once a video sink is no longer used, it must be destroyed by - * calling the pdraw_be_coded_video_sink_destroy() function. - * The params structure must be provided but all parameters are optional and - * can be left null. - * The callbacks structure must be provided and the flush callback function is - * required to be implemented; all callback functions are called from the - * internal pomp_loop thread. When the flush callback function is called, the - * application must flush the sink queue by calling - * mbuf_coded_video_frame_queue_flush() and must return all frames outside of - * the queue by calling mbuf_coded_video_frame_unref(); once the flushing is - * complete, the pdraw_be_coded_video_sink_queue_flushed() function must be - * called. - * - * @note media_id must refer to a coded video media. - * - * @param pdraw: PDrAW back-end instance handle - * @param media_id: identifier of the media on which to create the sink - * @param params: video sink parameters - * @param cbs: video sink callback functions - * @param userdata: callback functions user data (optional, can be null) - * @param ret_obj: video sink handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int pdraw_be_coded_video_sink_new( - struct pdraw_backend *self, - unsigned int media_id, - const struct pdraw_video_sink_params *params, - const struct pdraw_backend_coded_video_sink_cbs *cbs, - void *userdata, - struct pdraw_coded_video_sink **ret_obj); - - -/** - * Destroy a coded video sink. - * This function stops a running video sink and frees the associated resources. - * A video sink must not be destroyed unless all frames outside of the queue - * have been returned by calling mbuf_coded_video_frame_unref(). Once a video - * sink is destroyed the queue returned by pdraw_be_coded_video_sink_get_queue() - * must no longer be used. - * @param pdraw: PDrAW back-end instance handle - * @param sink: video sink handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_coded_video_sink_destroy(struct pdraw_backend *self, - struct pdraw_coded_video_sink *sink); - - -/** - * Resynchronize a coded video sink. - * This function schedules the output of a synchronization frame (IDR) for a - * running video sink. It can be used for example in case of - * unrecoverable video decoder errors to restart decoding. After a video sink - * creation, the first frame that is output is always a synchronization frame; - * therefore it is not necessary to call this function immediately after a - * video sink creation. - * @param pdraw: PDrAW back-end instance handle - * @param sink: video sink handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_coded_video_sink_resync(struct pdraw_backend *self, - struct pdraw_coded_video_sink *sink); - - -/** - * Get the coded video sink frame queue. - * This function returns the frame queue to use in order to retrieve frames - * from a running video sink. Frames are retrieved from the queue by using the - * mbuf_coded_video_frame_queue_pop() function. - * @param pdraw: PDrAW back-end instance handle - * @param sink: video sink handle - * @return a pointer on a mbuf_coded_video_frame_queue object on success, NULL - * in case of error - */ -PDRAW_BACKEND_API struct mbuf_coded_video_frame_queue * -pdraw_be_coded_video_sink_get_queue(struct pdraw_backend *self, - struct pdraw_coded_video_sink *sink); - - -/** - * Signal that a coded video sink has been flushed. - * This function is used to signal that flushing is complete. When the flush - * video sink callback function is called, the application must flush the sink - * queue by calling mbuf_coded_video_frame_queue_flush() and must return all - * frames outside of the queue by calling mbuf_coded_video_frame_unref(); once - * the flushing is complete, this function must be called. - * @param pdraw: PDrAW back-end instance handle - * @param sink: video sink handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_coded_video_sink_queue_flushed(struct pdraw_backend *self, - struct pdraw_coded_video_sink *sink); - - -/** - * Create a raw video sink. - * This function creates a video sink on a media of the given media_id. - * The media idenfifiers are known when the media_added or media_removed - * general callback functions are called. - * Once the sink is created, video frames are retrieved by getting buffers - * from the buffer queue returned by the pdraw_be_raw_video_sink_get_queue() - * function. Once a video sink is no longer used, it must be destroyed by - * calling the pdraw_be_raw_video_sink_destroy() function. - * The params structure must be provided but all parameters are optional and - * can be left null. - * The callbacks structure must be provided and the flush callback function is - * required to be implemented; all callback functions are called from the - * internal pomp_loop thread. When the flush callback function is called, the - * application must flush the sink queue by calling - * mbuf_raw_video_frame_queue_flush() and must return all frames outside of - * the queue by calling mbuf_raw_video_frame_unref(); once the flushing is - * complete, the pdraw_be_raw_video_sink_queue_flushed() function must be - * called. - * - * @note media_id must refer to a raw video media. - * - * @param pdraw: PDrAW back-end instance handle - * @param media_id: identifier of the media on which to create the sink - * @param params: video sink parameters - * @param cbs: video sink callback functions - * @param userdata: callback functions user data (optional, can be null) - * @param ret_obj: video sink handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_raw_video_sink_new(struct pdraw_backend *self, - unsigned int media_id, - const struct pdraw_video_sink_params *params, - const struct pdraw_backend_raw_video_sink_cbs *cbs, - void *userdata, - struct pdraw_raw_video_sink **ret_obj); - - -/** - * Destroy a raw video sink. - * This function stops a running video sink and frees the associated resources. - * A video sink must not be destroyed unless all frames outside of the queue - * have been returned by calling mbuf_raw_video_frame_unref(). Once a video - * sink is destroyed the queue returned by pdraw_be_raw_video_sink_get_queue() - * must no longer be used. - * @param pdraw: PDrAW back-end instance handle - * @param sink: video sink handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_raw_video_sink_destroy(struct pdraw_backend *self, - struct pdraw_raw_video_sink *sink); - - -/** - * Get the raw video sink frame queue. - * This function returns the frame queue to use in order to retrieve frames - * from a running video sink. Frames are retrieved from the queue by using the - * mbuf_raw_video_frame_queue_pop() function. - * @param pdraw: PDrAW back-end instance handle - * @param sink: video sink handle - * @return a pointer on a mbuf_raw_video_frame_queue object on success, NULL - * in case of error - */ -PDRAW_BACKEND_API struct mbuf_raw_video_frame_queue * -pdraw_be_raw_video_sink_get_queue(struct pdraw_backend *self, - struct pdraw_raw_video_sink *sink); - - -/** - * Signal that a raw video sink has been flushed. - * This function is used to signal that flushing is complete. When the flush - * video sink callback function is called, the application must flush the sink - * queue by calling mbuf_raw_video_frame_queue_flush() and must return all - * frames outside of the queue by calling mbuf_raw_video_frame_unref(); once - * the flushing is complete, this function must be called. - * @param pdraw: PDrAW back-end instance handle - * @param sink: video sink handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_raw_video_sink_queue_flushed(struct pdraw_backend *self, - struct pdraw_raw_video_sink *sink); - - -/** - * Settings API - */ - -/** - * Get the PDrAW instance friendly name. - * This function fills the str array with the null-terminated friendly name. - * The string must have been previously allocated. The function writes up to - * len characters. The friendly name is generally either the device's friendly - * name (e.g. "Bob's phone") or the application's name (e.g. - * "MyDroneControllerApp"). It is used for example in metadata exchanged with a - * streaming server. - * @param pdraw: PDrAW back-end instance handle - * @param str: pointer to the string to write to (output) - * @param len: maximum length of the string - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_get_friendly_name_setting(struct pdraw_backend *self, - char *str, - size_t len); - - -/** - * Set the PDrAW instance friendly name. - * The friendly_name string is copied internally. The friendly name is generally - * either the device's friendly name (e.g. "Bob's phone") or the application's - * name (e.g. "MyDroneControllerApp"). It is used for example in metadata - * exchanged with a streaming server. Setting the friendly name value is - * optional. - * @param pdraw: PDrAW back-end instance handle - * @param friendly_name: pointer to the friendly name string - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_set_friendly_name_setting(struct pdraw_backend *self, - const char *friendly_name); - - -/** - * Get the PDrAW instance serial number. - * This function fills the str array with the null-terminated serial number. - * The string must have been previously allocated. The function writes up to - * len characters. The serial number is generally the unique serial number of - * the device on which PDrAW is running. It is used for example in metadata - * exchanged with a streaming server. - * @param pdraw: PDrAW back-end instance handle - * @param str: pointer to the string to write to (output) - * @param len: maximum length of the string - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_get_serial_number_setting(struct pdraw_backend *self, - char *str, - size_t len); - - -/** - * Set the PDrAW instance serial number. - * The serial_number string is copied internally. The serial number is generally - * the unique serial number of the device on which PDrAW is running. It is used - * for example in metadata exchanged with a streaming server. Setting the serial - * number value is recommended as it is used as a unique identifier in the - * streaming protocols. - * @param pdraw: PDrAW back-end instance handle - * @param serial_number: pointer to the serial number string - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_set_serial_number_setting(struct pdraw_backend *self, - const char *serial_number); - - -/** - * Get the PDrAW instance software version. - * This function fills the str array with the null-terminated software version. - * The string must have been previously allocated. The function writes up to - * len characters. The software version is generally the version number of the - * application running PDrAW (e.g. "MyApp v1.2.3"). It is used for example in - * metadata exchanged with a streaming server. - * @param pdraw: PDrAW back-end instance handle - * @param str: pointer to the string to write to (output) - * @param len: maximum length of the string - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_get_software_version_setting(struct pdraw_backend *self, - char *str, - size_t len); - - -/** - * Set the PDrAW instance software version. - * The software_version string is copied internally. The software version is - * generally the version number of the application running PDrAW (e.g. - * "MyApp v1.2.3"). It is used for example in metadata exchanged with a - * streaming server. Setting the software version value is optional. - * @param pdraw: PDrAW back-end instance handle - * @param software_version: pointer to the software version string - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_set_software_version_setting(struct pdraw_backend *self, - const char *software_version); - - -/** - * Get the pipeline mode setting. - * This function returns the pipeline mode of a PDrAW instance. The pipeline - * mode controls whether to decode the selected video media (for full processing - * up to the rendering), or to disable video decoding (e.g. when no rendering - * is required, only a coded video sink). - * @param pdraw: PDrAW back-end instance handle - * @return the pipeline mode, or PDRAW_PIPELINE_MODE_DECODE_ALL in case of error - */ -PDRAW_BACKEND_API enum pdraw_pipeline_mode -pdraw_be_get_pipeline_mode_setting(struct pdraw_backend *self); - - -/** - * Set the pipeline mode setting. - * This function sets the pipeline mode of a PDrAW instance. This function can - * be called only prior to any open operation. The pipeline mode controls - * whether to decode the selected video media (for full processing up to the - * rendering), or to disable video decoding (e.g. when no rendering is required, - * only a coded video sink). - * @param pdraw: PDrAW back-end instance handle - * @param mode: pipeline mode - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_set_pipeline_mode_setting(struct pdraw_backend *self, - enum pdraw_pipeline_mode mode); - - -/** - * Get the display screen settings. - * This function returns the display screen settings through the xdpi, ydpi and - * device_margin_* parameters. This is only useful if HMD distortion correction - * is enabled in the video rendering. The xdpi and ydpi are pixel densities in - * dots per inches. The device margins are in millimeters. - * @param pdraw: PDrAW back-end instance handle - * @param xdpi: horizontal pixel density (output) - * @param ydpi: vertical pixel density (output) - * @param device_margin_top: top device margin (output) - * @param device_margin_bottom: bottom device margin (output) - * @param device_margin_left: left device margin (output) - * @param device_margin_right: right device margin (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_get_display_screen_settings(struct pdraw_backend *self, - float *xdpi, - float *ydpi, - float *device_margin_top, - float *device_margin_bottom, - float *device_margin_left, - float *device_margin_right); - - -/** - * Set the display screen settings. - * This function sets the display screen settings. This is only useful if HMD - * distortion correction is enabled in the video rendering. The xdpi and ydpi - * are pixel densities in dots per inches. The device margins are in - * millimeters. - * @param pdraw: PDrAW back-end instance handle - * @param xdpi: horizontal pixel density - * @param ydpi: vertical pixel density - * @param device_margin_top: top device margin - * @param device_margin_bottom: bottom device margin - * @param device_margin_left: left device margin - * @param device_margin_right: right device margin - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_set_display_screen_settings(struct pdraw_backend *self, - float xdpi, - float ydpi, - float device_margin_top, - float device_margin_bottom, - float device_margin_left, - float device_margin_right); - - -/** - * Get the HMD model setting. - * This function returns the head-mounted display (HMD) model. This is only - * useful if HMD distortion correction is enabled in the video rendering. - * @param pdraw: PDrAW back-end instance handle - * @return the HMD model, or PDRAW_HMD_MODEL_UNKNOWN in case of error - */ -PDRAW_BACKEND_API enum pdraw_hmd_model -pdraw_be_get_hmd_model_setting(struct pdraw_backend *self); - - -/** - * Set the HMD model setting. - * This function sets the head-mounted display (HMD) model. This is only - * useful if HMD distortion correction is enabled in the video rendering. - * @param pdraw: PDrAW back-end instance handle - * @param hmd_model: HMD model - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int -pdraw_be_set_hmd_model_setting(struct pdraw_backend *self, - enum pdraw_hmd_model hmd_model); - - -/** - * Platform-specific API - */ - -/** - * Set the Android JVM pointer. - * This function sets the JVM pointer for internal calls to the Android SDK API. - * This is only useful on Android and is ignored on other platforms. If the - * JVM pointer is not provided on Android platforms, some features may not be - * available. - * @param pdraw: PDrAW back-end instance handle - * @param jvm: JVM pointer - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int pdraw_be_set_android_jvm(struct pdraw_backend *self, - void *jvm); - - -/** - * Debug API - */ - -/** - * Dump the current pipeline as a directed graph using the DOT file format. - * @param pdraw: PDrAW back-end instance handle - * @param file_name: DOT file to write to - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int pdraw_be_dump_pipeline(struct pdraw_backend *self, - const char *file_name); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* !_PDRAW_BACKEND_H_ */ +/** + * Parrot Drones Awesome Video Viewer + * PDrAW back-end library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_BACKEND_H_ +#define _PDRAW_BACKEND_H_ + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* To be used for all public API */ +#ifdef PDRAW_BACKEND_API_EXPORTS +# ifdef _WIN32 +# define PDRAW_BACKEND_API __declspec(dllexport) +# else /* !_WIN32 */ +# define PDRAW_BACKEND_API __attribute__((visibility("default"))) +# endif /* !_WIN32 */ +#else /* !PDRAW_BACKEND_API_EXPORTS */ +# define PDRAW_BACKEND_API +#endif /* !PDRAW_BACKEND_API_EXPORTS */ + + +/* Forward declarations */ +struct pdraw_backend; + + +/* General callback functions */ +struct pdraw_backend_cbs { + /* Stop response callback function, called when a stop operation is + * complete or has failed (optional, can be null, but highly recommended + * for correct PDrAW session management). + * The status parameter is the stop operation status: 0 on success, + * or a negative errno value in case of error. + * @param pdraw: PDrAW back-end instance handle + * @param status: 0 on success, negative errno value in case of error + * @param userdata: user data pointer */ + void (*stop_resp)(struct pdraw_backend *pdraw, + int status, + void *userdata); + + /* Media added callback function, called when a media has been added + * internally in the PDrAW pipeline. Medias are for example raw or coded + * video medias. The info structure gives the media identifier that can + * be used for example to create a video sink on this media. + * @param pdraw: PDrAW back-end instance handle + * @param info: pointer on the media information + * @param userdata: user data pointer */ + void (*media_added)(struct pdraw_backend *pdraw, + const struct pdraw_media_info *info, + void *userdata); + + /* Media removed callback function, called when a media has been removed + * internally from the PDrAW pipeline. Medias are for example raw or + * coded video medias. When a media is removed, any video sink created + * on this media must then be stopped. + * @param pdraw: PDrAW back-end instance handle + * @param info: pointer on the media information + * @param userdata: user data pointer */ + void (*media_removed)(struct pdraw_backend *pdraw, + const struct pdraw_media_info *info, + void *userdata); + + /* Socket creation callback function, called immediately after a + * socket creation with its file descriptor as parameter (optional, + * can be null). + * @param pdraw: PDrAW back-end instance handle + * @param fd: socket file descriptor + * @param userdata: user data pointer */ + void (*socket_created)(struct pdraw_backend *pdraw, + int fd, + void *userdata); +}; + + +/* Demuxer callback functions */ +struct pdraw_backend_demuxer_cbs { + /* Open response callback function, called when an open operation is + * complete or has failed (optional, can be null, but highly recommended + * for correct PDrAW session management). + * The status parameter is the open operation status: 0 on success, + * or a negative errno value in case of error. + * If this function reports an error, the demuxer still needs to be + * closed: pdraw_be_demuxer_close() must be called and one must wait for + * the close_resp callback function to be issued prior to calling + * pdraw_be_demuxer_destroy(). + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @param status: 0 on success, negative errno value in case of error + * @param userdata: user data pointer */ + void (*open_resp)(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + int status, + void *userdata); + + /* Close response callback function, called when a close operation is + * complete or has failed (optional, can be null, but highly recommended + * for correct PDrAW session management). + * The status parameter is the close operation status: 0 on success, + * or a negative errno value in case of error. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @param status: 0 on success, negative errno value in case of error + * @param userdata: user data pointer */ + void (*close_resp)(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + int status, + void *userdata); + + /* Unrecoverable error callback function, called when a previously + * opened session is no longer running. When this function is called, + * the demuxer is no longer running; pdraw_be_demuxer_close() must be + * called and one must wait for the close_resp callback function to be + * issued prior to calling pdraw_be_demuxer_destroy(). + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @param userdata: user data pointer */ + void (*unrecoverable_error)(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + void *userdata); + + /* Demuxer media selection callback function, called with a list of + * video medias found from which the application must choose one or more + * to process in the pipeline. The return value of the callback function + * must be a bitfield of the identifiers of the chosen medias (from the + * pdraw_demuxer_media structure), or 0 to choose the default medias. If + * the return value is -ENOSYS, the callback is considered not + * implemented and the default medias are chosen. If the return value is + * -ECANCELED no media is chosen and the open operation is aborted. If + * the return value is another negative errno or an invalid bitfield the + * open_resp callback function will be called if an open operation is in + * progress, or the unrecoverable_error callback function otherwise. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @param medias: array of demuxer media + * @param count: demuxer media array element count + * @param userdata: user data pointer + * @return a bitfield of the identifiers of the chosen medias, + * 0 or -ENOSYS to choose the default medias, + * -ECANCELED to choose no media and abort the open operation, + * or another negative errno value in case of error */ + int (*select_media)(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + const struct pdraw_demuxer_media *medias, + size_t count, + void *userdata); + + /* Ready to play callback function, called when the playback is ready + * to start (optional, can be null). This function is called to indicate + * that the PDrAW session is ready to process play operations. + * Generally, the session is ready to play as soon as the open_resp + * callback function has been called with a success status. One case + * when the open operation was successful and the playback is not ready + * is when connected to a SkyController's RTSP server but the + * SkyController itself is not yet connected to a drone. Similarly, + * when connected to a drone's stream through a SkyController's RTSP + * server, if the drone is disconnected from the SkyController, this + * function will be called with a 0 value in the ready parameter. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @param ready: 1 if the session is ready to play, 0 otherwise + * @param userdata: user data pointer */ + void (*ready_to_play)(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + int ready, + void *userdata); + + /* End of range callback function, called when the playback is suspended + * after having reached the end of the playback duration (optional, + * can be null). This function is only called for replays (either local + * or streamed), not for live streams. The timestamp parameter is the + * current play time in microseconds at the moment the playback is + * suspended. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @param timestamp: current playback time in microseconds + * @param userdata: user data pointer */ + void (*end_of_range)(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + uint64_t timestamp, + void *userdata); + + /* Play response callback function, called when a play operation is + * complete (the playback has started) or has failed (optional, can be + * null). The status parameter is the play operation status: 0 on + * success, or a negative errno value in case of error. The timestamp + * parameter is the current play time in microseconds at the moment the + * playback is started. The speed parameter is the current playback + * speed; a negative value means playing backward. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @param status: 0 on success, negative errno value in case of error + * @param timestamp: current playback time in microseconds + * @param speed: current playback speed, negative means backward + * @param userdata: user data pointer */ + void (*play_resp)(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + int status, + uint64_t timestamp, + float speed, + void *userdata); + + /* Pause response callback function, called when a pause operation is + * complete (the playback is suspended) or has failed (optional, can be + * null). The status parameter is the pause operation status: 0 on + * success, or a negative errno value in case of error. The timestamp + * parameter is the current play time in microseconds at the moment the + * playback is paused. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @param status: 0 on success, negative errno value in case of error + * @param timestamp: current playback time in microseconds + * @param userdata: user data pointer */ + void (*pause_resp)(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + int status, + uint64_t timestamp, + void *userdata); + + /* Seek response callback function, called when a seek operation is + * complete or has failed (optional, can be null). + * The status parameter is the seek operation status: 0 on success, + * or a negative errno value in case of error. The timestamp parameter + * is the current play time in microseconds after seeking. The speed + * parameter is the current playback speed; a negative value means + * playing backward. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @param status: 0 on success, negative errno value in case of error + * @param timestamp: current playback time in microseconds + * @param speed: current playback speed, negative means backward + * @param userdata: user data pointer */ + void (*seek_resp)(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + int status, + uint64_t timestamp, + float speed, + void *userdata); +}; + + +/* Video renderer callback functions */ +struct pdraw_backend_video_renderer_cbs { + /* Media added callback function, called when a media has been added + * internally to the renderer. Medias are raw video medias. + * This function is called from the internal pomp_loop thread. + * @param pdraw: PDrAW back-end instance handle + * @param renderer: renderer handle + * @param info: pointer on the media information + * @param userdata: user data pointer */ + void (*media_added)(struct pdraw_backend *pdraw, + struct pdraw_video_renderer *renderer, + const struct pdraw_media_info *info, + void *userdata); + + /* Media removed callback function, called when a media has been removed + * internally from the renderer. Medias are raw video medias. + * This function is called from the internal pomp_loop thread. + * @param pdraw: PDrAW back-end instance handle + * @param renderer: renderer handle + * @param info: pointer on the media information + * @param userdata: user data pointer */ + void (*media_removed)(struct pdraw_backend *pdraw, + struct pdraw_video_renderer *renderer, + const struct pdraw_media_info *info, + void *userdata); + + /* Render ready callback function, called both when a new frame is + * ready for rendering and periodically (optional, can be null). + * This function is called from the internal pomp_loop thread. + * @param pdraw: PDrAW back-end instance handle + * @param renderer: renderer handle + * @param userdata: user data pointer */ + void (*render_ready)(struct pdraw_backend *pdraw, + struct pdraw_video_renderer *renderer, + void *userdata); + + /* External texture loading callback function (optional, can be null). + * This function is called before the rendering of the video frame in + * order to override the frame loading as a texture. This can be used + * to transform the video frames outside of PDrAW before resuming the + * rendering. This function is called from the rendering thread. + * @param pdraw: PDrAW back-end instance handle + * @param renderer: renderer handle + * @param texture_width: texture width in pixels + * @param texture_height: texture height in pixels + * @param media_info: media information + * @param frame: frame information + * @param frame_userdata: frame user data buffer + * @param frame_userdata_len: frame user data buffer size in bytes + * @param userdata: user data pointer + * @return 0 on success, negative errno value in case of error */ + int (*load_texture)(struct pdraw_backend *pdraw, + struct pdraw_video_renderer *renderer, + unsigned int texture_width, + unsigned int texture_height, + const struct pdraw_media_info *media_info, + struct mbuf_raw_video_frame *frame, + const void *frame_userdata, + size_t frame_userdata_len, + void *userdata); + + /* Overlay rendering callback function (optional, can be null). + * This function is called after the rendering of the video frame + * (if one is available) in order to render an application overlay + * on top of the video. When HMD distorsion correction is enabled + * in the renderer, it is applied after the overlay rendering. + * This function is called from the rendering thread. + * When no frame is available for the rendering, the frame_meta + * and frame_extra parameters are NULL. + * @param pdraw: PDrAW back-end instance handle + * @param renderer: renderer handle + * @param render_pos: rendering position + * @param content_pos: video content position + * @param view_mat: 4x4 view matrix + * @param proj_mat: 4x4 projection matrix + * @param media_info: media information + * @param frame_meta: frame metadata (optional, can be NULL) + * @param frame_extra: frame extra information (optional, can be NULL) + * @param userdata: user data pointer */ + void (*render_overlay)( + struct pdraw_backend *pdraw, + struct pdraw_video_renderer *renderer, + const struct pdraw_rect *render_pos, + const struct pdraw_rect *content_pos, + const float *view_mat, + const float *proj_mat, + const struct pdraw_media_info *media_info, + struct vmeta_frame *frame_meta, + const struct pdraw_video_frame_extra *frame_extra, + void *userdata); +}; + + +/* Coded video sink callback functions */ +struct pdraw_backend_coded_video_sink_cbs { + /* Video sink flush callback function, called when flushing is required + * (mandatory). When this function is called, the application must flush + * the sink queue by calling mbuf_coded_video_frame_queue_flush() and + * must return all frames outside of the queue by calling + * mbuf_coded_video_frame_unref(); once the flushing is done, the + * pdraw_be_coded_video_sink_queue_flushed() function must be called. + * @param pdraw: PDrAW back-end instance handle + * @param sink: video sink handle + * @param userdata: user data pointer */ + void (*flush)(struct pdraw_backend *pdraw, + struct pdraw_coded_video_sink *sink, + void *userdata); +}; + + +/* Video sink callback functions */ +struct pdraw_backend_raw_video_sink_cbs { + /* Video sink flush callback function, called when flushing is required + * (mandatory). When this function is called, the application must flush + * the sink queue by calling mbuf_raw_video_frame_queue_flush() and + * must return all frames outside of the queue by calling + * mbuf_raw_video_frame_unref(); once the flushing is done, the + * pdraw_be_raw_video_sink_queue_flushed() function must be called. + * @param pdraw: PDrAW back-end instance handle + * @param sink: video sink handle + * @param userdata: user data pointer */ + void (*flush)(struct pdraw_backend *pdraw, + struct pdraw_raw_video_sink *sink, + void *userdata); +}; + + +/** + * Instance management API + */ + +/** + * Create a PDrAW back-end instance. + * All API functions can be called from any thread, with the exception of + * rendering functions which must still be called from the rendering thread. + * All callback functions with the exception of video renderer callback + * functions are called from the internal pomp_loop thread; synchronization + * is up to the application. + * The callbacks structure must be provided but all callback functions are + * optional. However, for correct management of the session it is highly + * recommended to implement at least the open_resp and close_resp functions. + * The instance handle is returned through the ret_obj parameter. When no + * longer needed, the instance must be freed using the pdraw_be_destroy() + * function. The pdraw_be_stop() function must be called and one must wait + * for the stop_resp callback function to be issued prior to calling + * pdraw_be_destroy(). + * @param cbs: back-end instance callback functions + * @param userdata: callback functions user data pointer + * @param ret_obj: PDrAW back-end instance handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int pdraw_be_new(const struct pdraw_backend_cbs *cbs, + void *userdata, + struct pdraw_backend **ret_obj); + + +/** + * Free a PDrAW back-end instance. + * This function frees all resources associated with a back-end instance. + * If a successful pdraw_be_open_*() was made, the pdraw_be_close() function + * must be called and one must wait for the close_resp callback function to + * be issued prior to calling pdraw_be_destroy(). + * @param self: PDrAW back-end instance handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int pdraw_be_destroy(struct pdraw_backend *self); + + +/** + * Stop a PDrAW back-end instance. + * This function stops a PDrAW back-end instance and all the associated objects. + * The function returns before the actual stopping is done. If the function + * returns 0, the stop_resp callback function will be called once the stopping + * is successful (0 status) or has failed (negative errno status). If the + * function returns a negative errno value (immediate failure), the stop_resp + * callback function will not be called. After a successful stop, the PDrAW + * instance must be destroyed by calling pdraw_destroy(). + * @param self: PDrAW back-end instance handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int pdraw_be_stop(struct pdraw_backend *self); + + +/** + * Get the PDrAW back-end internal event loop. + * This function returns a pointer to the event loop used internally. + * @param self: PDrAW back-end instance handle + * @return a pointer on the internal loop on success, NULL in case of error + */ +PDRAW_BACKEND_API struct pomp_loop * +pdraw_be_get_loop(struct pdraw_backend *self); + + +/** + * Demuxer API + */ + +/** + * Create a demuxer on a URL (stream or local file). + * The URL can be either an RTSP URL (starting with "rtsp://") or a local file + * path (either absolute or relative). + * The function returns before the actual opening is done. If the function + * returns 0, the open_resp callback function will be issued once the open + * operation is successful (0 status) or has failed (negative errno status). + * If the function returns a negative errno value (immediate failure), the + * open_resp callback function will not be issued. + * Once a demuxer is no longer used, it must be closed and then destroyed + * (@see the pdraw_be_demuxer_close() function). + * @param pdraw: PDrAW back-end instance handle + * @param url: URL of the resource to open + * @param cbs: demuxer callback functions + * @param userdata: callback functions user data (optional, can be null) + * @param ret_obj: demuxer handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_demuxer_new_from_url(struct pdraw_backend *self, + const char *url, + const struct pdraw_backend_demuxer_cbs *cbs, + void *userdata, + struct pdraw_demuxer **ret_obj); + + +/** + * Create a demuxer on a single stream. + * This function opens an RTP/AVP stream. No session management is done: it + * is the application's responsibility to handle the ports negociation with + * the sender. If null local ports are given as parameter, the effective port + * numbers used can be retrieved using the + * pdraw_be_get_single_stream_local_stream_port() for the RTP port and + * pdraw_be_get_single_stream_local_control_port() for the RTCP functions. + * If the local_addr parameter is left null, any local network interface will + * be used. The remote_addr, remote_stream_port and remote_control_port + * parameters can be left null if unknown; they will be known once the stream + * is being received. + * The function returns before the actual opening is done. If the function + * returns 0, the open_resp callback function will be issued once the open + * operation is successful (0 status) or has failed (negative errno status). + * If the function returns a negative errno value (immediate failure), the + * open_resp callback function will not be issued. + * Once a demuxer is no longer used, it must be closed and then destroyed + * (@see the pdraw_be_demuxer_close() function). + * @param pdraw: PDrAW back-end instance handle + * @param local_addr: local IP address (optional, can be NULL) + * @param local_stream_port: local stream (RTP) port (optional, can be 0) + * @param local_control_port: local control (RTCP) port (optional, can be 0) + * @param remote_addr: remote IP address (optional, can be NULL) + * @param remote_stream_port: remote stream (RTP) port (optional, can be 0) + * @param remote_control_port: remote control (RTCP) port (optional, can be 0) + * @param cbs: demuxer callback functions + * @param userdata: callback functions user data (optional, can be null) + * @param ret_obj: demuxer handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_demuxer_new_single_stream(struct pdraw_backend *self, + const char *local_addr, + uint16_t local_stream_port, + uint16_t local_control_port, + const char *remote_addr, + uint16_t remote_stream_port, + uint16_t remote_control_port, + const struct pdraw_backend_demuxer_cbs *cbs, + void *userdata, + struct pdraw_demuxer **ret_obj); + + +/** + * Create a demuxer on a stream URL through a mux channel. + * The URL must be an RTSP URL (starting with "rtsp://"). Mux channels are used + * to transfer data between a SkyController remote and a smartphone through USB; + * see Parrot's libmux for more information. + * No concurrent sessions can run on the mux channel; therefore the user must + * take care of limiting the number of PDrAW instances and demuxer objects + * running on the mux channel to only one. + * The function returns before the actual opening is done. If the function + * returns 0, the open_resp callback function will be issued once the open + * operation is successful (0 status) or has failed (negative errno status). + * If the function returns a negative errno value (immediate failure), the + * open_resp callback function will not be issued. + * Once a demuxer is no longer used, it must be closed and then destroyed + * (@see the pdraw_be_demuxer_close() function). + * @param pdraw: PDrAW back-end instance handle + * @param url: URL of the resource to open + * @param mux: mux instance handle + * @param cbs: demuxer callback functions + * @param userdata: callback functions user data (optional, can be null) + * @param ret_obj: demuxer handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int pdraw_be_demuxer_new_from_url_on_mux( + struct pdraw_backend *self, + const char *url, + struct mux_ctx *mux, + const struct pdraw_backend_demuxer_cbs *cbs, + void *userdata, + struct pdraw_demuxer **ret_obj); + + +/** + * Destroy a demuxer. + * This function stops a running demuxer and frees the associated resources. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int pdraw_be_demuxer_destroy(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer); + + +/** + * Close a demuxer. + * This function closes a previously opened demuxer (either record or stream). + * The function returns before the actual closing is done. If the function + * returns 0, the close_resp callback function will be issued once the close is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the close_resp callback + * function will not be issued. After a successful close, the demuxer must be + * destroyed by calling pdraw_be_demuxer_destroy(). + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int pdraw_be_demuxer_close(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer); + + +/** + * Get the single stream local stream port. + * This function returns the local stream (RTP) port currently in use after + * a succesful open operation on a single stream (RTP/AVP) or an RTSP URL. + * If no open operation has been done, or if an open operation has been done + * on a mux channel, 0 is returned. + * This is useful when calling pdraw_be_open_single_stream() with null local + * ports to let PDrAW open sockets on any available port. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @return the stream port on success, 0 in case of error + */ +PDRAW_BACKEND_API uint16_t pdraw_be_demuxer_get_single_stream_local_stream_port( + struct pdraw_backend *self, + struct pdraw_demuxer *demuxer); + + +/** + * Get the single stream local control port. + * This function returns the local control (RTCP) port currently in use after + * a succesful open operation on a single stream (RTP/AVP) or an RTSP URL. + * If no open operation has been done, or if an open operation has been done + * on a mux channel, 0 is returned. + * This is useful when calling pdraw_be_open_single_stream() with null local + * ports to let PDrAW open sockets on any available port. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @return the stream port on success, 0 in case of error + */ +PDRAW_BACKEND_API uint16_t +pdraw_be_demuxer_get_single_stream_local_control_port( + struct pdraw_backend *self, + struct pdraw_demuxer *demuxer); + + +/** + * Get the ready to play status. + * This function returns 1 if a successful open operation has been completed + * and if the playback is ready to start, 0 otherwise. One case when a + * successful open operation is complete but the playback is not ready is when + * connected to a SkyController's RTSP server but the SkyController itself is + * not yet connected to a drone. + * The value returned by this function is identical to the ready parameter + * passed to the ready_to_play callback function when it is issued. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @return the ready to play status on success, 0 in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_demuxer_is_ready_to_play(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer); + + +/** + * Get the pause status. + * This function returns 1 if the playback is currently paused, 0 otherwise. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @return the pause status on success, 0 in case of error + */ +PDRAW_BACKEND_API int pdraw_be_demuxer_is_paused(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer); + + +/** + * Play at normal speed (x1.0). + * This function starts the playback of the video. + * The function returns before the actual operation is done. If the function + * returns 0, the play_resp callback function will be issued once the play is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the play_resp callback + * function will not be issued. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int pdraw_be_demuxer_play(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer); + + +/** + * Play at the given speed. + * This function starts the playback of the video. If the speed parameter is + * negative, the video is played backward. If the speed is greater than or + * equal to PDRAW_PLAY_SPEED_MAX, the speed is ignored and the video is played + * at the maximum speed achievable. If the speed is less than or equal to + * -PDRAW_PLAY_SPEED_MAX, the speed is ignored and the video is played backward + * at the maximum speed achievable. A 0.0 speed has the same effet as calling + * the pdraw_be_pause() function. On a live stream, the speed parameter has + * no effect. + * The function returns before the actual operation is done. If the function + * returns 0, the play_resp callback function will be issued once the play is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the play_resp callback + * function will not be issued. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @param speed: playback speed (0.0 means pause, negative value means + * play backward) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_demuxer_play_with_speed(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer, + float speed); + + +/** + * Pause the playback. + * This function suspends the playback of the video. The session is not closed + * and the playback can be resumed using the play functions. + * The function returns before the actual operation is done. If the function + * returns 0, the pause_resp callback function will be issued once the pause is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the pause_resp callback + * function will not be issued. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int pdraw_be_demuxer_pause(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer); + + +/** + * Go to previous frame in frame-by-frame playback. + * This function plays the previous frame while the playback is paused. If the + * playback is not currently paused an error is returned. Frame-by-frame is + * only available on local replays (MP4 records). + * The function returns before the actual operation is done. If the function + * returns 0, the seek_resp callback function will be issued once the seek is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the seek_resp callback + * function will not be issued. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_demuxer_previous_frame(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer); + + +/** + * Go to next frame in frame-by-frame playback. + * This function plays the next frame while the playback is paused. If the + * playback is not currently paused an error is returned. Frame-by-frame is + * only available on local replays (MP4 records). + * The function returns before the actual operation is done. If the function + * returns 0, the seek_resp callback function will be issued once the seek is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the seek_resp callback + * function will not be issued. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_demuxer_next_frame(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer); + + +/** + * Seek forward or backward. + * This function seeks forward (positive delta) or backward (negative delta). + * The delta parameter is in microseconds. The exact parameter is a boolean + * value; when exact is 0 the seek is done to the nearest synchronization sample + * preceeding the delta, otherwise the seek is done to the sample nearest to the + * delta. Seeking is only available on replays (either local or streamed), + * not on live streams. + * The function returns before the actual operation is done. If the function + * returns 0, the seek_resp callback function will be issued once the seek is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the seek_resp callback + * function will not be issued. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @param delta: time delta in microseconds (positive or negative) + * @param exact: 1 means seek to the sample closest to the delta, 0 means seek + * to the nearest synchronization sample preceeding the delta + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int pdraw_be_demuxer_seek(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer, + int64_t delta, + int exact); + + +/** + * Seek forward. + * This function seeks forward by delta microseconds. It has the same + * behavior as calling pdraw_be_seek() with a positive delta. The exact + * parameter is a boolean value; when exact is 0 the seek is done to the + * nearest synchronization sample preceeding the delta, otherwise the seek + * is done to the sample nearest to the delta. Seeking is only available on + * replays (either local or streamed), not on live streams. + * The function returns before the actual operation is done. If the function + * returns 0, the seek_resp callback function will be issued once the seek is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the seek_resp callback + * function will not be issued. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @param delta: positive time delta forward in microseconds + * @param exact: 1 means seek to the sample closest to the delta, 0 means seek + * to the nearest synchronization sample preceeding the delta + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_demuxer_seek_forward(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer, + uint64_t delta, + int exact); + + +/** + * Seek backward. + * This function seeks backward by delta microseconds (postive). It has the same + * behavior as calling pdraw_be_seek() with a negative delta. The exact + * parameter is a boolean value; when exact is 0 the seek is done to the nearest + * synchronization sample preceeding the delta, otherwise the seek is done to + * the sample nearest to the delta. Seeking is only available on replays + * (either local or streamed), not on live streams. + * The function returns before the actual operation is done. If the function + * returns 0, the seek_resp callback function will be issued once the seek is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the seek_resp callback + * function will not be issued. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @param delta: positive time delta backward in microseconds + * @param exact: 1 means seek to the sample closest to the delta, 0 means seek + * to the nearest synchronization sample preceeding the delta + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int pdraw_be_demuxer_seek_back(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer, + uint64_t delta, + int exact); + + +/** + * Seek to a given time. + * This function seeks to the given play timestamp in microseconds. The exact + * parameter is a boolean value; when exact is 0 the seek is done to the nearest + * synchronization sample preceeding the timestamp, otherwise the seek is done + * to the sample nearest to the timestamp. Seeking is only available on replays + * (either local or streamed), not on live streams. + * The function returns before the actual operation is done. If the function + * returns 0, the seek_resp callback function will be issued once the seek is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the seek_resp callback + * function will not be issued. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @param timestamp: play timestamp in microseconds + * @param exact: 1 means seek to the sample closest to the timestamp, + * 0 means seek to the nearest synchronization sample + * preceeding the timestamp + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int pdraw_be_demuxer_seek_to(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer, + uint64_t timestamp, + int exact); + + +/** + * Get the playback duration. + * This function returns the playback duration in microseconds. The duration is + * only available on replays (either local or streamed), not on live streams. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @return the duration in microseconds on success, 0 in case of error + */ +PDRAW_BACKEND_API uint64_t +pdraw_be_demuxer_get_duration(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer); + + +/** + * Get the playback current time. + * This function returns the current playback position in microseconds. + * On replays (either local or streamed) this is the position between 0 and + * the duration; on live streams this is the time since the start of the + * stream session. + * @param pdraw: PDrAW back-end instance handle + * @param demuxer: demuxer handle + * @return the current time in microseconds on success, 0 in case of error + */ +PDRAW_BACKEND_API uint64_t +pdraw_be_demuxer_get_current_time(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer); + + +/** + * Muxer API + */ + +/** + * Create a muxer (experimental). + * This function creates a muxer with a given URL. + * Once the muxer is created medias can be added by id using the + * pdraw_be_muxer_add_media() function. Once a muxer is no longer used it must + * be destroyed by calling the pdraw_be_muxer_destroy() function. If writing to + * an MP4 file, the file is finalized in the pdraw_be_muxer_destroy() function. + * @note: experimental only, the function returns -ENOSYS + * @param pdraw: PDrAW back-end instance handle + * @param url: destination URL + * @param ret_obj: muxer handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int pdraw_be_muxer_new(struct pdraw_backend *self, + const char *url, + struct pdraw_muxer **ret_obj); + + +/** + * Destroy a muxer. + * This function stops a running muxer and frees the associated resources. + * @param pdraw: PDrAW back-end instance handle + * @param muxer: muxer handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int pdraw_be_muxer_destroy(struct pdraw_backend *self, + struct pdraw_muxer *muxer); + + +/** + * Add a media to a muxer. + * This function adds a media to the muxer by its media_id. The media + * idenfifiers are known when the media_added or media_removed general + * callback functions are called. + * The params structure is only relevant for video medias; the structure must + * then be provided but all parameters are optional and can be left null. + * @param pdraw: PDrAW back-end instance handle + * @param muxer: muxer handle + * @param media_id: identifier of the media to add to the muxer + * @param params: muxer video media parameters + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_muxer_add_media(struct pdraw_backend *self, + struct pdraw_muxer *muxer, + unsigned int media_id, + const struct pdraw_muxer_video_media_params *params); + + +/** + * Video renderer API + * @warning: all functions must be called from the application's + * rendering thread + */ + +/** + * Create a video renderer. + * This function creates a video renderer on a media of the given media id; + * if the media id is zero the first raw media encountered is used. Once + * the renderer is created, the rendering is done by calling the + * pdraw_video_renderer_render*() functions. Once a renderer is no longer used + * it must be destroyed by calling the pdraw_be_video_renderer_destroy() + * function. The render_pos parameter sets the position and size of the + * rendering in the window/view; these coordinates are in pixels from the + * bottom-left corner (OpenGL coordinates). The params structure must be + * provided but all parameters are optional and can be left null. The callbacks + * structure must be provided but all callback functions are optional; all + * callback functions are called from the rendering thread, except the + * render_ready function which is called from the internal pomp_loop thread. + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW back-end instance handle + * @param media_id: identifier of the raw media to render (from a + * pdraw_media_info structure); if zero the first + * raw media found is used for rendering + * @param render_pos: rendering position and size + * @param params: renderer parameters + * @param cbs: renderer callback functions + * @param userdata: callback functions user data (optional, can be null) + * @param ret_obj: renderer handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_video_renderer_new(struct pdraw_backend *self, + unsigned int media_id, + const struct pdraw_rect *render_pos, + const struct pdraw_video_renderer_params *params, + const struct pdraw_backend_video_renderer_cbs *cbs, + void *userdata, + struct pdraw_video_renderer **ret_obj); + + +/** + * Create a video renderer on an EGL display. + * This function creates a video renderer on an active EGL display context on a + * media of the given media id; if the media id is zero the first raw + * media encountered is used. Once the renderer is created, the rendering is + * done by calling the pdraw_video_renderer_render*() functions. Once a renderer + * is no longer used it must be destroyed by calling the + * pdraw_be_video_renderer_destroy() function. The render_pos parameter sets the + * position and size of the rendering in the window/view; these coordinates are + * in pixels from the bottom-left corner (OpenGL coordinates). The params + * structure must be provided but all parameters are optional and can be left + * null. The callbacks structure must be provided but all callback functions are + * optional; all callback functions are called from the rendering thread, + * except the render_ready function which is called from the internal pomp_loop + * thread. + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW back-end instance handle + * @param media_id: identifier of the raw media to render (from a + * pdraw_media_info structure); if zero the first + * raw media found is used for rendering + * @param render_pos: rendering position and size + * @param params: renderer parameters + * @param cbs: renderer callback functions + * @param userdata: callback functions user data (optional, can be null) + * @param egl_display: EGL display context + * @param ret_obj: renderer handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int pdraw_be_video_renderer_new_egl( + struct pdraw_backend *self, + unsigned int media_id, + const struct pdraw_rect *render_pos, + const struct pdraw_video_renderer_params *params, + const struct pdraw_backend_video_renderer_cbs *cbs, + void *userdata, + struct egl_display *egl_display, + struct pdraw_video_renderer **ret_obj); + + +/** + * Stop a video renderer. + * This function stops a running video renderer and frees the associated + * resources. + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW back-end instance handle + * @param renderer: renderer handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_video_renderer_destroy(struct pdraw_backend *self, + struct pdraw_video_renderer *renderer); + + +/** + * Resize a video renderer. + * This function updates the rendering position on a running video renderer. + * The render_pos parameter sets the position and size of the rendering in the + * window/view; these coordinates are in pixels from the bottom-left corner + * (OpenGL coordinates). + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW back-end instance handle + * @param renderer: renderer handle + * @param render_pos: rendering position and size + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_video_renderer_resize(struct pdraw_backend *self, + struct pdraw_video_renderer *renderer, + const struct pdraw_rect *render_pos); + + +/** + * Set the video renderer media identifier. + * This function updates the identifier of the media on which the rendering is + * done; if the media id is zero the first raw media encountered is used. + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW back-end instance handle + * @param renderer: renderer handle + * @param media_id: identifier of the raw media to render (from a + * pdraw_media_info structure); if zero the first raw media + * found is used for rendering + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_video_renderer_set_media_id(struct pdraw_backend *self, + struct pdraw_video_renderer *renderer, + unsigned int media_id); + + +/** + * Get the video renderer media identifier. + * This function retrieves the identifier of the media on which the rendering + * is done. + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW back-end instance handle + * @param renderer: renderer handle + * @return the identifier of the media on success, 0 if no media is being + * renderered or in case of error + */ +PDRAW_BACKEND_API unsigned int +pdraw_be_video_renderer_get_media_id(struct pdraw_backend *self, + struct pdraw_video_renderer *renderer); + + +/** + * Set the video renderer parameters. + * This function updates the rendering parameters on a running video renderer. + * The params structure must be provided but all parameters are optional and + * can be left null. + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW back-end instance handle + * @param renderer: renderer handle + * @param params: renderer parameters + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int pdraw_be_video_renderer_set_params( + struct pdraw_backend *self, + struct pdraw_video_renderer *renderer, + const struct pdraw_video_renderer_params *params); + + +/** + * Get the video renderer parameters. + * This function retrieves the rendering parameters on a running video renderer. + * The provided params structure is filled by the function. + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW back-end instance handle + * @param renderer: renderer handle + * @param params: renderer parameters (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_video_renderer_get_params(struct pdraw_backend *self, + struct pdraw_video_renderer *renderer, + struct pdraw_video_renderer_params *params); + + +/** + * Render the video. + * This function renders the video with the current rendering position and + * rendering parameters. + * For render-on-demand, consider using the render_ready callback function + * which is called both when a new frame is ready for rendering and + * periodically. Note that the render_ready callback function is called from + * the internal pomp_loop thread, not the rendering thread; the synchronization + * is up to the caller. + * If a content_pos structure is provided, it is filled with the actual position + * and size of the video within the rendering position; these coordinates are + * in pixels from the bottom-left corner (OpenGL coordinates). + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW back-end instance handle + * @param renderer: renderer handle + * @param content_pos: video content position (output; optional, can be null) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_video_renderer_render(struct pdraw_backend *self, + struct pdraw_video_renderer *renderer, + struct pdraw_rect *content_pos); + + +/** + * Render the video with provided matrices. + * This function renders the video with the current rendering position and + * rendering parameters, and the provided view and projection matrices. + * The view_mat and proj_mat matrices are 4x4 OpenGL matrices. + * For render-on-demand, consider using the render_ready callback function + * which is called both when a new frame is ready for rendering and + * periodically. Note that the render_ready callback function is called from + * the internal pomp_loop thread, not the rendering thread; the synchronization + * is up to the caller. + * If a content_pos structure is provided, it is filled with the actual position + * and size of the video within the rendering position; these coordinates are + * in pixels from the bottom-left corner (OpenGL coordinates). + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW back-end instance handle + * @param renderer: renderer handle + * @param content_pos: video content position (output; optional, can be null) + * @param view_mat: 4x4 view matrix + * @param proj_mat: 4x4 projection matrix + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_video_renderer_render_mat(struct pdraw_backend *self, + struct pdraw_video_renderer *renderer, + struct pdraw_rect *content_pos, + const float *view_mat, + const float *proj_mat); + + +/** + * Video sink API + */ + +/** + * Create a coded video sink. + * This function creates a video sink on a media of the given media_id. + * The media idenfifiers are known when the media_added or media_removed + * general callback functions are called. + * Once the sink is created, video frames are retrieved by getting buffers + * from the buffer queue returned by the pdraw_be_coded_video_sink_get_queue() + * function. Once a video sink is no longer used, it must be destroyed by + * calling the pdraw_be_coded_video_sink_destroy() function. + * The params structure must be provided but all parameters are optional and + * can be left null. + * The callbacks structure must be provided and the flush callback function is + * required to be implemented; all callback functions are called from the + * internal pomp_loop thread. When the flush callback function is called, the + * application must flush the sink queue by calling + * mbuf_coded_video_frame_queue_flush() and must return all frames outside of + * the queue by calling mbuf_coded_video_frame_unref(); once the flushing is + * complete, the pdraw_be_coded_video_sink_queue_flushed() function must be + * called. + * + * @note media_id must refer to a coded video media. + * + * @param pdraw: PDrAW back-end instance handle + * @param media_id: identifier of the media on which to create the sink + * @param params: video sink parameters + * @param cbs: video sink callback functions + * @param userdata: callback functions user data (optional, can be null) + * @param ret_obj: video sink handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int pdraw_be_coded_video_sink_new( + struct pdraw_backend *self, + unsigned int media_id, + const struct pdraw_video_sink_params *params, + const struct pdraw_backend_coded_video_sink_cbs *cbs, + void *userdata, + struct pdraw_coded_video_sink **ret_obj); + + +/** + * Destroy a coded video sink. + * This function stops a running video sink and frees the associated resources. + * A video sink must not be destroyed unless all frames outside of the queue + * have been returned by calling mbuf_coded_video_frame_unref(). Once a video + * sink is destroyed the queue returned by pdraw_be_coded_video_sink_get_queue() + * must no longer be used. + * @param pdraw: PDrAW back-end instance handle + * @param sink: video sink handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_coded_video_sink_destroy(struct pdraw_backend *self, + struct pdraw_coded_video_sink *sink); + + +/** + * Resynchronize a coded video sink. + * This function schedules the output of a synchronization frame (IDR) for a + * running video sink. It can be used for example in case of + * unrecoverable video decoder errors to restart decoding. After a video sink + * creation, the first frame that is output is always a synchronization frame; + * therefore it is not necessary to call this function immediately after a + * video sink creation. + * @param pdraw: PDrAW back-end instance handle + * @param sink: video sink handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_coded_video_sink_resync(struct pdraw_backend *self, + struct pdraw_coded_video_sink *sink); + + +/** + * Get the coded video sink frame queue. + * This function returns the frame queue to use in order to retrieve frames + * from a running video sink. Frames are retrieved from the queue by using the + * mbuf_coded_video_frame_queue_pop() function. + * @param pdraw: PDrAW back-end instance handle + * @param sink: video sink handle + * @return a pointer on a mbuf_coded_video_frame_queue object on success, NULL + * in case of error + */ +PDRAW_BACKEND_API struct mbuf_coded_video_frame_queue * +pdraw_be_coded_video_sink_get_queue(struct pdraw_backend *self, + struct pdraw_coded_video_sink *sink); + + +/** + * Signal that a coded video sink has been flushed. + * This function is used to signal that flushing is complete. When the flush + * video sink callback function is called, the application must flush the sink + * queue by calling mbuf_coded_video_frame_queue_flush() and must return all + * frames outside of the queue by calling mbuf_coded_video_frame_unref(); once + * the flushing is complete, this function must be called. + * @param pdraw: PDrAW back-end instance handle + * @param sink: video sink handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_coded_video_sink_queue_flushed(struct pdraw_backend *self, + struct pdraw_coded_video_sink *sink); + + +/** + * Create a raw video sink. + * This function creates a video sink on a media of the given media_id. + * The media idenfifiers are known when the media_added or media_removed + * general callback functions are called. + * Once the sink is created, video frames are retrieved by getting buffers + * from the buffer queue returned by the pdraw_be_raw_video_sink_get_queue() + * function. Once a video sink is no longer used, it must be destroyed by + * calling the pdraw_be_raw_video_sink_destroy() function. + * The params structure must be provided but all parameters are optional and + * can be left null. + * The callbacks structure must be provided and the flush callback function is + * required to be implemented; all callback functions are called from the + * internal pomp_loop thread. When the flush callback function is called, the + * application must flush the sink queue by calling + * mbuf_raw_video_frame_queue_flush() and must return all frames outside of + * the queue by calling mbuf_raw_video_frame_unref(); once the flushing is + * complete, the pdraw_be_raw_video_sink_queue_flushed() function must be + * called. + * + * @note media_id must refer to a raw video media. + * + * @param pdraw: PDrAW back-end instance handle + * @param media_id: identifier of the media on which to create the sink + * @param params: video sink parameters + * @param cbs: video sink callback functions + * @param userdata: callback functions user data (optional, can be null) + * @param ret_obj: video sink handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_raw_video_sink_new(struct pdraw_backend *self, + unsigned int media_id, + const struct pdraw_video_sink_params *params, + const struct pdraw_backend_raw_video_sink_cbs *cbs, + void *userdata, + struct pdraw_raw_video_sink **ret_obj); + + +/** + * Destroy a raw video sink. + * This function stops a running video sink and frees the associated resources. + * A video sink must not be destroyed unless all frames outside of the queue + * have been returned by calling mbuf_raw_video_frame_unref(). Once a video + * sink is destroyed the queue returned by pdraw_be_raw_video_sink_get_queue() + * must no longer be used. + * @param pdraw: PDrAW back-end instance handle + * @param sink: video sink handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_raw_video_sink_destroy(struct pdraw_backend *self, + struct pdraw_raw_video_sink *sink); + + +/** + * Get the raw video sink frame queue. + * This function returns the frame queue to use in order to retrieve frames + * from a running video sink. Frames are retrieved from the queue by using the + * mbuf_raw_video_frame_queue_pop() function. + * @param pdraw: PDrAW back-end instance handle + * @param sink: video sink handle + * @return a pointer on a mbuf_raw_video_frame_queue object on success, NULL + * in case of error + */ +PDRAW_BACKEND_API struct mbuf_raw_video_frame_queue * +pdraw_be_raw_video_sink_get_queue(struct pdraw_backend *self, + struct pdraw_raw_video_sink *sink); + + +/** + * Signal that a raw video sink has been flushed. + * This function is used to signal that flushing is complete. When the flush + * video sink callback function is called, the application must flush the sink + * queue by calling mbuf_raw_video_frame_queue_flush() and must return all + * frames outside of the queue by calling mbuf_raw_video_frame_unref(); once + * the flushing is complete, this function must be called. + * @param pdraw: PDrAW back-end instance handle + * @param sink: video sink handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_raw_video_sink_queue_flushed(struct pdraw_backend *self, + struct pdraw_raw_video_sink *sink); + + +/** + * Settings API + */ + +/** + * Get the PDrAW instance friendly name. + * This function fills the str array with the null-terminated friendly name. + * The string must have been previously allocated. The function writes up to + * len characters. The friendly name is generally either the device's friendly + * name (e.g. "Bob's phone") or the application's name (e.g. + * "MyDroneControllerApp"). It is used for example in metadata exchanged with a + * streaming server. + * @param pdraw: PDrAW back-end instance handle + * @param str: pointer to the string to write to (output) + * @param len: maximum length of the string + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_get_friendly_name_setting(struct pdraw_backend *self, + char *str, + size_t len); + + +/** + * Set the PDrAW instance friendly name. + * The friendly_name string is copied internally. The friendly name is generally + * either the device's friendly name (e.g. "Bob's phone") or the application's + * name (e.g. "MyDroneControllerApp"). It is used for example in metadata + * exchanged with a streaming server. Setting the friendly name value is + * optional. + * @param pdraw: PDrAW back-end instance handle + * @param friendly_name: pointer to the friendly name string + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_set_friendly_name_setting(struct pdraw_backend *self, + const char *friendly_name); + + +/** + * Get the PDrAW instance serial number. + * This function fills the str array with the null-terminated serial number. + * The string must have been previously allocated. The function writes up to + * len characters. The serial number is generally the unique serial number of + * the device on which PDrAW is running. It is used for example in metadata + * exchanged with a streaming server. + * @param pdraw: PDrAW back-end instance handle + * @param str: pointer to the string to write to (output) + * @param len: maximum length of the string + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_get_serial_number_setting(struct pdraw_backend *self, + char *str, + size_t len); + + +/** + * Set the PDrAW instance serial number. + * The serial_number string is copied internally. The serial number is generally + * the unique serial number of the device on which PDrAW is running. It is used + * for example in metadata exchanged with a streaming server. Setting the serial + * number value is recommended as it is used as a unique identifier in the + * streaming protocols. + * @param pdraw: PDrAW back-end instance handle + * @param serial_number: pointer to the serial number string + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_set_serial_number_setting(struct pdraw_backend *self, + const char *serial_number); + + +/** + * Get the PDrAW instance software version. + * This function fills the str array with the null-terminated software version. + * The string must have been previously allocated. The function writes up to + * len characters. The software version is generally the version number of the + * application running PDrAW (e.g. "MyApp v1.2.3"). It is used for example in + * metadata exchanged with a streaming server. + * @param pdraw: PDrAW back-end instance handle + * @param str: pointer to the string to write to (output) + * @param len: maximum length of the string + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_get_software_version_setting(struct pdraw_backend *self, + char *str, + size_t len); + + +/** + * Set the PDrAW instance software version. + * The software_version string is copied internally. The software version is + * generally the version number of the application running PDrAW (e.g. + * "MyApp v1.2.3"). It is used for example in metadata exchanged with a + * streaming server. Setting the software version value is optional. + * @param pdraw: PDrAW back-end instance handle + * @param software_version: pointer to the software version string + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_set_software_version_setting(struct pdraw_backend *self, + const char *software_version); + + +/** + * Get the pipeline mode setting. + * This function returns the pipeline mode of a PDrAW instance. The pipeline + * mode controls whether to decode the selected video media (for full processing + * up to the rendering), or to disable video decoding (e.g. when no rendering + * is required, only a coded video sink). + * @param pdraw: PDrAW back-end instance handle + * @return the pipeline mode, or PDRAW_PIPELINE_MODE_DECODE_ALL in case of error + */ +PDRAW_BACKEND_API enum pdraw_pipeline_mode +pdraw_be_get_pipeline_mode_setting(struct pdraw_backend *self); + + +/** + * Set the pipeline mode setting. + * This function sets the pipeline mode of a PDrAW instance. This function can + * be called only prior to any open operation. The pipeline mode controls + * whether to decode the selected video media (for full processing up to the + * rendering), or to disable video decoding (e.g. when no rendering is required, + * only a coded video sink). + * @param pdraw: PDrAW back-end instance handle + * @param mode: pipeline mode + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_set_pipeline_mode_setting(struct pdraw_backend *self, + enum pdraw_pipeline_mode mode); + + +/** + * Get the display screen settings. + * This function returns the display screen settings through the xdpi, ydpi and + * device_margin_* parameters. This is only useful if HMD distortion correction + * is enabled in the video rendering. The xdpi and ydpi are pixel densities in + * dots per inches. The device margins are in millimeters. + * @param pdraw: PDrAW back-end instance handle + * @param xdpi: horizontal pixel density (output) + * @param ydpi: vertical pixel density (output) + * @param device_margin_top: top device margin (output) + * @param device_margin_bottom: bottom device margin (output) + * @param device_margin_left: left device margin (output) + * @param device_margin_right: right device margin (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_get_display_screen_settings(struct pdraw_backend *self, + float *xdpi, + float *ydpi, + float *device_margin_top, + float *device_margin_bottom, + float *device_margin_left, + float *device_margin_right); + + +/** + * Set the display screen settings. + * This function sets the display screen settings. This is only useful if HMD + * distortion correction is enabled in the video rendering. The xdpi and ydpi + * are pixel densities in dots per inches. The device margins are in + * millimeters. + * @param pdraw: PDrAW back-end instance handle + * @param xdpi: horizontal pixel density + * @param ydpi: vertical pixel density + * @param device_margin_top: top device margin + * @param device_margin_bottom: bottom device margin + * @param device_margin_left: left device margin + * @param device_margin_right: right device margin + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_set_display_screen_settings(struct pdraw_backend *self, + float xdpi, + float ydpi, + float device_margin_top, + float device_margin_bottom, + float device_margin_left, + float device_margin_right); + + +/** + * Get the HMD model setting. + * This function returns the head-mounted display (HMD) model. This is only + * useful if HMD distortion correction is enabled in the video rendering. + * @param pdraw: PDrAW back-end instance handle + * @return the HMD model, or PDRAW_HMD_MODEL_UNKNOWN in case of error + */ +PDRAW_BACKEND_API enum pdraw_hmd_model +pdraw_be_get_hmd_model_setting(struct pdraw_backend *self); + + +/** + * Set the HMD model setting. + * This function sets the head-mounted display (HMD) model. This is only + * useful if HMD distortion correction is enabled in the video rendering. + * @param pdraw: PDrAW back-end instance handle + * @param hmd_model: HMD model + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int +pdraw_be_set_hmd_model_setting(struct pdraw_backend *self, + enum pdraw_hmd_model hmd_model); + + +/** + * Platform-specific API + */ + +/** + * Set the Android JVM pointer. + * This function sets the JVM pointer for internal calls to the Android SDK API. + * This is only useful on Android and is ignored on other platforms. If the + * JVM pointer is not provided on Android platforms, some features may not be + * available. + * @param pdraw: PDrAW back-end instance handle + * @param jvm: JVM pointer + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int pdraw_be_set_android_jvm(struct pdraw_backend *self, + void *jvm); + + +/** + * Debug API + */ + +/** + * Dump the current pipeline as a directed graph using the DOT file format. + * @param pdraw: PDrAW back-end instance handle + * @param file_name: DOT file to write to + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int pdraw_be_dump_pipeline(struct pdraw_backend *self, + const char *file_name); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* !_PDRAW_BACKEND_H_ */ diff --git a/libpdraw-backend/include/pdraw/pdraw_backend.hpp b/libpdraw-backend/include/pdraw/pdraw_backend.hpp index 6936e25..3353710 100644 --- a/libpdraw-backend/include/pdraw/pdraw_backend.hpp +++ b/libpdraw-backend/include/pdraw/pdraw_backend.hpp @@ -1,118 +1,118 @@ -/** - * Parrot Drones Awesome Video Viewer - * PDrAW back-end library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_BACKEND_HPP_ -#define _PDRAW_BACKEND_HPP_ - -#include - -#include - -#include -#include - -/* To be used for all public API */ -#ifdef PDRAW_BACKEND_API_EXPORTS -# ifdef _WIN32 -# define PDRAW_BACKEND_API __declspec(dllexport) -# else /* !_WIN32 */ -# define PDRAW_BACKEND_API __attribute__((visibility("default"))) -# endif /* !_WIN32 */ -#else /* !PDRAW_BACKEND_API_EXPORTS */ -# define PDRAW_BACKEND_API -#endif /* !PDRAW_BACKEND_API_EXPORTS */ - -using namespace Pdraw; - -namespace PdrawBackend { - - -/* PDrAW back-end object interface; - * see the createPdrawBackend() function for creating a PDrAW back-end instance - */ -class IPdrawBackend : public IPdraw { -public: - /** - * Destroy a PDrAW back-end instance. - * This function frees all resources associated with a PDrAW back-end - * instance. The stop() function must be called and one must wait for - * the stopResponse() listener function to be called prior to destroying - * the PDrAW back-end instance. - */ - virtual ~IPdrawBackend(void) {} - - /** - * Start a PDrAW back-end instance. - * This function must be called after the PDrAW back-end object creation - * prior to calling any other function. - * @return 0 on success, negative errno value in case of error - */ - virtual int start(void) = 0; - - /** - * Stop a PDrAW back-end instance. - * This function must be called prior to destroying the PDrAW back-end - * instance. - * @return 0 on success, negative errno value in case of error - */ - virtual int stop(void) = 0; - - /** - * Get the PDrAW back-end internal event loop. - * This function returns a pointer to the event loop used internally. - * @return a pointer on the internal loop on success, - * nullptr in case of error - */ - virtual struct pomp_loop *getLoop(void) = 0; -}; - - -/** - * Create a PDrAW back-end instance. - * All API functions can be called from any thread, with the exception of - * rendering functions which must still be called from the rendering thread. - * All listener functions with the exception of video renderer listener - * functions are called from the internal pomp_loop thread; synchronization - * is up to the application. A valid listener must be provided. The instance - * handle is returned through the retObj parameter. - * When no longer needed, the instance must be deleted to free the resources. - * The stop() function must be called and one must wait for the stopResponse() - * listener function to be called prior to destroying the instance. - * @param listener: listener functions implementation - * @param retObj: PDrAW back-end instance handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_BACKEND_API int createPdrawBackend(IPdraw::Listener *listener, - IPdrawBackend **retObj); - - -} /* namespace PdrawBackend */ - -#endif /* !_PDRAW_BACKEND_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer + * PDrAW back-end library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_BACKEND_HPP_ +#define _PDRAW_BACKEND_HPP_ + +#include + +#include + +#include +#include + +/* To be used for all public API */ +#ifdef PDRAW_BACKEND_API_EXPORTS +# ifdef _WIN32 +# define PDRAW_BACKEND_API __declspec(dllexport) +# else /* !_WIN32 */ +# define PDRAW_BACKEND_API __attribute__((visibility("default"))) +# endif /* !_WIN32 */ +#else /* !PDRAW_BACKEND_API_EXPORTS */ +# define PDRAW_BACKEND_API +#endif /* !PDRAW_BACKEND_API_EXPORTS */ + +using namespace Pdraw; + +namespace PdrawBackend { + + +/* PDrAW back-end object interface; + * see the createPdrawBackend() function for creating a PDrAW back-end instance + */ +class IPdrawBackend : public IPdraw { +public: + /** + * Destroy a PDrAW back-end instance. + * This function frees all resources associated with a PDrAW back-end + * instance. The stop() function must be called and one must wait for + * the stopResponse() listener function to be called prior to destroying + * the PDrAW back-end instance. + */ + virtual ~IPdrawBackend(void) {} + + /** + * Start a PDrAW back-end instance. + * This function must be called after the PDrAW back-end object creation + * prior to calling any other function. + * @return 0 on success, negative errno value in case of error + */ + virtual int start(void) = 0; + + /** + * Stop a PDrAW back-end instance. + * This function must be called prior to destroying the PDrAW back-end + * instance. + * @return 0 on success, negative errno value in case of error + */ + virtual int stop(void) = 0; + + /** + * Get the PDrAW back-end internal event loop. + * This function returns a pointer to the event loop used internally. + * @return a pointer on the internal loop on success, + * nullptr in case of error + */ + virtual struct pomp_loop *getLoop(void) = 0; +}; + + +/** + * Create a PDrAW back-end instance. + * All API functions can be called from any thread, with the exception of + * rendering functions which must still be called from the rendering thread. + * All listener functions with the exception of video renderer listener + * functions are called from the internal pomp_loop thread; synchronization + * is up to the application. A valid listener must be provided. The instance + * handle is returned through the retObj parameter. + * When no longer needed, the instance must be deleted to free the resources. + * The stop() function must be called and one must wait for the stopResponse() + * listener function to be called prior to destroying the instance. + * @param listener: listener functions implementation + * @param retObj: PDrAW back-end instance handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_BACKEND_API int createPdrawBackend(IPdraw::Listener *listener, + IPdrawBackend **retObj); + + +} /* namespace PdrawBackend */ + +#endif /* !_PDRAW_BACKEND_HPP_ */ diff --git a/libpdraw-backend/src/pdraw_backend_impl.cpp b/libpdraw-backend/src/pdraw_backend_impl.cpp index 6debaa2..71b831a 100644 --- a/libpdraw-backend/src/pdraw_backend_impl.cpp +++ b/libpdraw-backend/src/pdraw_backend_impl.cpp @@ -1,4959 +1,4959 @@ -/** - * Parrot Drones Awesome Video Viewer - * PDrAW back-end library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include - -#define ULOG_TAG pdraw_backend -#include -ULOG_DECLARE_TAG(pdraw_backend); - -#include "pdraw_backend_impl.hpp" - -#ifdef _WIN32 -# define PIPE_BUF 4096 -#endif /* _WIN32 */ - -namespace PdrawBackend { - - -#define PDRAW_STATIC_ASSERT(x) typedef char __STATIC_ASSERT__[(x) ? 1 : -1] - - -enum cmd_type { - CMD_TYPE_STOP, - CMD_TYPE_DEMUXER_CREATE_SINGLE, - CMD_TYPE_DEMUXER_CREATE_URL, - CMD_TYPE_DEMUXER_CREATE_URL_MUX, - CMD_TYPE_DEMUXER_DESTROY, - CMD_TYPE_DEMUXER_CLOSE, - CMD_TYPE_DEMUXER_GET_SINGLE_STREAM_LOCAL_STREAM_PORT, - CMD_TYPE_DEMUXER_GET_SINGLE_STREAM_LOCAL_CONTROL_PORT, - CMD_TYPE_DEMUXER_IS_READY_TO_PLAY, - CMD_TYPE_DEMUXER_IS_PAUSED, - CMD_TYPE_DEMUXER_PLAY, - CMD_TYPE_DEMUXER_PREVIOUS_FRAME, - CMD_TYPE_DEMUXER_NEXT_FRAME, - CMD_TYPE_DEMUXER_SEEK, - CMD_TYPE_DEMUXER_SEEK_TO, - CMD_TYPE_DEMUXER_GET_CURRENT_TIME, - CMD_TYPE_DEMUXER_GET_DURATION, - CMD_TYPE_MUXER_CREATE, - CMD_TYPE_MUXER_DESTROY, - CMD_TYPE_MUXER_ADD_MEDIA, - CMD_TYPE_CODED_VIDEO_SINK_CREATE, - CMD_TYPE_CODED_VIDEO_SINK_DESTROY, - CMD_TYPE_CODED_VIDEO_SINK_RESYNC, - CMD_TYPE_CODED_VIDEO_SINK_GET_QUEUE, - CMD_TYPE_CODED_VIDEO_SINK_QUEUE_FLUSHED, - CMD_TYPE_RAW_VIDEO_SINK_CREATE, - CMD_TYPE_RAW_VIDEO_SINK_DESTROY, - CMD_TYPE_RAW_VIDEO_SINK_GET_QUEUE, - CMD_TYPE_RAW_VIDEO_SINK_QUEUE_FLUSHED, - CMD_TYPE_GET_FRIENDLY_NAME_SETTING, - CMD_TYPE_SET_FRIENDLY_NAME_SETTING, - CMD_TYPE_GET_SERIAL_NUMBER_SETTING, - CMD_TYPE_SET_SERIAL_NUMBER_SETTING, - CMD_TYPE_GET_SOFTWARE_VERSION_SETTING, - CMD_TYPE_SET_SOFTWARE_VERSION_SETTING, - CMD_TYPE_GET_PIPELINE_MODE_SETTING, - CMD_TYPE_SET_PIPELINE_MODE_SETTING, - CMD_TYPE_GET_DISPLAY_SCREEN_SETTINGS, - CMD_TYPE_SET_DISPLAY_SCREEN_SETTINGS, - CMD_TYPE_GET_HMD_MODEL_SETTING, - CMD_TYPE_SET_HMD_MODEL_SETTING, - CMD_TYPE_SET_ANDROID_JVM, - CMD_TYPE_DUMP_PIPELINE, -}; - - -struct cmd_msg { - enum cmd_type type; - union { - struct { - union { - bool boolean; - unsigned int uint; - uint64_t uint64; - int64_t int64; - float flt; - void *ptr; - char string[40]; - }; - } generic; - struct { - char local_addr[16]; - uint16_t local_stream_port; - uint16_t local_control_port; - char remote_addr[16]; - uint16_t remote_stream_port; - uint16_t remote_control_port; - IPdraw::IDemuxer::Listener *listener; - } demuxer_create_single; - struct { - char url[200]; - IPdraw::IDemuxer::Listener *listener; - } demuxer_create_url; - struct { - char url[200]; - struct mux_ctx *mux; - IPdraw::IDemuxer::Listener *listener; - } demuxer_create_url_mux; - struct { - PdrawBackend::Demuxer *demuxer; - float speed; - } demuxer_play; - struct { - PdrawBackend::Demuxer *demuxer; - int64_t delta; - bool exact; - } demuxer_seek; - struct { - PdrawBackend::Demuxer *demuxer; - uint64_t timestamp; - bool exact; - } demuxer_seek_to; - struct { - unsigned int media_id; - struct pdraw_video_sink_params params; - IPdraw::ICodedVideoSink::Listener *listener; - } coded_video_sink_create; - struct { - unsigned int media_id; - struct pdraw_video_sink_params params; - IPdraw::IRawVideoSink::Listener *listener; - } raw_video_sink_create; - struct { - char url[200]; - } muxer_create; - struct { - IPdraw::IMuxer *muxer; - unsigned int media_id; - struct pdraw_muxer_video_media_params params; - } muxer_add_media; - struct { - float xdpi; - float ydpi; - float device_margin_top; - float device_margin_bottom; - float device_margin_left; - float device_margin_right; - } set_display_screen_settings; - struct { - char file_name[200]; - } dump_pipeline; - }; -}; -PDRAW_STATIC_ASSERT(sizeof(struct cmd_msg) <= PIPE_BUF - 1); - - -/** - * Public functions - */ - -int createPdrawBackend(IPdraw::Listener *listener, IPdrawBackend **retObj) -{ - IPdrawBackend *self = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(listener == nullptr, EINVAL); - - self = new PdrawBackend(listener); - if (self == nullptr) { - ULOGE("failed to create pdraw backend instance"); - return -ENOMEM; - } - - *retObj = self; - return 0; -} - - -PdrawBackend::PdrawBackend(IPdraw::Listener *listener) -{ - mApiMutexCreated = false; - mApiReady = false; - mMutexCreated = false; - mCondCreated = false; - mLoopThreadLaunched = false; - mThreadShouldStop = false; - mLoop = nullptr; - mMbox = nullptr; - mStarted = false; - mRetValReady = false; - mRetStatus = 0; - mRetBool = false; - mRetUint16 = 0; - mRetUint64 = 0; - memset(mRetFloat, 0, sizeof(mRetFloat)); - mRetPipelineMode = PDRAW_PIPELINE_MODE_DECODE_ALL; - mRetHmdModel = PDRAW_HMD_MODEL_UNKNOWN; - mRetCodedQueue = nullptr; - mRetRawQueue = nullptr; - memset(&mRetMediaInfo, 0, sizeof(mRetMediaInfo)); - mRetCodedVideoSink = nullptr; - mRetRawVideoSink = nullptr; - mRetDemuxer = nullptr; - mRetMuxer = nullptr; - mPdraw = nullptr; - mListener = listener; - mMapsMutexCreated = false; - mPendingDemuxerAndListener = {}; - mPendingVideoRendererAndListener = {}; - mPendingCodedVideoSinkAndListener = {}; - mPendingRawVideoSinkAndListener = {}; - memset(&mLoopThread, 0, sizeof(pthread_t)); -} - - -PdrawBackend::~PdrawBackend(void) -{ - int err; - - if (mStarted) - ULOGW("destroying pdraw backend while still running"); - - mThreadShouldStop = true; - if (mLoop != nullptr) { - err = pomp_loop_wakeup(mLoop); - if (err < 0) - ULOG_ERRNO("pomp_loop_wakeup", -err); - } - if (mLoopThreadLaunched) { - err = pthread_join(mLoopThread, nullptr); - if (err != 0) - ULOG_ERRNO("pthread_join", err); - mLoopThreadLaunched = false; - } - if (mPdraw != nullptr) { - delete mPdraw; - mPdraw = nullptr; - } - if (mLoop != nullptr) { - err = pomp_loop_destroy(mLoop); - if (err < 0) - ULOG_ERRNO("pomp_loop_destroy", -err); - mLoop = nullptr; - } - mStarted = false; - if (mMbox != nullptr) { - mbox_destroy(mMbox); - mMbox = nullptr; - } - if (mCondCreated) { - err = pthread_cond_destroy(&mCond); - if (err != 0) - ULOG_ERRNO("pthread_cond_destroy", err); - mCondCreated = false; - } - if (mMutexCreated) { - err = pthread_mutex_destroy(&mMutex); - if (err != 0) - ULOG_ERRNO("pthread_mutex_destroy", err); - mMutexCreated = false; - } - if (mApiMutexCreated) { - err = pthread_mutex_destroy(&mApiMutex); - if (err != 0) - ULOG_ERRNO("pthread_mutex_destroy", err); - mApiMutexCreated = false; - } - if (mMapsMutexCreated) { - err = pthread_mutex_destroy(&mMapsMutex); - if (err != 0) - ULOG_ERRNO("pthread_mutex_destroy", err); - mMapsMutexCreated = false; - } -} - - -int PdrawBackend::start(void) -{ - int res; - pthread_mutexattr_t attr; - - ULOG_ERRNO_RETURN_ERR_IF(mListener == nullptr, EPROTO); - - res = pthread_mutexattr_init(&attr); - if (res != 0) { - ULOG_ERRNO("pthread_mutexattr_init", res); - return -res; - } - - res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - if (res != 0) { - pthread_mutexattr_destroy(&attr); - ULOG_ERRNO("pthread_mutexattr_settype", res); - return -res; - } - - res = pthread_mutex_init(&mApiMutex, &attr); - if (res != 0) { - ULOG_ERRNO("pthread_mutex_init", res); - return -res; - } - mApiMutexCreated = true; - pthread_mutexattr_destroy(&attr); - - res = pthread_mutex_init(&mMutex, nullptr); - if (res != 0) { - ULOG_ERRNO("pthread_mutex_init", res); - return -res; - } - mMutexCreated = true; - - res = pthread_cond_init(&mCond, nullptr); - if (res != 0) { - ULOG_ERRNO("pthread_cond_init", res); - return -res; - } - mCondCreated = true; - - res = pthread_mutex_init(&mMapsMutex, nullptr); - if (res != 0) { - ULOG_ERRNO("pthread_mutex_init", res); - return -res; - } - mMapsMutexCreated = true; - - mMbox = mbox_new(sizeof(cmd_msg)); - if (mMbox == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("mbox_new", -res); - return res; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - res = pthread_create(&mLoopThread, nullptr, &loopThread, (void *)this); - if (res != 0) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("pthread_create", res); - return -res; - } - mLoopThreadLaunched = true; - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - res = mRetStatus; - mRetStatus = 0; - mRetValReady = false; - mStarted = true; - mApiReady = true; - pthread_mutex_unlock(&mMutex); - - return res; -} - - -int PdrawBackend::stop(void) -{ - int res; - struct cmd_msg *cmd = nullptr; - - if (!mStarted) - return 0; - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - res = mPdraw->stop(); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_STOP; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - res = mRetStatus; - mRetStatus = false; - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - mStarted = false; - pthread_mutex_unlock(&mApiMutex); - return res; -} - - -struct pomp_loop *PdrawBackend::getLoop(void) -{ - ULOG_ERRNO_RETURN_VAL_IF(!mStarted, EPROTO, nullptr); - - return mLoop; -} - - -int PdrawBackend::createDemuxer(const std::string &url, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF( - url.length() > sizeof(cmd->demuxer_create_url.url) - 1, - ENOBUFS); - ULOG_ERRNO_RETURN_ERR_IF(!mStarted, EPROTO); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - res = doCreateDemuxer(url, listener, retObj); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_DEMUXER_CREATE_URL; - strncpy(cmd->demuxer_create_url.url, - url.c_str(), - sizeof(cmd->demuxer_create_url.url)); - cmd->demuxer_create_url.url[sizeof(cmd->demuxer_create_url.url) - 1] = - '\0'; - cmd->demuxer_create_url.listener = listener; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - res = mRetStatus; - *retObj = mRetDemuxer; - mRetStatus = 0; - mRetDemuxer = nullptr; - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); - return res; -} - - -int PdrawBackend::createDemuxer(const std::string &localAddr, - uint16_t localStreamPort, - uint16_t localControlPort, - const std::string &remoteAddr, - uint16_t remoteStreamPort, - uint16_t remoteControlPort, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF( - localAddr.length() > - sizeof(cmd->demuxer_create_single.local_addr) - 1, - ENOBUFS); - ULOG_ERRNO_RETURN_ERR_IF( - remoteAddr.length() > - sizeof(cmd->demuxer_create_single.remote_addr) - 1, - ENOBUFS); - ULOG_ERRNO_RETURN_ERR_IF(!mStarted, EPROTO); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - res = doCreateDemuxer(localAddr, - localStreamPort, - localControlPort, - remoteAddr, - remoteStreamPort, - remoteControlPort, - listener, - retObj); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_DEMUXER_CREATE_SINGLE; - strncpy(cmd->demuxer_create_single.local_addr, - localAddr.c_str(), - sizeof(cmd->demuxer_create_single.local_addr)); - cmd->demuxer_create_single - .local_addr[sizeof(cmd->demuxer_create_single.local_addr) - 1] = - '\0'; - cmd->demuxer_create_single.local_stream_port = localStreamPort; - cmd->demuxer_create_single.local_control_port = localControlPort; - strncpy(cmd->demuxer_create_single.remote_addr, - remoteAddr.c_str(), - sizeof(cmd->demuxer_create_single.remote_addr)); - cmd->demuxer_create_single - .remote_addr[sizeof(cmd->demuxer_create_single.remote_addr) - - 1] = '\0'; - cmd->demuxer_create_single.remote_stream_port = remoteStreamPort; - cmd->demuxer_create_single.remote_control_port = remoteControlPort; - cmd->demuxer_create_single.listener = listener; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - res = mRetStatus; - *retObj = mRetDemuxer; - mRetStatus = 0; - mRetDemuxer = nullptr; - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); - return res; -} - - -int PdrawBackend::createDemuxer(const std::string &url, - struct mux_ctx *mux, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF( - url.length() > sizeof(cmd->demuxer_create_url_mux.url) - 1, - ENOBUFS); - ULOG_ERRNO_RETURN_ERR_IF(!mStarted, EPROTO); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - res = doCreateDemuxer(url, mux, listener, retObj); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_DEMUXER_CREATE_URL_MUX; - strncpy(cmd->demuxer_create_url_mux.url, - url.c_str(), - sizeof(cmd->demuxer_create_url_mux.url)); - cmd->demuxer_create_url_mux - .url[sizeof(cmd->demuxer_create_url_mux.url) - 1] = '\0'; - cmd->demuxer_create_url_mux.mux = mux; - cmd->demuxer_create_url_mux.listener = listener; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - res = mRetStatus; - *retObj = mRetDemuxer; - mRetStatus = 0; - mRetDemuxer = nullptr; - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); - return res; -} - - -PdrawBackend::Demuxer::~Demuxer(void) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_IF(mDemuxer == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - delete mDemuxer; - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_DEMUXER_DESTROY; - cmd->generic.ptr = this; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); -} - - -int PdrawBackend::Demuxer::close(void) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - res = mDemuxer->close(); - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_DEMUXER_CLOSE; - cmd->generic.ptr = this; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - res = mBackend->mRetStatus; - mBackend->mRetStatus = 0; - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); - return res; -} - - -uint16_t PdrawBackend::Demuxer::getSingleStreamLocalStreamPort(void) -{ - int res; - uint16_t ret = 0; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_VAL_IF(mBackend == nullptr, EPROTO, 0); - ULOG_ERRNO_RETURN_VAL_IF(!mBackend->mStarted, EPROTO, 0); - ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EPROTO, 0); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - ret = mDemuxer->getSingleStreamLocalStreamPort(); - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_DEMUXER_GET_SINGLE_STREAM_LOCAL_STREAM_PORT; - cmd->generic.ptr = this; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - ret = mBackend->mRetUint16; - mBackend->mRetUint16 = 0; - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); - return ret; -} - - -uint16_t PdrawBackend::Demuxer::getSingleStreamLocalControlPort(void) -{ - int res; - uint16_t ret = 0; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_VAL_IF(mBackend == nullptr, EPROTO, 0); - ULOG_ERRNO_RETURN_VAL_IF(!mBackend->mStarted, EPROTO, 0); - ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EPROTO, 0); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - ret = mDemuxer->getSingleStreamLocalControlPort(); - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_DEMUXER_GET_SINGLE_STREAM_LOCAL_CONTROL_PORT; - cmd->generic.ptr = this; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - ret = mBackend->mRetUint16; - mBackend->mRetUint16 = 0; - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); - return ret; -} - - -bool PdrawBackend::Demuxer::isReadyToPlay(void) -{ - int res; - bool ret = false; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_VAL_IF(mBackend == nullptr, EPROTO, false); - ULOG_ERRNO_RETURN_VAL_IF(!mBackend->mStarted, EPROTO, false); - ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EPROTO, false); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - ret = mDemuxer->isReadyToPlay(); - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_DEMUXER_IS_READY_TO_PLAY; - cmd->generic.ptr = this; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - ret = mBackend->mRetBool; - mBackend->mRetBool = false; - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); - return ret; -} - - -bool PdrawBackend::Demuxer::isPaused(void) -{ - int res; - bool ret = false; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_VAL_IF(mBackend == nullptr, EPROTO, false); - ULOG_ERRNO_RETURN_VAL_IF(!mBackend->mStarted, EPROTO, false); - ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EPROTO, false); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - ret = mDemuxer->isPaused(); - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_DEMUXER_IS_PAUSED; - cmd->generic.ptr = this; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - ret = mBackend->mRetBool; - mBackend->mRetBool = false; - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); - return ret; -} - - -int PdrawBackend::Demuxer::play(float speed) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - res = mDemuxer->play(speed); - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_DEMUXER_PLAY; - cmd->demuxer_play.demuxer = this; - cmd->demuxer_play.speed = speed; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - res = mBackend->mRetStatus; - mBackend->mRetStatus = 0; - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); - return res; -} - - -int PdrawBackend::Demuxer::pause(void) -{ - return play(0.); -} - - -int PdrawBackend::Demuxer::previousFrame(void) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - res = mDemuxer->previousFrame(); - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_DEMUXER_PREVIOUS_FRAME; - cmd->generic.ptr = this; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - res = mBackend->mRetStatus; - mBackend->mRetStatus = 0; - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); - return res; -} - - -int PdrawBackend::Demuxer::nextFrame(void) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - res = mDemuxer->nextFrame(); - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_DEMUXER_NEXT_FRAME; - cmd->generic.ptr = this; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - res = mBackend->mRetStatus; - mBackend->mRetStatus = 0; - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); - return res; -} - - -int PdrawBackend::Demuxer::seek(int64_t delta, bool exact) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - res = mDemuxer->seek(delta, exact); - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_DEMUXER_SEEK; - cmd->demuxer_seek.demuxer = this; - cmd->demuxer_seek.delta = delta; - cmd->demuxer_seek.exact = exact; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - res = mBackend->mRetStatus; - mBackend->mRetStatus = 0; - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); - return res; -} - - -int PdrawBackend::Demuxer::seekForward(uint64_t delta, bool exact) -{ - return seek((int64_t)delta); -} - - -int PdrawBackend::Demuxer::seekBack(uint64_t delta, bool exact) -{ - return seek(-((int64_t)delta)); -} - - -int PdrawBackend::Demuxer::seekTo(uint64_t timestamp, bool exact) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - res = mDemuxer->seekTo(timestamp, exact); - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_DEMUXER_SEEK_TO; - cmd->demuxer_seek_to.demuxer = this; - cmd->demuxer_seek_to.timestamp = timestamp; - cmd->demuxer_seek_to.exact = exact; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - res = mBackend->mRetStatus; - mBackend->mRetStatus = 0; - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); - return res; -} - - -uint64_t PdrawBackend::Demuxer::getDuration(void) -{ - int res; - uint64_t ret = 0; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_VAL_IF(mBackend == nullptr, EPROTO, 0); - ULOG_ERRNO_RETURN_VAL_IF(!mBackend->mStarted, EPROTO, 0); - ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EPROTO, 0); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - ret = mDemuxer->getDuration(); - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_DEMUXER_GET_DURATION; - cmd->generic.ptr = this; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - ret = mBackend->mRetUint64; - mBackend->mRetUint64 = 0; - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); - return ret; -} - - -uint64_t PdrawBackend::Demuxer::getCurrentTime(void) -{ - int res; - uint64_t ret = 0; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_VAL_IF(mBackend == nullptr, EPROTO, 0); - ULOG_ERRNO_RETURN_VAL_IF(!mBackend->mStarted, EPROTO, 0); - ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EPROTO, 0); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - ret = mDemuxer->getCurrentTime(); - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_DEMUXER_GET_CURRENT_TIME; - cmd->generic.ptr = this; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - ret = mBackend->mRetUint64; - mBackend->mRetUint64 = 0; - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); - return ret; -} - - -int PdrawBackend::createMuxer(const std::string &url, IPdraw::IMuxer **retObj) -{ - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF( - url.length() > sizeof(cmd->muxer_create.url) - 1, ENOBUFS); - ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(!mStarted, EPROTO); - -#if MUXER_TEST - int res; - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - res = doCreateMuxer(url, retObj); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_MUXER_CREATE; - strncpy(cmd->muxer_create.url, - url.c_str(), - sizeof(cmd->muxer_create.url)); - cmd->muxer_create.url[sizeof(cmd->muxer_create.url) - 1] = '\0'; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - res = mRetStatus; - *retObj = mRetMuxer; - mRetStatus = 0; - mRetMuxer = nullptr; - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); - return res; - -#else - /* Not supported yet */ - return -ENOSYS; -#endif -} - - -PdrawBackend::Muxer::Muxer(PdrawBackend *backend, const std::string &url) -{ - mBackend = backend; - mMuxer = nullptr; -} - - -PdrawBackend::Muxer::~Muxer(void) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_IF(mMuxer == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - delete mMuxer; - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_MUXER_DESTROY; - cmd->generic.ptr = this; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); -} - - -int PdrawBackend::Muxer::addMedia( - unsigned int mediaId, - const struct pdraw_muxer_video_media_params *params) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(mMuxer == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - res = mMuxer->addMedia(mediaId, params); - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_MUXER_ADD_MEDIA; - cmd->muxer_add_media.muxer = this; - cmd->muxer_add_media.media_id = mediaId; - if (params != nullptr) - cmd->muxer_add_media.params = *params; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - res = mBackend->mRetStatus; - mBackend->mRetStatus = 0; - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); - return res; -} - - -/* Called on the rendering thread */ -int PdrawBackend::createVideoRenderer( - unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - IPdraw::IVideoRenderer::Listener *listener, - IPdraw::IVideoRenderer **retObj, - struct egl_display *eglDisplay) -{ - int res = 0; - PdrawBackend::VideoRenderer *renderer = nullptr; - IPdraw::IVideoRenderer *rnd = nullptr; - std::pair::iterator, - bool> - inserted; - - ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(!mStarted, EPROTO); - - pthread_mutex_lock(&mApiMutex); - - renderer = new PdrawBackend::VideoRenderer( - this, renderPos, params, listener, eglDisplay); - if (renderer == nullptr) - goto out; - - mPendingVideoRendererAndListener = { - .r = renderer, - .l = listener, - }; - - res = mPdraw->createVideoRenderer( - mediaId, renderPos, params, this, &rnd, eglDisplay); - if (res < 0) { - ULOG_ERRNO("pdraw->createVideoRenderer", -res); - delete renderer; - renderer = nullptr; - goto out; - } - renderer->setRenderer(rnd); - - pthread_mutex_lock(&mMapsMutex); - inserted = mVideoRendererListenersMap.insert( - std::pair( - renderer->getRenderer(), - mPendingVideoRendererAndListener)); - if (inserted.second == false) { - ULOGW("failed to insert the video renderer listener " - "in the map"); - } - pthread_mutex_unlock(&mMapsMutex); - -out: - mPendingVideoRendererAndListener = {}; - *retObj = renderer; - - pthread_mutex_unlock(&mApiMutex); - - return res; -} - - -/* Called on the rendering thread */ -PdrawBackend::VideoRenderer::VideoRenderer( - PdrawBackend *backend, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - IPdraw::IVideoRenderer::Listener *listener, - struct egl_display *eglDisplay) -{ - mBackend = backend; - mListener = listener; - mRenderer = nullptr; -} - - -/* Called on the rendering thread */ -PdrawBackend::VideoRenderer::~VideoRenderer(void) -{ - size_t erased; - - ULOG_ERRNO_RETURN_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_IF(!mBackend->mStarted, EPROTO); - - if (mRenderer == nullptr) - return; - - pthread_mutex_lock(&mBackend->mApiMutex); - - pthread_mutex_lock(&mBackend->mMapsMutex); - erased = mBackend->mVideoRendererListenersMap.erase(mRenderer); - if (erased != 1) { - ULOGW("failed to erase the video renderer listener " - "from the map"); - } - pthread_mutex_unlock(&mBackend->mMapsMutex); - - delete mRenderer; - mRenderer = nullptr; - - pthread_mutex_unlock(&mBackend->mApiMutex); -} - - -/* Called on the rendering thread */ -int PdrawBackend::VideoRenderer::resize(const struct pdraw_rect *renderPos) -{ - int res; - - ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(mRenderer == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - res = mRenderer->resize(renderPos); - - pthread_mutex_unlock(&mBackend->mApiMutex); - - return res; -} - - -/* Called on the rendering thread */ -int PdrawBackend::VideoRenderer::setMediaId(unsigned int mediaId) -{ - int res; - - ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(mRenderer == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - res = mRenderer->setMediaId(mediaId); - - pthread_mutex_unlock(&mBackend->mApiMutex); - - return res; -} - - -/* Called on the rendering thread */ -unsigned int PdrawBackend::VideoRenderer::getMediaId(void) -{ - unsigned int res; - - ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(mRenderer == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - res = mRenderer->getMediaId(); - - pthread_mutex_unlock(&mBackend->mApiMutex); - - return res; -} - - -/* Called on the rendering thread */ -int PdrawBackend::VideoRenderer::setParams( - const struct pdraw_video_renderer_params *params) -{ - int res; - - ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(mRenderer == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - res = mRenderer->setParams(params); - - pthread_mutex_unlock(&mBackend->mApiMutex); - - return res; -} - - -/* Called on the rendering thread */ -int PdrawBackend::VideoRenderer::getParams( - struct pdraw_video_renderer_params *params) -{ - int res; - - ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(mRenderer == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - res = mRenderer->getParams(params); - - pthread_mutex_unlock(&mBackend->mApiMutex); - - return res; -} - - -/* Called on the rendering thread */ -int PdrawBackend::VideoRenderer::render(struct pdraw_rect *contentPos, - const float *viewMat, - const float *projMat) -{ - int res; - - ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(mRenderer == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - res = mRenderer->render(contentPos, viewMat, projMat); - - pthread_mutex_unlock(&mBackend->mApiMutex); - - return res; -} - - -int PdrawBackend::createCodedVideoSink( - unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::ICodedVideoSink::Listener *listener, - IPdraw::ICodedVideoSink **retObj) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(!mStarted, EPROTO); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - res = doCreateCodedVideoSink(mediaId, params, listener, retObj); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_CODED_VIDEO_SINK_CREATE; - cmd->coded_video_sink_create.media_id = mediaId; - cmd->coded_video_sink_create.params = *params; - cmd->coded_video_sink_create.listener = listener; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - res = mRetStatus; - *retObj = mRetCodedVideoSink; - mRetStatus = 0; - mRetCodedVideoSink = nullptr; - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); - return res; -} - - -PdrawBackend::CodedVideoSink::CodedVideoSink( - PdrawBackend *backend, - unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::ICodedVideoSink::Listener *listener) -{ - mBackend = backend; - mListener = listener; - mSink = nullptr; -} - - -PdrawBackend::CodedVideoSink::~CodedVideoSink(void) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_IF(mSink == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - delete mSink; - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_CODED_VIDEO_SINK_DESTROY; - cmd->generic.ptr = this; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); -} - - -int PdrawBackend::CodedVideoSink::resync(void) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(mSink == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - res = mSink->resync(); - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_CODED_VIDEO_SINK_RESYNC; - cmd->generic.ptr = this; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - res = mBackend->mRetStatus; - mBackend->mRetStatus = 0; - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); - return res; -} - - -struct mbuf_coded_video_frame_queue * -PdrawBackend::CodedVideoSink::getQueue(void) -{ - int res; - struct mbuf_coded_video_frame_queue *ret = nullptr; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_VAL_IF(mBackend == nullptr, EPROTO, nullptr); - ULOG_ERRNO_RETURN_VAL_IF(!mBackend->mStarted, EPROTO, nullptr); - ULOG_ERRNO_RETURN_VAL_IF(mSink == nullptr, EPROTO, nullptr); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - ret = mSink->getQueue(); - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_CODED_VIDEO_SINK_GET_QUEUE; - cmd->generic.ptr = this; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - ret = mBackend->mRetCodedQueue; - mBackend->mRetCodedQueue = 0; - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); - return ret; -} - - -int PdrawBackend::CodedVideoSink::queueFlushed(void) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(mSink == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - res = mSink->queueFlushed(); - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_CODED_VIDEO_SINK_QUEUE_FLUSHED; - cmd->generic.ptr = this; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - res = mBackend->mRetStatus; - mBackend->mRetStatus = 0; - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); - return res; -} - - -int PdrawBackend::createRawVideoSink( - unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::IRawVideoSink::Listener *listener, - IPdraw::IRawVideoSink **retObj) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(!mStarted, EPROTO); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - res = doCreateRawVideoSink(mediaId, params, listener, retObj); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_RAW_VIDEO_SINK_CREATE; - cmd->raw_video_sink_create.media_id = mediaId; - cmd->raw_video_sink_create.params = *params; - cmd->raw_video_sink_create.listener = listener; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - res = mRetStatus; - *retObj = mRetRawVideoSink; - mRetStatus = 0; - mRetRawVideoSink = nullptr; - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); - return res; -} - - -PdrawBackend::RawVideoSink::RawVideoSink( - PdrawBackend *backend, - unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::IRawVideoSink::Listener *listener) -{ - mBackend = backend; - mListener = listener; - mSink = nullptr; -} - - -PdrawBackend::RawVideoSink::~RawVideoSink(void) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_IF(mSink == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - delete mSink; - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_RAW_VIDEO_SINK_DESTROY; - cmd->generic.ptr = this; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); -} - - -struct mbuf_raw_video_frame_queue *PdrawBackend::RawVideoSink::getQueue(void) -{ - int res; - struct mbuf_raw_video_frame_queue *ret = nullptr; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_VAL_IF(mBackend == nullptr, EPROTO, nullptr); - ULOG_ERRNO_RETURN_VAL_IF(!mBackend->mStarted, EPROTO, nullptr); - ULOG_ERRNO_RETURN_VAL_IF(mSink == nullptr, EPROTO, nullptr); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - ret = mSink->getQueue(); - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_RAW_VIDEO_SINK_GET_QUEUE; - cmd->generic.ptr = this; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - ret = mBackend->mRetRawQueue; - mBackend->mRetRawQueue = 0; - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); - return ret; -} - - -int PdrawBackend::RawVideoSink::queueFlushed(void) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); - ULOG_ERRNO_RETURN_ERR_IF(mSink == nullptr, EPROTO); - - pthread_mutex_lock(&mBackend->mApiMutex); - - if (pthread_self() == mBackend->mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - res = mSink->queueFlushed(); - goto out2; - } - - pthread_mutex_lock(&mBackend->mMutex); - mBackend->mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_RAW_VIDEO_SINK_QUEUE_FLUSHED; - cmd->generic.ptr = this; - res = mbox_push(mBackend->mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mBackend->mRetValReady) - pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); - - res = mBackend->mRetStatus; - mBackend->mRetStatus = 0; - mBackend->mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mBackend->mMutex); - -out2: - pthread_mutex_unlock(&mBackend->mApiMutex); - return res; -} - - -void PdrawBackend::getFriendlyNameSetting(std::string *friendlyName) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - mPdraw->getFriendlyNameSetting(friendlyName); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_GET_FRIENDLY_NAME_SETTING; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - if (friendlyName) - *friendlyName = mRetString; - mRetString = ""; - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); -} - - -void PdrawBackend::setFriendlyNameSetting(const std::string &friendlyName) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); - ULOG_ERRNO_RETURN_IF(friendlyName.length() > - sizeof(cmd->generic.string) - 1, - ENOBUFS); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - mPdraw->setFriendlyNameSetting(friendlyName); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_SET_FRIENDLY_NAME_SETTING; - strncpy(cmd->generic.string, - friendlyName.c_str(), - sizeof(cmd->generic.string)); - cmd->generic.string[sizeof(cmd->generic.string) - 1] = '\0'; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); -} - - -void PdrawBackend::getSerialNumberSetting(std::string *serialNumber) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - mPdraw->getSerialNumberSetting(serialNumber); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_GET_SERIAL_NUMBER_SETTING; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - if (serialNumber) - *serialNumber = mRetString; - mRetString = ""; - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); -} - - -void PdrawBackend::setSerialNumberSetting(const std::string &serialNumber) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); - ULOG_ERRNO_RETURN_IF(serialNumber.length() > - sizeof(cmd->generic.string) - 1, - ENOBUFS); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - mPdraw->setSerialNumberSetting(serialNumber); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_SET_SERIAL_NUMBER_SETTING; - strncpy(cmd->generic.string, - serialNumber.c_str(), - sizeof(cmd->generic.string)); - cmd->generic.string[sizeof(cmd->generic.string) - 1] = '\0'; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); -} - - -void PdrawBackend::getSoftwareVersionSetting(std::string *softwareVersion) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - mPdraw->getSoftwareVersionSetting(softwareVersion); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_GET_SOFTWARE_VERSION_SETTING; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - if (softwareVersion) - *softwareVersion = mRetString; - mRetString = ""; - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); -} - - -void PdrawBackend::setSoftwareVersionSetting(const std::string &softwareVersion) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); - ULOG_ERRNO_RETURN_IF(softwareVersion.length() > - sizeof(cmd->generic.string) - 1, - ENOBUFS); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - mPdraw->setSoftwareVersionSetting(softwareVersion); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_SET_SOFTWARE_VERSION_SETTING; - strncpy(cmd->generic.string, - softwareVersion.c_str(), - sizeof(cmd->generic.string)); - cmd->generic.string[sizeof(cmd->generic.string) - 1] = '\0'; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); -} - - -enum pdraw_pipeline_mode PdrawBackend::getPipelineModeSetting(void) -{ - int res; - enum pdraw_pipeline_mode ret = PDRAW_PIPELINE_MODE_DECODE_ALL; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_VAL_IF( - !mStarted, EPROTO, PDRAW_PIPELINE_MODE_DECODE_ALL); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - ret = mPdraw->getPipelineModeSetting(); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_GET_PIPELINE_MODE_SETTING; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - ret = mRetPipelineMode; - mRetPipelineMode = PDRAW_PIPELINE_MODE_DECODE_ALL; - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); - return ret; -} - - -void PdrawBackend::setPipelineModeSetting(enum pdraw_pipeline_mode mode) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - mPdraw->setPipelineModeSetting(mode); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_SET_PIPELINE_MODE_SETTING; - cmd->generic.uint = mode; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); -} - - -void PdrawBackend::getDisplayScreenSettings(float *xdpi, - float *ydpi, - float *deviceMarginTop, - float *deviceMarginBottom, - float *deviceMarginLeft, - float *deviceMarginRight) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - mPdraw->getDisplayScreenSettings(xdpi, - ydpi, - deviceMarginTop, - deviceMarginBottom, - deviceMarginLeft, - deviceMarginRight); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_GET_DISPLAY_SCREEN_SETTINGS; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - if (xdpi) - *xdpi = mRetFloat[0]; - if (ydpi) - *ydpi = mRetFloat[1]; - if (deviceMarginTop) - *deviceMarginTop = mRetFloat[2]; - if (deviceMarginBottom) - *deviceMarginBottom = mRetFloat[3]; - if (deviceMarginLeft) - *deviceMarginLeft = mRetFloat[4]; - if (deviceMarginRight) - *deviceMarginRight = mRetFloat[5]; - memset(mRetFloat, 0, sizeof(mRetFloat)); - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); -} - - -void PdrawBackend::setDisplayScreenSettings(float xdpi, - float ydpi, - float deviceMarginTop, - float deviceMarginBottom, - float deviceMarginLeft, - float deviceMarginRight) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - mPdraw->setDisplayScreenSettings(xdpi, - ydpi, - deviceMarginTop, - deviceMarginBottom, - deviceMarginLeft, - deviceMarginRight); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_SET_DISPLAY_SCREEN_SETTINGS; - cmd->set_display_screen_settings.xdpi = xdpi; - cmd->set_display_screen_settings.ydpi = ydpi; - cmd->set_display_screen_settings.device_margin_top = deviceMarginTop; - cmd->set_display_screen_settings.device_margin_bottom = - deviceMarginBottom; - cmd->set_display_screen_settings.device_margin_left = deviceMarginLeft; - cmd->set_display_screen_settings.device_margin_right = - deviceMarginRight; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); -} - - -enum pdraw_hmd_model PdrawBackend::getHmdModelSetting(void) -{ - int res; - enum pdraw_hmd_model ret = PDRAW_HMD_MODEL_UNKNOWN; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_VAL_IF(!mStarted, EPROTO, ret); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - ret = mPdraw->getHmdModelSetting(); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_GET_HMD_MODEL_SETTING; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - ret = mRetHmdModel; - mRetHmdModel = PDRAW_HMD_MODEL_UNKNOWN; - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); - return ret; -} - - -void PdrawBackend::setHmdModelSetting(enum pdraw_hmd_model hmdModel) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - mPdraw->setHmdModelSetting(hmdModel); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_SET_HMD_MODEL_SETTING; - cmd->generic.uint = hmdModel; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); -} - - -void PdrawBackend::setAndroidJvm(void *jvm) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - mPdraw->setAndroidJvm(jvm); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_SET_ANDROID_JVM; - cmd->generic.ptr = jvm; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); -} - - -int PdrawBackend::dumpPipeline(const std::string &fileName) -{ - int res; - struct cmd_msg *cmd = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF( - fileName.length() > sizeof(cmd->dump_pipeline.file_name) - 1, - ENOBUFS); - ULOG_ERRNO_RETURN_ERR_IF(!mStarted, EPROTO); - - pthread_mutex_lock(&mApiMutex); - - if (pthread_self() == mLoopThread) { - /* Execution is on the loop thread, - * call the function directly */ - res = mPdraw->dumpPipeline(fileName); - goto out2; - } - - pthread_mutex_lock(&mMutex); - mRetValReady = false; - - cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); - if (cmd == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("calloc", -res); - goto out; - } - - /* Send a message to the loop */ - cmd->type = CMD_TYPE_DUMP_PIPELINE; - strncpy(cmd->dump_pipeline.file_name, - fileName.c_str(), - sizeof(cmd->dump_pipeline.file_name)); - cmd->dump_pipeline.file_name[sizeof(cmd->dump_pipeline.file_name) - 1] = - '\0'; - res = mbox_push(mMbox, cmd); - if (res < 0) { - ULOG_ERRNO("mbox_push", -res); - goto out; - } - - while (!mRetValReady) - pthread_cond_wait(&mCond, &mMutex); - - res = mRetStatus; - mRetStatus = 0; - mRetValReady = false; - -out: - free(cmd); - pthread_mutex_unlock(&mMutex); - -out2: - pthread_mutex_unlock(&mApiMutex); - return res; -} - - -/** - * Private functions - */ - -void PdrawBackend::stopResponse(IPdraw *pdraw, int status) -{ - if (pthread_self() != mLoopThread) - ULOGW("%s not called from the loop thread", __func__); - mListener->stopResponse(this, status); - mThreadShouldStop = true; - if (mLoop) { - int err = pomp_loop_wakeup(mLoop); - if (err < 0) - ULOG_ERRNO("pomp_loop_wakeup", -err); - } -} - - -void PdrawBackend::onMediaAdded(IPdraw *pdraw, - const struct pdraw_media_info *info) -{ - if (pthread_self() != mLoopThread) - ULOGW("%s not called from the loop thread", __func__); - mListener->onMediaAdded(this, info); -} - - -void PdrawBackend::onMediaRemoved(IPdraw *pdraw, - const struct pdraw_media_info *info) -{ - if (pthread_self() != mLoopThread) - ULOGW("%s not called from the loop thread", __func__); - mListener->onMediaRemoved(this, info); -} - - -void PdrawBackend::onSocketCreated(IPdraw *pdraw, int fd) -{ - if (pthread_self() != mLoopThread) - ULOGW("%s not called from the loop thread", __func__); - mListener->onSocketCreated(this, fd); -} - - -void PdrawBackend::demuxerOpenResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status) -{ - std::map::iterator it; - struct demuxerAndListener dl; - - if (pthread_self() != mLoopThread) - ULOGW("%s not called from the loop thread", __func__); - - pthread_mutex_lock(&mMapsMutex); - it = mDemuxerListenersMap.find(demuxer); - if (it == mDemuxerListenersMap.end()) { - dl = mPendingDemuxerAndListener; - } else { - dl = it->second; - } - pthread_mutex_unlock(&mMapsMutex); - if (dl.l == nullptr) { - ULOGE("%s: failed to find the demuxer listener in the map", - __func__); - return; - } - if (dl.d == nullptr) { - ULOGE("%s: failed to find the demuxer in the map", __func__); - return; - } - - dl.l->demuxerOpenResponse(this, dl.d, status); -} - - -void PdrawBackend::demuxerCloseResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status) -{ - std::map::iterator it; - struct demuxerAndListener dl; - - if (pthread_self() != mLoopThread) - ULOGW("%s not called from the loop thread", __func__); - - pthread_mutex_lock(&mMapsMutex); - it = mDemuxerListenersMap.find(demuxer); - if (it == mDemuxerListenersMap.end()) { - dl = mPendingDemuxerAndListener; - } else { - dl = it->second; - } - pthread_mutex_unlock(&mMapsMutex); - if (dl.l == nullptr) { - ULOGE("%s: failed to find the demuxer listener in the map", - __func__); - return; - } - if (dl.d == nullptr) { - ULOGE("%s: failed to find the demuxer in the map", __func__); - return; - } - - dl.l->demuxerCloseResponse(this, dl.d, status); -} - - -void PdrawBackend::onDemuxerUnrecoverableError(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer) -{ - std::map::iterator it; - struct demuxerAndListener dl; - - if (pthread_self() != mLoopThread) - ULOGW("%s not called from the loop thread", __func__); - - pthread_mutex_lock(&mMapsMutex); - it = mDemuxerListenersMap.find(demuxer); - if (it == mDemuxerListenersMap.end()) { - dl = mPendingDemuxerAndListener; - } else { - dl = it->second; - } - pthread_mutex_unlock(&mMapsMutex); - if (dl.l == nullptr) { - ULOGE("%s: failed to find the demuxer listener in the map", - __func__); - return; - } - if (dl.d == nullptr) { - ULOGE("%s: failed to find the demuxer in the map", __func__); - return; - } - - dl.l->onDemuxerUnrecoverableError(this, dl.d); -} - - -int PdrawBackend::demuxerSelectMedia(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - const struct pdraw_demuxer_media *medias, - size_t count) -{ - std::map::iterator it; - struct demuxerAndListener dl; - - if (pthread_self() != mLoopThread) - ULOGW("%s not called from the loop thread", __func__); - - pthread_mutex_lock(&mMapsMutex); - it = mDemuxerListenersMap.find(demuxer); - if (it == mDemuxerListenersMap.end()) { - dl = mPendingDemuxerAndListener; - } else { - dl = it->second; - } - pthread_mutex_unlock(&mMapsMutex); - if (dl.l == nullptr) { - ULOGE("%s: failed to find the demuxer listener in the map", - __func__); - return -ENOENT; - } - if (dl.d == nullptr) { - ULOGE("%s: failed to find the demuxer in the map", __func__); - return -ENOENT; - } - - return dl.l->demuxerSelectMedia(this, dl.d, medias, count); -} - - -void PdrawBackend::demuxerReadyToPlay(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - bool ready) -{ - std::map::iterator it; - struct demuxerAndListener dl; - - if (pthread_self() != mLoopThread) - ULOGW("%s not called from the loop thread", __func__); - - pthread_mutex_lock(&mMapsMutex); - it = mDemuxerListenersMap.find(demuxer); - if (it == mDemuxerListenersMap.end()) { - dl = mPendingDemuxerAndListener; - } else { - dl = it->second; - } - pthread_mutex_unlock(&mMapsMutex); - if (dl.l == nullptr) { - ULOGE("%s: failed to find the demuxer listener in the map", - __func__); - return; - } - if (dl.d == nullptr) { - ULOGE("%s: failed to find the demuxer in the map", __func__); - return; - } - - dl.l->demuxerReadyToPlay(this, dl.d, ready); -} - - -void PdrawBackend::onDemuxerEndOfRange(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - uint64_t timestamp) -{ - std::map::iterator it; - struct demuxerAndListener dl; - - if (pthread_self() != mLoopThread) - ULOGW("%s not called from the loop thread", __func__); - - pthread_mutex_lock(&mMapsMutex); - it = mDemuxerListenersMap.find(demuxer); - if (it == mDemuxerListenersMap.end()) { - dl = mPendingDemuxerAndListener; - } else { - dl = it->second; - } - pthread_mutex_unlock(&mMapsMutex); - if (dl.l == nullptr) { - ULOGE("%s: failed to find the demuxer listener in the map", - __func__); - return; - } - if (dl.d == nullptr) { - ULOGE("%s: failed to find the demuxer in the map", __func__); - return; - } - - dl.l->onDemuxerEndOfRange(this, dl.d, timestamp); -} - - -void PdrawBackend::demuxerPlayResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp, - float speed) -{ - std::map::iterator it; - struct demuxerAndListener dl; - - if (pthread_self() != mLoopThread) - ULOGW("%s not called from the loop thread", __func__); - - pthread_mutex_lock(&mMapsMutex); - it = mDemuxerListenersMap.find(demuxer); - if (it == mDemuxerListenersMap.end()) { - dl = mPendingDemuxerAndListener; - } else { - dl = it->second; - } - pthread_mutex_unlock(&mMapsMutex); - if (dl.l == nullptr) { - ULOGE("%s: failed to find the demuxer listener in the map", - __func__); - return; - } - if (dl.d == nullptr) { - ULOGE("%s: failed to find the demuxer in the map", __func__); - return; - } - - dl.l->demuxerPlayResponse(this, dl.d, status, timestamp, speed); -} - - -void PdrawBackend::demuxerPauseResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp) -{ - std::map::iterator it; - struct demuxerAndListener dl; - - if (pthread_self() != mLoopThread) - ULOGW("%s not called from the loop thread", __func__); - - pthread_mutex_lock(&mMapsMutex); - it = mDemuxerListenersMap.find(demuxer); - if (it == mDemuxerListenersMap.end()) { - dl = mPendingDemuxerAndListener; - } else { - dl = it->second; - } - pthread_mutex_unlock(&mMapsMutex); - if (dl.l == nullptr) { - ULOGE("%s: failed to find the demuxer listener in the map", - __func__); - return; - } - if (dl.d == nullptr) { - ULOGE("%s: failed to find the demuxer in the map", __func__); - return; - } - - dl.l->demuxerPauseResponse(this, dl.d, status, timestamp); -} - - -void PdrawBackend::demuxerSeekResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp, - float speed) -{ - std::map::iterator it; - struct demuxerAndListener dl; - - if (pthread_self() != mLoopThread) - ULOGW("%s not called from the loop thread", __func__); - - pthread_mutex_lock(&mMapsMutex); - it = mDemuxerListenersMap.find(demuxer); - if (it == mDemuxerListenersMap.end()) { - dl = mPendingDemuxerAndListener; - } else { - dl = it->second; - } - pthread_mutex_unlock(&mMapsMutex); - if (dl.l == nullptr) { - ULOGE("%s: failed to find the demuxer listener in the map", - __func__); - return; - } - if (dl.d == nullptr) { - ULOGE("%s: failed to find the demuxer in the map", __func__); - return; - } - - dl.l->demuxerSeekResponse(this, dl.d, status, timestamp, speed); -} - - -void PdrawBackend::onVideoRendererMediaAdded( - Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IVideoRenderer *renderer, - const struct pdraw_media_info *info) -{ - std::map::iterator it; - struct videoRendererAndListener rl; - - if (pthread_self() != mLoopThread) - ULOGW("%s not called from the loop thread", __func__); - - pthread_mutex_lock(&mMapsMutex); - it = mVideoRendererListenersMap.find(renderer); - if (it == mVideoRendererListenersMap.end()) { - rl = mPendingVideoRendererAndListener; - } else { - rl = it->second; - } - pthread_mutex_unlock(&mMapsMutex); - if (rl.l == nullptr) { - ULOGE("%s: failed to find the video renderer " - "listener in the map", - __func__); - return; - } - if (rl.r == nullptr) { - ULOGE("%s: failed to find the video renderer in the map", - __func__); - return; - } - - rl.l->onVideoRendererMediaAdded(this, rl.r, info); -} - - -void PdrawBackend::onVideoRendererMediaRemoved( - Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IVideoRenderer *renderer, - const struct pdraw_media_info *info) -{ - std::map::iterator it; - struct videoRendererAndListener rl; - - if (pthread_self() != mLoopThread) - ULOGW("%s not called from the loop thread", __func__); - - pthread_mutex_lock(&mMapsMutex); - it = mVideoRendererListenersMap.find(renderer); - if (it == mVideoRendererListenersMap.end()) { - rl = mPendingVideoRendererAndListener; - } else { - rl = it->second; - } - pthread_mutex_unlock(&mMapsMutex); - if (rl.l == nullptr) { - ULOGE("%s: failed to find the video renderer " - "listener in the map", - __func__); - return; - } - if (rl.r == nullptr) { - ULOGE("%s: failed to find the video renderer in the map", - __func__); - return; - } - - rl.l->onVideoRendererMediaRemoved(this, rl.r, info); -} - - -void PdrawBackend::onVideoRenderReady(IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer) -{ - std::map::iterator it; - struct videoRendererAndListener rl; - - if (pthread_self() != mLoopThread) - ULOGW("%s not called from the loop thread", __func__); - - pthread_mutex_lock(&mMapsMutex); - it = mVideoRendererListenersMap.find(renderer); - if (it == mVideoRendererListenersMap.end()) { - rl = mPendingVideoRendererAndListener; - } else { - rl = it->second; - } - pthread_mutex_unlock(&mMapsMutex); - if (rl.l == nullptr) { - ULOGE("%s: failed to find the video renderer " - "listener in the map", - __func__); - return; - } - if (rl.r == nullptr) { - ULOGE("%s: failed to find the video renderer in the map", - __func__); - return; - } - - rl.l->onVideoRenderReady(this, rl.r); -} - - -int PdrawBackend::loadVideoTexture(IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer, - unsigned int textureWidth, - unsigned int textureHeight, - const struct pdraw_media_info *mediaInfo, - struct mbuf_raw_video_frame *frame, - const void *frameUserdata, - size_t frameUserdataLen) -{ - std::map::iterator it; - struct videoRendererAndListener rl; - - pthread_mutex_lock(&mMapsMutex); - it = mVideoRendererListenersMap.find(renderer); - if (it == mVideoRendererListenersMap.end()) { - rl = mPendingVideoRendererAndListener; - } else { - rl = it->second; - } - pthread_mutex_unlock(&mMapsMutex); - if (rl.l == nullptr) { - ULOGE("%s: failed to find the video renderer " - "listener in the map", - __func__); - return -ENOENT; - } - if (rl.r == nullptr) { - ULOGE("%s: failed to find the video renderer in the map", - __func__); - return -ENOENT; - } - - return rl.l->loadVideoTexture(this, - rl.r, - textureWidth, - textureHeight, - mediaInfo, - frame, - frameUserdata, - frameUserdataLen); -} - - -int PdrawBackend::renderVideoOverlay( - IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer, - const struct pdraw_rect *renderPos, - const struct pdraw_rect *contentPos, - const float *viewMat, - const float *projMat, - const struct pdraw_media_info *mediaInfo, - struct vmeta_frame *frameMeta, - const struct pdraw_video_frame_extra *frameExtra) -{ - std::map::iterator it; - struct videoRendererAndListener rl; - - pthread_mutex_lock(&mMapsMutex); - it = mVideoRendererListenersMap.find(renderer); - if (it == mVideoRendererListenersMap.end()) { - rl = mPendingVideoRendererAndListener; - } else { - rl = it->second; - } - pthread_mutex_unlock(&mMapsMutex); - if (rl.l == nullptr) { - ULOGE("%s: failed to find the video renderer " - "listener in the map", - __func__); - return -ENOENT; - } - if (rl.r == nullptr) { - ULOGE("%s: failed to find the video renderer in the map", - __func__); - return -ENOENT; - } - - return rl.l->renderVideoOverlay(this, - rl.r, - renderPos, - contentPos, - viewMat, - projMat, - mediaInfo, - frameMeta, - frameExtra); -} - - -void PdrawBackend::onCodedVideoSinkFlush(IPdraw *pdraw, - IPdraw::ICodedVideoSink *sink) -{ - std::map::iterator it; - struct codedVideoSinkAndListener sl; - - if (pthread_self() != mLoopThread) - ULOGW("%s not called from the loop thread", __func__); - - pthread_mutex_lock(&mMapsMutex); - it = mCodedVideoSinkListenersMap.find(sink); - if (it == mCodedVideoSinkListenersMap.end()) { - sl = mPendingCodedVideoSinkAndListener; - } else { - sl = it->second; - } - pthread_mutex_unlock(&mMapsMutex); - if (sl.l == nullptr) { - ULOGE("%s: failed to find the video sink listener in the map", - __func__); - return; - } - if (sl.s == nullptr) { - ULOGE("%s: failed to find the video sink in the map", __func__); - return; - } - - sl.l->onCodedVideoSinkFlush(this, sl.s); -} - - -void PdrawBackend::onRawVideoSinkFlush(IPdraw *pdraw, - IPdraw::IRawVideoSink *sink) -{ - std::map::iterator it; - struct rawVideoSinkAndListener sl; - - if (pthread_self() != mLoopThread) - ULOGW("%s not called from the loop thread", __func__); - - pthread_mutex_lock(&mMapsMutex); - it = mRawVideoSinkListenersMap.find(sink); - if (it == mRawVideoSinkListenersMap.end()) { - sl = mPendingRawVideoSinkAndListener; - } else { - sl = it->second; - } - pthread_mutex_unlock(&mMapsMutex); - if (sl.l == nullptr) { - ULOGE("%s: failed to find the video sink listener in the map", - __func__); - return; - } - if (sl.s == nullptr) { - ULOGE("%s: failed to find the video sink in the map", __func__); - return; - } - - sl.l->onRawVideoSinkFlush(this, sl.s); -} - - -void *PdrawBackend::loopThread(void *ptr) -{ - PdrawBackend *self = (PdrawBackend *)ptr; - int res = 0, err; - - pthread_mutex_lock(&self->mMutex); - - if (self->mMbox == nullptr) { - res = -EPROTO; - ULOGE("invalid mailbox"); - goto error; - } - - self->mLoop = pomp_loop_new(); - if (self->mLoop == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("pomp_loop_new", -res); - goto error; - } - - res = pomp_loop_add(self->mLoop, - mbox_get_read_fd(self->mMbox), - POMP_FD_EVENT_IN, - &mboxCb, - self); - if (res < 0) { - ULOG_ERRNO("pomp_loop_add", -res); - goto error; - } - - res = createPdraw(self->mLoop, self, &self->mPdraw); - if (res < 0) { - ULOG_ERRNO("createPdraw", -res); - goto error; - } - - self->mRetStatus = 0; - self->mRetValReady = true; - pthread_mutex_unlock(&self->mMutex); - pthread_cond_signal(&self->mCond); - - while (!self->mThreadShouldStop) { - pomp_loop_wait_and_process(self->mLoop, -1); - } - - goto out; - -error: - self->mRetStatus = res; - self->mRetValReady = true; - pthread_mutex_unlock(&self->mMutex); - pthread_cond_signal(&self->mCond); -out: - if (self->mPdraw != nullptr) { - delete self->mPdraw; - self->mPdraw = nullptr; - } - if (self->mLoop != nullptr) { - if (self->mMbox != nullptr) { - err = pomp_loop_remove(self->mLoop, - mbox_get_read_fd(self->mMbox)); - if (err < 0) - ULOG_ERRNO("pomp_loop_remove", -err); - if (res == 0) - res = err; - } - - err = pomp_loop_destroy(self->mLoop); - if (err < 0) - ULOG_ERRNO("pomp_loop_destroy", -err); - if (res == 0) - res = err; - self->mLoop = nullptr; - } - - return (void *)(intptr_t)res; -} - - -void PdrawBackend::mboxCb(int fd, uint32_t revents, void *userdata) -{ - PdrawBackend *self = (PdrawBackend *)userdata; - int res; - void *message; - - message = malloc(sizeof(cmd_msg)); - if (message == nullptr) { - ULOG_ERRNO("malloc", ENOMEM); - return; - } - - do { - struct cmd_msg *msg = (struct cmd_msg *)message; - - /* Read from the mailbox */ - res = mbox_peek(self->mMbox, msg); - if (res < 0) { - if (res != -EAGAIN) - ULOG_ERRNO("mbox_peek", -res); - break; - } - - switch (msg->type) { - case CMD_TYPE_STOP: { - self->internalStop(); - break; - } - case CMD_TYPE_DEMUXER_CREATE_SINGLE: { - std::string l(msg->demuxer_create_single.local_addr); - std::string r(msg->demuxer_create_single.remote_addr); - self->internalDemuxerCreate( - l, - msg->demuxer_create_single.local_stream_port, - msg->demuxer_create_single.local_control_port, - r, - msg->demuxer_create_single.remote_stream_port, - msg->demuxer_create_single.remote_control_port, - msg->demuxer_create_single.listener); - break; - } - case CMD_TYPE_DEMUXER_CREATE_URL: { - std::string u(msg->demuxer_create_url.url); - self->internalDemuxerCreate( - u, msg->demuxer_create_url.listener); - break; - } - case CMD_TYPE_DEMUXER_CREATE_URL_MUX: { - std::string u(msg->demuxer_create_url_mux.url); - self->internalDemuxerCreate( - u, - msg->demuxer_create_url_mux.mux, - msg->demuxer_create_url_mux.listener); - break; - } - case CMD_TYPE_DEMUXER_DESTROY: { - self->internalDemuxerDestroy( - reinterpret_cast( - msg->generic.ptr)); - break; - } - case CMD_TYPE_DEMUXER_CLOSE: { - self->internalDemuxerClose( - reinterpret_cast( - msg->generic.ptr)); - break; - } - case CMD_TYPE_DEMUXER_GET_SINGLE_STREAM_LOCAL_STREAM_PORT: { - self->internalDemuxerGetSingleStreamLocalStreamPort( - reinterpret_cast( - msg->generic.ptr)); - break; - } - case CMD_TYPE_DEMUXER_GET_SINGLE_STREAM_LOCAL_CONTROL_PORT: { - self->internalDemuxerGetSingleStreamLocalControlPort( - reinterpret_cast( - msg->generic.ptr)); - break; - } - case CMD_TYPE_DEMUXER_IS_READY_TO_PLAY: { - self->internalDemuxerIsReadyToPlay( - reinterpret_cast( - msg->generic.ptr)); - break; - } - case CMD_TYPE_DEMUXER_IS_PAUSED: { - self->internalDemuxerIsPaused( - reinterpret_cast( - msg->generic.ptr)); - break; - } - case CMD_TYPE_DEMUXER_PLAY: { - self->internalDemuxerPlay(msg->demuxer_play.demuxer, - msg->demuxer_play.speed); - break; - } - case CMD_TYPE_DEMUXER_PREVIOUS_FRAME: { - self->internalDemuxerPreviousFrame( - reinterpret_cast( - msg->generic.ptr)); - break; - } - case CMD_TYPE_DEMUXER_NEXT_FRAME: { - self->internalDemuxerNextFrame( - reinterpret_cast( - msg->generic.ptr)); - break; - } - case CMD_TYPE_DEMUXER_SEEK: { - self->internalDemuxerSeek(msg->demuxer_seek.demuxer, - msg->demuxer_seek.delta, - msg->demuxer_seek.exact); - break; - } - case CMD_TYPE_DEMUXER_SEEK_TO: { - self->internalDemuxerSeekTo( - msg->demuxer_seek_to.demuxer, - msg->demuxer_seek_to.timestamp, - msg->demuxer_seek_to.exact); - break; - } - case CMD_TYPE_DEMUXER_GET_DURATION: { - self->internalDemuxerGetDuration( - reinterpret_cast( - msg->generic.ptr)); - break; - } - case CMD_TYPE_DEMUXER_GET_CURRENT_TIME: { - self->internalDemuxerGetCurrentTime( - reinterpret_cast( - msg->generic.ptr)); - break; - } - case CMD_TYPE_MUXER_CREATE: { - std::string u(msg->muxer_create.url); - self->internalMuxerCreate(u); - break; - } - case CMD_TYPE_MUXER_DESTROY: { - self->internalMuxerDestroy( - reinterpret_cast( - msg->generic.ptr)); - break; - } - case CMD_TYPE_MUXER_ADD_MEDIA: { - self->internalMuxerAddMedia( - reinterpret_cast( - msg->muxer_add_media.muxer), - msg->muxer_add_media.media_id, - &msg->muxer_add_media.params); - break; - } - case CMD_TYPE_CODED_VIDEO_SINK_CREATE: { - self->internalCodedVideoSinkCreate( - msg->coded_video_sink_create.media_id, - &msg->coded_video_sink_create.params, - msg->coded_video_sink_create.listener); - break; - } - case CMD_TYPE_CODED_VIDEO_SINK_DESTROY: { - self->internalCodedVideoSinkDestroy( - reinterpret_cast(msg->generic.ptr)); - break; - } - case CMD_TYPE_CODED_VIDEO_SINK_RESYNC: { - self->internalCodedVideoSinkResync( - reinterpret_cast(msg->generic.ptr)); - break; - } - case CMD_TYPE_CODED_VIDEO_SINK_GET_QUEUE: { - self->internalCodedVideoSinkGetQueue( - reinterpret_cast(msg->generic.ptr)); - break; - } - case CMD_TYPE_CODED_VIDEO_SINK_QUEUE_FLUSHED: { - self->internalCodedVideoSinkQueueFlushed( - reinterpret_cast(msg->generic.ptr)); - break; - } - case CMD_TYPE_RAW_VIDEO_SINK_CREATE: { - self->internalRawVideoSinkCreate( - msg->raw_video_sink_create.media_id, - &msg->raw_video_sink_create.params, - msg->raw_video_sink_create.listener); - break; - } - case CMD_TYPE_RAW_VIDEO_SINK_DESTROY: { - self->internalRawVideoSinkDestroy( - reinterpret_cast( - msg->generic.ptr)); - break; - } - case CMD_TYPE_RAW_VIDEO_SINK_GET_QUEUE: { - self->internalRawVideoSinkGetQueue( - reinterpret_cast( - msg->generic.ptr)); - break; - } - case CMD_TYPE_RAW_VIDEO_SINK_QUEUE_FLUSHED: { - self->internalRawVideoSinkQueueFlushed( - reinterpret_cast( - msg->generic.ptr)); - break; - } - case CMD_TYPE_GET_FRIENDLY_NAME_SETTING: { - self->internalGetFriendlyNameSetting(); - break; - } - case CMD_TYPE_SET_FRIENDLY_NAME_SETTING: { - std::string str = msg->generic.string; - self->internalSetFriendlyNameSetting(str); - break; - } - case CMD_TYPE_GET_SERIAL_NUMBER_SETTING: { - self->internalGetSerialNumberSetting(); - break; - } - case CMD_TYPE_SET_SERIAL_NUMBER_SETTING: { - std::string str = msg->generic.string; - self->internalSetSerialNumberSetting(str); - break; - } - case CMD_TYPE_GET_SOFTWARE_VERSION_SETTING: { - self->internalGetSoftwareVersionSetting(); - break; - } - case CMD_TYPE_SET_SOFTWARE_VERSION_SETTING: { - std::string str = msg->generic.string; - self->internalSetSoftwareVersionSetting(str); - break; - } - case CMD_TYPE_GET_PIPELINE_MODE_SETTING: { - self->internalGetPipelineModeSetting(); - break; - } - case CMD_TYPE_SET_PIPELINE_MODE_SETTING: { - self->internalSetPipelineModeSetting( - (enum pdraw_pipeline_mode)msg->generic.uint); - break; - } - case CMD_TYPE_GET_DISPLAY_SCREEN_SETTINGS: { - self->internalGetDisplayScreenSettings(); - break; - } - case CMD_TYPE_SET_DISPLAY_SCREEN_SETTINGS: { - self->internalSetDisplayScreenSettings( - msg->set_display_screen_settings.xdpi, - msg->set_display_screen_settings.ydpi, - msg->set_display_screen_settings - .device_margin_top, - msg->set_display_screen_settings - .device_margin_bottom, - msg->set_display_screen_settings - .device_margin_left, - msg->set_display_screen_settings - .device_margin_right); - break; - } - case CMD_TYPE_GET_HMD_MODEL_SETTING: { - self->internalGetHmdModelSetting(); - break; - } - case CMD_TYPE_SET_HMD_MODEL_SETTING: { - self->internalSetHmdModelSetting( - (enum pdraw_hmd_model)msg->generic.uint); - break; - } - case CMD_TYPE_SET_ANDROID_JVM: { - self->internalSetAndroidJvm(msg->generic.ptr); - break; - } - case CMD_TYPE_DUMP_PIPELINE: { - std::string f(msg->dump_pipeline.file_name); - self->internalDumpPipeline(f); - break; - } - default: - ULOGE("unknown command: %d", msg->type); - break; - } - } while (res == 0); - - free(message); -} - - -int PdrawBackend::doCreateDemuxer(const std::string &url, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj) -{ - int res; - PdrawBackend::Demuxer *demuxer = nullptr; - IPdraw::IDemuxer *d = nullptr; - std::pair::iterator, - bool> - inserted; - - demuxer = new PdrawBackend::Demuxer(this, listener); - if (demuxer == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("PdrawBackend::Demuxer", -res); - return res; - } - - mPendingDemuxerAndListener = { - .d = demuxer, - .l = listener, - }; - - res = mPdraw->createDemuxer(url, this, &d); - if (res < 0) { - ULOG_ERRNO("pdraw->createDemuxer", -res); - delete demuxer; - demuxer = nullptr; - return res; - } - demuxer->setDemuxer(d); - - pthread_mutex_lock(&mMapsMutex); - inserted = mDemuxerListenersMap.insert( - std::pair( - demuxer->getDemuxer(), mPendingDemuxerAndListener)); - if (inserted.second == false) - ULOGW("failed to insert the demuxer listener in the map"); - pthread_mutex_unlock(&mMapsMutex); - - *retObj = demuxer; - return 0; -} - - -int PdrawBackend::doCreateDemuxer(const std::string &localAddr, - uint16_t localStreamPort, - uint16_t localControlPort, - const std::string &remoteAddr, - uint16_t remoteStreamPort, - uint16_t remoteControlPort, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj) -{ - int res; - PdrawBackend::Demuxer *demuxer = nullptr; - IPdraw::IDemuxer *d = nullptr; - std::pair::iterator, - bool> - inserted; - - demuxer = new PdrawBackend::Demuxer(this, listener); - if (demuxer == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("PdrawBackend::Demuxer", -res); - return res; - } - - mPendingDemuxerAndListener = { - .d = demuxer, - .l = listener, - }; - - res = mPdraw->createDemuxer(localAddr, - localStreamPort, - localControlPort, - remoteAddr, - remoteStreamPort, - remoteControlPort, - this, - &d); - if (res < 0) { - ULOG_ERRNO("pdraw->createDemuxer", -res); - delete demuxer; - demuxer = nullptr; - return res; - } - demuxer->setDemuxer(d); - - pthread_mutex_lock(&mMapsMutex); - inserted = mDemuxerListenersMap.insert( - std::pair( - demuxer->getDemuxer(), mPendingDemuxerAndListener)); - if (inserted.second == false) - ULOGW("failed to insert the demuxer listener in the map"); - pthread_mutex_unlock(&mMapsMutex); - - *retObj = demuxer; - return 0; -} - - -int PdrawBackend::doCreateDemuxer(const std::string &url, - struct mux_ctx *mux, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj) -{ - int res; - PdrawBackend::Demuxer *demuxer = nullptr; - IPdraw::IDemuxer *d = nullptr; - std::pair::iterator, - bool> - inserted; - - demuxer = new PdrawBackend::Demuxer(this, listener); - if (demuxer == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("PdrawBackend::Demuxer", -res); - return res; - } - - mPendingDemuxerAndListener = { - .d = demuxer, - .l = listener, - }; - - res = mPdraw->createDemuxer(url, mux, this, &d); - if (res < 0) { - ULOG_ERRNO("pdraw->createDemuxer", -res); - delete demuxer; - demuxer = nullptr; - return res; - } - demuxer->setDemuxer(d); - - pthread_mutex_lock(&mMapsMutex); - inserted = mDemuxerListenersMap.insert( - std::pair( - demuxer->getDemuxer(), mPendingDemuxerAndListener)); - if (inserted.second == false) - ULOGW("failed to insert the demuxer listener in the map"); - pthread_mutex_unlock(&mMapsMutex); - - *retObj = demuxer; - return 0; -} - - -int PdrawBackend::doCreateMuxer(const std::string &url, IPdraw::IMuxer **retObj) -{ - int res; - PdrawBackend::Muxer *muxer = nullptr; - IPdraw::IMuxer *m = nullptr; - - muxer = new PdrawBackend::Muxer(this, url); - if (muxer == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("PdrawBackend::Muxer", -res); - return res; - } - - res = mPdraw->createMuxer(url, &m); - if (res < 0) { - ULOG_ERRNO("pdraw->createMuxer", -res); - delete muxer; - muxer = nullptr; - return res; - } - muxer->setMuxer(m); - - *retObj = muxer; - return 0; -} - - -int PdrawBackend::doCreateCodedVideoSink( - unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::ICodedVideoSink::Listener *listener, - IPdraw::ICodedVideoSink **retObj) -{ - int res; - PdrawBackend::CodedVideoSink *sink = nullptr; - IPdraw::ICodedVideoSink *s = nullptr; - std::pair::iterator, - bool> - inserted; - - sink = new PdrawBackend::CodedVideoSink( - this, mediaId, params, listener); - if (sink == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("PdrawBackend::CodedVideoSink", -res); - return res; - } - - mPendingCodedVideoSinkAndListener = { - .s = sink, - .l = listener, - }; - - res = mPdraw->createCodedVideoSink(mediaId, params, this, &s); - if (res < 0) { - ULOG_ERRNO("pdraw->createCodedVideoSink", -res); - delete sink; - sink = nullptr; - return res; - } - sink->setCodedVideoSink(s); - - pthread_mutex_lock(&mMapsMutex); - inserted = mCodedVideoSinkListenersMap.insert( - std::pair( - sink->getCodedVideoSink(), - mPendingCodedVideoSinkAndListener)); - if (inserted.second == false) - ULOGW("failed to insert the video sink listener in the map"); - pthread_mutex_unlock(&mMapsMutex); - - *retObj = sink; - return 0; -} - - -int PdrawBackend::doCreateRawVideoSink( - unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::IRawVideoSink::Listener *listener, - IPdraw::IRawVideoSink **retObj) -{ - int res; - PdrawBackend::RawVideoSink *sink = nullptr; - IPdraw::IRawVideoSink *s = nullptr; - std::pair::iterator, - bool> - inserted; - - sink = new PdrawBackend::RawVideoSink(this, mediaId, params, listener); - if (sink == nullptr) { - res = -ENOMEM; - ULOG_ERRNO("PdrawBackend::RawVideoSink", -res); - return res; - } - - mPendingRawVideoSinkAndListener = { - .s = sink, - .l = listener, - }; - - res = mPdraw->createRawVideoSink(mediaId, params, this, &s); - if (res < 0) { - ULOG_ERRNO("pdraw->createRawVideoSink", -res); - delete sink; - sink = nullptr; - return res; - } - sink->setRawVideoSink(s); - - pthread_mutex_lock(&mMapsMutex); - inserted = mRawVideoSinkListenersMap.insert( - std::pair( - sink->getRawVideoSink(), - mPendingRawVideoSinkAndListener)); - if (inserted.second == false) - ULOGW("failed to insert the video sink listener in the map"); - pthread_mutex_unlock(&mMapsMutex); - - *retObj = sink; - return 0; -} - - -void PdrawBackend::internalStop() -{ - int res = mPdraw->stop(); - - pthread_mutex_lock(&mMutex); - mRetStatus = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalDemuxerCreate(const std::string &url, - IPdraw::IDemuxer::Listener *listener) -{ - int res; - IPdraw::IDemuxer *demuxer = nullptr; - - res = doCreateDemuxer(url, listener, &demuxer); - - mPendingDemuxerAndListener = {}; - pthread_mutex_lock(&mMutex); - mRetStatus = res; - mRetDemuxer = demuxer; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalDemuxerCreate(const std::string &localAddr, - uint16_t localStreamPort, - uint16_t localControlPort, - const std::string &remoteAddr, - uint16_t remoteStreamPort, - uint16_t remoteControlPort, - IPdraw::IDemuxer::Listener *listener) -{ - int res; - IPdraw::IDemuxer *demuxer = nullptr; - - res = doCreateDemuxer(localAddr, - localStreamPort, - localControlPort, - remoteAddr, - remoteStreamPort, - remoteControlPort, - listener, - &demuxer); - - mPendingDemuxerAndListener = {}; - pthread_mutex_lock(&mMutex); - mRetStatus = res; - mRetDemuxer = demuxer; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalDemuxerCreate(const std::string &url, - struct mux_ctx *mux, - IPdraw::IDemuxer::Listener *listener) -{ - int res; - IPdraw::IDemuxer *demuxer = nullptr; - - res = doCreateDemuxer(url, mux, listener, &demuxer); - - mPendingDemuxerAndListener = {}; - pthread_mutex_lock(&mMutex); - mRetStatus = res; - mRetDemuxer = demuxer; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalDemuxerDestroy(PdrawBackend::Demuxer *demuxer) -{ - size_t erased; - - pthread_mutex_lock(&mMapsMutex); - erased = mDemuxerListenersMap.erase(demuxer->getDemuxer()); - if (erased != 1) - ULOGW("failed to erase the demuxer listener from the map"); - pthread_mutex_unlock(&mMapsMutex); - - delete demuxer->getDemuxer(); - - pthread_mutex_lock(&mMutex); - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalDemuxerClose(PdrawBackend::Demuxer *demuxer) -{ - int res = demuxer->getDemuxer()->close(); - - pthread_mutex_lock(&mMutex); - mRetStatus = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalDemuxerGetSingleStreamLocalStreamPort( - PdrawBackend::Demuxer *demuxer) -{ - uint16_t res = demuxer->getDemuxer()->getSingleStreamLocalStreamPort(); - - pthread_mutex_lock(&mMutex); - mRetUint16 = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalDemuxerGetSingleStreamLocalControlPort( - PdrawBackend::Demuxer *demuxer) -{ - uint16_t res = demuxer->getDemuxer()->getSingleStreamLocalControlPort(); - - pthread_mutex_lock(&mMutex); - mRetUint16 = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalDemuxerIsReadyToPlay(PdrawBackend::Demuxer *demuxer) -{ - bool res = demuxer->getDemuxer()->isReadyToPlay(); - - pthread_mutex_lock(&mMutex); - mRetBool = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalDemuxerIsPaused(PdrawBackend::Demuxer *demuxer) -{ - bool res = demuxer->getDemuxer()->isPaused(); - - pthread_mutex_lock(&mMutex); - mRetBool = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalDemuxerPlay(PdrawBackend::Demuxer *demuxer, - float speed) -{ - int res = demuxer->getDemuxer()->play(speed); - - pthread_mutex_lock(&mMutex); - mRetStatus = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalDemuxerPreviousFrame(PdrawBackend::Demuxer *demuxer) -{ - int res = demuxer->getDemuxer()->previousFrame(); - - pthread_mutex_lock(&mMutex); - mRetStatus = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalDemuxerNextFrame(PdrawBackend::Demuxer *demuxer) -{ - int res = demuxer->getDemuxer()->nextFrame(); - - pthread_mutex_lock(&mMutex); - mRetStatus = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalDemuxerSeek(PdrawBackend::Demuxer *demuxer, - int64_t delta, - bool exact) -{ - int res = demuxer->getDemuxer()->seek(delta, exact); - - pthread_mutex_lock(&mMutex); - mRetStatus = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalDemuxerSeekTo(PdrawBackend::Demuxer *demuxer, - uint64_t timestamp, - bool exact) -{ - int res = demuxer->getDemuxer()->seekTo(timestamp, exact); - - pthread_mutex_lock(&mMutex); - mRetStatus = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalDemuxerGetDuration(PdrawBackend::Demuxer *demuxer) -{ - uint64_t res = demuxer->getDemuxer()->getDuration(); - - pthread_mutex_lock(&mMutex); - mRetUint64 = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalDemuxerGetCurrentTime(PdrawBackend::Demuxer *demuxer) -{ - uint64_t res = demuxer->getDemuxer()->getCurrentTime(); - - pthread_mutex_lock(&mMutex); - mRetUint64 = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalMuxerCreate(const std::string &url) -{ - int res; - IPdraw::IMuxer *muxer = nullptr; - - res = doCreateMuxer(url, &muxer); - - pthread_mutex_lock(&mMutex); - mRetStatus = res; - mRetMuxer = muxer; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalMuxerDestroy(PdrawBackend::Muxer *muxer) -{ - delete muxer->getMuxer(); - - pthread_mutex_lock(&mMutex); - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalMuxerAddMedia( - PdrawBackend::Muxer *muxer, - unsigned int mediaId, - const struct pdraw_muxer_video_media_params *params) -{ - int res = muxer->getMuxer()->addMedia(mediaId, params); - - pthread_mutex_lock(&mMutex); - mRetStatus = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalCodedVideoSinkCreate( - unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::ICodedVideoSink::Listener *listener) -{ - int res; - IPdraw::ICodedVideoSink *sink = nullptr; - - res = doCreateCodedVideoSink(mediaId, params, listener, &sink); - - mPendingCodedVideoSinkAndListener = {}; - pthread_mutex_lock(&mMutex); - mRetStatus = res; - mRetCodedVideoSink = sink; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalCodedVideoSinkDestroy( - PdrawBackend::CodedVideoSink *sink) -{ - size_t erased; - - pthread_mutex_lock(&mMapsMutex); - erased = mCodedVideoSinkListenersMap.erase(sink->getCodedVideoSink()); - if (erased != 1) - ULOGW("failed to erase the video sink listener from the map"); - pthread_mutex_unlock(&mMapsMutex); - - delete sink->getCodedVideoSink(); - - pthread_mutex_lock(&mMutex); - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalCodedVideoSinkResync( - PdrawBackend::CodedVideoSink *sink) -{ - int res = sink->getCodedVideoSink()->resync(); - - pthread_mutex_lock(&mMutex); - mRetStatus = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalCodedVideoSinkGetQueue( - PdrawBackend::CodedVideoSink *sink) -{ - struct mbuf_coded_video_frame_queue *res = - sink->getCodedVideoSink()->getQueue(); - - pthread_mutex_lock(&mMutex); - mRetCodedQueue = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalCodedVideoSinkQueueFlushed( - PdrawBackend::CodedVideoSink *sink) -{ - int res = sink->getCodedVideoSink()->queueFlushed(); - - pthread_mutex_lock(&mMutex); - mRetStatus = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalRawVideoSinkCreate( - unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::IRawVideoSink::Listener *listener) -{ - int res; - IPdraw::IRawVideoSink *sink = nullptr; - - res = doCreateRawVideoSink(mediaId, params, listener, &sink); - - mPendingRawVideoSinkAndListener = {}; - pthread_mutex_lock(&mMutex); - mRetStatus = res; - mRetRawVideoSink = sink; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalRawVideoSinkDestroy(PdrawBackend::RawVideoSink *sink) -{ - size_t erased; - - pthread_mutex_lock(&mMapsMutex); - erased = mRawVideoSinkListenersMap.erase(sink->getRawVideoSink()); - if (erased != 1) - ULOGW("failed to erase the video sink listener from the map"); - pthread_mutex_unlock(&mMapsMutex); - - delete sink->getRawVideoSink(); - - pthread_mutex_lock(&mMutex); - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalRawVideoSinkGetQueue( - PdrawBackend::RawVideoSink *sink) -{ - struct mbuf_raw_video_frame_queue *res = - sink->getRawVideoSink()->getQueue(); - - pthread_mutex_lock(&mMutex); - mRetRawQueue = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalRawVideoSinkQueueFlushed( - PdrawBackend::RawVideoSink *sink) -{ - int res = sink->getRawVideoSink()->queueFlushed(); - - pthread_mutex_lock(&mMutex); - mRetStatus = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalGetFriendlyNameSetting(void) -{ - std::string friendlyName; - mPdraw->getFriendlyNameSetting(&friendlyName); - - pthread_mutex_lock(&mMutex); - mRetString = friendlyName; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalSetFriendlyNameSetting( - const std::string &friendlyName) -{ - mPdraw->setFriendlyNameSetting(friendlyName); - - pthread_mutex_lock(&mMutex); - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalGetSerialNumberSetting(void) -{ - std::string serialNumber; - mPdraw->getSerialNumberSetting(&serialNumber); - - pthread_mutex_lock(&mMutex); - mRetString = serialNumber; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalSetSerialNumberSetting( - const std::string &serialNumber) -{ - mPdraw->setSerialNumberSetting(serialNumber); - - pthread_mutex_lock(&mMutex); - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalGetSoftwareVersionSetting(void) -{ - std::string softwareVersion; - mPdraw->getSoftwareVersionSetting(&softwareVersion); - - pthread_mutex_lock(&mMutex); - mRetString = softwareVersion; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalSetSoftwareVersionSetting( - const std::string &softwareVersion) -{ - mPdraw->setSoftwareVersionSetting(softwareVersion); - - pthread_mutex_lock(&mMutex); - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalGetPipelineModeSetting(void) -{ - enum pdraw_pipeline_mode res = mPdraw->getPipelineModeSetting(); - - pthread_mutex_lock(&mMutex); - mRetPipelineMode = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalSetPipelineModeSetting(enum pdraw_pipeline_mode mode) -{ - mPdraw->setPipelineModeSetting(mode); - - pthread_mutex_lock(&mMutex); - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalGetDisplayScreenSettings(void) -{ - float xdpi = 0.f, ydpi = 0.f; - float deviceMarginTop = 0.f, deviceMarginBottom = 0.f; - float deviceMarginLeft = 0.f, deviceMarginRight = 0.f; - mPdraw->getDisplayScreenSettings(&xdpi, - &ydpi, - &deviceMarginTop, - &deviceMarginBottom, - &deviceMarginLeft, - &deviceMarginRight); - - pthread_mutex_lock(&mMutex); - mRetFloat[0] = xdpi; - mRetFloat[1] = ydpi; - mRetFloat[2] = deviceMarginTop; - mRetFloat[3] = deviceMarginBottom; - mRetFloat[4] = deviceMarginLeft; - mRetFloat[5] = deviceMarginRight; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalSetDisplayScreenSettings(float xdpi, - float ydpi, - float deviceMarginTop, - float deviceMarginBottom, - float deviceMarginLeft, - float deviceMarginRight) -{ - mPdraw->setDisplayScreenSettings(xdpi, - ydpi, - deviceMarginTop, - deviceMarginBottom, - deviceMarginLeft, - deviceMarginRight); - - pthread_mutex_lock(&mMutex); - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalGetHmdModelSetting(void) -{ - enum pdraw_hmd_model res = mPdraw->getHmdModelSetting(); - - pthread_mutex_lock(&mMutex); - mRetHmdModel = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalSetHmdModelSetting(enum pdraw_hmd_model hmdModel) -{ - mPdraw->setHmdModelSetting(hmdModel); - - pthread_mutex_lock(&mMutex); - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalSetAndroidJvm(void *jvm) -{ - mPdraw->setAndroidJvm(jvm); - - pthread_mutex_lock(&mMutex); - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - - -void PdrawBackend::internalDumpPipeline(const std::string &fileName) -{ - int res = mPdraw->dumpPipeline(fileName); - - pthread_mutex_lock(&mMutex); - mRetStatus = res; - mRetValReady = true; - pthread_mutex_unlock(&mMutex); - pthread_cond_signal(&mCond); -} - -} /* namespace PdrawBackend */ +/** + * Parrot Drones Awesome Video Viewer + * PDrAW back-end library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#define ULOG_TAG pdraw_backend +#include +ULOG_DECLARE_TAG(pdraw_backend); + +#include "pdraw_backend_impl.hpp" + +#ifdef _WIN32 +# define PIPE_BUF 4096 +#endif /* _WIN32 */ + +namespace PdrawBackend { + + +#define PDRAW_STATIC_ASSERT(x) typedef char __STATIC_ASSERT__[(x) ? 1 : -1] + + +enum cmd_type { + CMD_TYPE_STOP, + CMD_TYPE_DEMUXER_CREATE_SINGLE, + CMD_TYPE_DEMUXER_CREATE_URL, + CMD_TYPE_DEMUXER_CREATE_URL_MUX, + CMD_TYPE_DEMUXER_DESTROY, + CMD_TYPE_DEMUXER_CLOSE, + CMD_TYPE_DEMUXER_GET_SINGLE_STREAM_LOCAL_STREAM_PORT, + CMD_TYPE_DEMUXER_GET_SINGLE_STREAM_LOCAL_CONTROL_PORT, + CMD_TYPE_DEMUXER_IS_READY_TO_PLAY, + CMD_TYPE_DEMUXER_IS_PAUSED, + CMD_TYPE_DEMUXER_PLAY, + CMD_TYPE_DEMUXER_PREVIOUS_FRAME, + CMD_TYPE_DEMUXER_NEXT_FRAME, + CMD_TYPE_DEMUXER_SEEK, + CMD_TYPE_DEMUXER_SEEK_TO, + CMD_TYPE_DEMUXER_GET_CURRENT_TIME, + CMD_TYPE_DEMUXER_GET_DURATION, + CMD_TYPE_MUXER_CREATE, + CMD_TYPE_MUXER_DESTROY, + CMD_TYPE_MUXER_ADD_MEDIA, + CMD_TYPE_CODED_VIDEO_SINK_CREATE, + CMD_TYPE_CODED_VIDEO_SINK_DESTROY, + CMD_TYPE_CODED_VIDEO_SINK_RESYNC, + CMD_TYPE_CODED_VIDEO_SINK_GET_QUEUE, + CMD_TYPE_CODED_VIDEO_SINK_QUEUE_FLUSHED, + CMD_TYPE_RAW_VIDEO_SINK_CREATE, + CMD_TYPE_RAW_VIDEO_SINK_DESTROY, + CMD_TYPE_RAW_VIDEO_SINK_GET_QUEUE, + CMD_TYPE_RAW_VIDEO_SINK_QUEUE_FLUSHED, + CMD_TYPE_GET_FRIENDLY_NAME_SETTING, + CMD_TYPE_SET_FRIENDLY_NAME_SETTING, + CMD_TYPE_GET_SERIAL_NUMBER_SETTING, + CMD_TYPE_SET_SERIAL_NUMBER_SETTING, + CMD_TYPE_GET_SOFTWARE_VERSION_SETTING, + CMD_TYPE_SET_SOFTWARE_VERSION_SETTING, + CMD_TYPE_GET_PIPELINE_MODE_SETTING, + CMD_TYPE_SET_PIPELINE_MODE_SETTING, + CMD_TYPE_GET_DISPLAY_SCREEN_SETTINGS, + CMD_TYPE_SET_DISPLAY_SCREEN_SETTINGS, + CMD_TYPE_GET_HMD_MODEL_SETTING, + CMD_TYPE_SET_HMD_MODEL_SETTING, + CMD_TYPE_SET_ANDROID_JVM, + CMD_TYPE_DUMP_PIPELINE, +}; + + +struct cmd_msg { + enum cmd_type type; + union { + struct { + union { + bool boolean; + unsigned int uint; + uint64_t uint64; + int64_t int64; + float flt; + void *ptr; + char string[40]; + }; + } generic; + struct { + char local_addr[16]; + uint16_t local_stream_port; + uint16_t local_control_port; + char remote_addr[16]; + uint16_t remote_stream_port; + uint16_t remote_control_port; + IPdraw::IDemuxer::Listener *listener; + } demuxer_create_single; + struct { + char url[200]; + IPdraw::IDemuxer::Listener *listener; + } demuxer_create_url; + struct { + char url[200]; + struct mux_ctx *mux; + IPdraw::IDemuxer::Listener *listener; + } demuxer_create_url_mux; + struct { + PdrawBackend::Demuxer *demuxer; + float speed; + } demuxer_play; + struct { + PdrawBackend::Demuxer *demuxer; + int64_t delta; + bool exact; + } demuxer_seek; + struct { + PdrawBackend::Demuxer *demuxer; + uint64_t timestamp; + bool exact; + } demuxer_seek_to; + struct { + unsigned int media_id; + struct pdraw_video_sink_params params; + IPdraw::ICodedVideoSink::Listener *listener; + } coded_video_sink_create; + struct { + unsigned int media_id; + struct pdraw_video_sink_params params; + IPdraw::IRawVideoSink::Listener *listener; + } raw_video_sink_create; + struct { + char url[200]; + } muxer_create; + struct { + IPdraw::IMuxer *muxer; + unsigned int media_id; + struct pdraw_muxer_video_media_params params; + } muxer_add_media; + struct { + float xdpi; + float ydpi; + float device_margin_top; + float device_margin_bottom; + float device_margin_left; + float device_margin_right; + } set_display_screen_settings; + struct { + char file_name[200]; + } dump_pipeline; + }; +}; +PDRAW_STATIC_ASSERT(sizeof(struct cmd_msg) <= PIPE_BUF - 1); + + +/** + * Public functions + */ + +int createPdrawBackend(IPdraw::Listener *listener, IPdrawBackend **retObj) +{ + IPdrawBackend *self = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(listener == nullptr, EINVAL); + + self = new PdrawBackend(listener); + if (self == nullptr) { + ULOGE("failed to create pdraw backend instance"); + return -ENOMEM; + } + + *retObj = self; + return 0; +} + + +PdrawBackend::PdrawBackend(IPdraw::Listener *listener) +{ + mApiMutexCreated = false; + mApiReady = false; + mMutexCreated = false; + mCondCreated = false; + mLoopThreadLaunched = false; + mThreadShouldStop = false; + mLoop = nullptr; + mMbox = nullptr; + mStarted = false; + mRetValReady = false; + mRetStatus = 0; + mRetBool = false; + mRetUint16 = 0; + mRetUint64 = 0; + memset(mRetFloat, 0, sizeof(mRetFloat)); + mRetPipelineMode = PDRAW_PIPELINE_MODE_DECODE_ALL; + mRetHmdModel = PDRAW_HMD_MODEL_UNKNOWN; + mRetCodedQueue = nullptr; + mRetRawQueue = nullptr; + memset(&mRetMediaInfo, 0, sizeof(mRetMediaInfo)); + mRetCodedVideoSink = nullptr; + mRetRawVideoSink = nullptr; + mRetDemuxer = nullptr; + mRetMuxer = nullptr; + mPdraw = nullptr; + mListener = listener; + mMapsMutexCreated = false; + mPendingDemuxerAndListener = {}; + mPendingVideoRendererAndListener = {}; + mPendingCodedVideoSinkAndListener = {}; + mPendingRawVideoSinkAndListener = {}; + memset(&mLoopThread, 0, sizeof(pthread_t)); +} + + +PdrawBackend::~PdrawBackend(void) +{ + int err; + + if (mStarted) + ULOGW("destroying pdraw backend while still running"); + + mThreadShouldStop = true; + if (mLoop != nullptr) { + err = pomp_loop_wakeup(mLoop); + if (err < 0) + ULOG_ERRNO("pomp_loop_wakeup", -err); + } + if (mLoopThreadLaunched) { + err = pthread_join(mLoopThread, nullptr); + if (err != 0) + ULOG_ERRNO("pthread_join", err); + mLoopThreadLaunched = false; + } + if (mPdraw != nullptr) { + delete mPdraw; + mPdraw = nullptr; + } + if (mLoop != nullptr) { + err = pomp_loop_destroy(mLoop); + if (err < 0) + ULOG_ERRNO("pomp_loop_destroy", -err); + mLoop = nullptr; + } + mStarted = false; + if (mMbox != nullptr) { + mbox_destroy(mMbox); + mMbox = nullptr; + } + if (mCondCreated) { + err = pthread_cond_destroy(&mCond); + if (err != 0) + ULOG_ERRNO("pthread_cond_destroy", err); + mCondCreated = false; + } + if (mMutexCreated) { + err = pthread_mutex_destroy(&mMutex); + if (err != 0) + ULOG_ERRNO("pthread_mutex_destroy", err); + mMutexCreated = false; + } + if (mApiMutexCreated) { + err = pthread_mutex_destroy(&mApiMutex); + if (err != 0) + ULOG_ERRNO("pthread_mutex_destroy", err); + mApiMutexCreated = false; + } + if (mMapsMutexCreated) { + err = pthread_mutex_destroy(&mMapsMutex); + if (err != 0) + ULOG_ERRNO("pthread_mutex_destroy", err); + mMapsMutexCreated = false; + } +} + + +int PdrawBackend::start(void) +{ + int res; + pthread_mutexattr_t attr; + + ULOG_ERRNO_RETURN_ERR_IF(mListener == nullptr, EPROTO); + + res = pthread_mutexattr_init(&attr); + if (res != 0) { + ULOG_ERRNO("pthread_mutexattr_init", res); + return -res; + } + + res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + if (res != 0) { + pthread_mutexattr_destroy(&attr); + ULOG_ERRNO("pthread_mutexattr_settype", res); + return -res; + } + + res = pthread_mutex_init(&mApiMutex, &attr); + if (res != 0) { + ULOG_ERRNO("pthread_mutex_init", res); + return -res; + } + mApiMutexCreated = true; + pthread_mutexattr_destroy(&attr); + + res = pthread_mutex_init(&mMutex, nullptr); + if (res != 0) { + ULOG_ERRNO("pthread_mutex_init", res); + return -res; + } + mMutexCreated = true; + + res = pthread_cond_init(&mCond, nullptr); + if (res != 0) { + ULOG_ERRNO("pthread_cond_init", res); + return -res; + } + mCondCreated = true; + + res = pthread_mutex_init(&mMapsMutex, nullptr); + if (res != 0) { + ULOG_ERRNO("pthread_mutex_init", res); + return -res; + } + mMapsMutexCreated = true; + + mMbox = mbox_new(sizeof(cmd_msg)); + if (mMbox == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("mbox_new", -res); + return res; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + res = pthread_create(&mLoopThread, nullptr, &loopThread, (void *)this); + if (res != 0) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("pthread_create", res); + return -res; + } + mLoopThreadLaunched = true; + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + res = mRetStatus; + mRetStatus = 0; + mRetValReady = false; + mStarted = true; + mApiReady = true; + pthread_mutex_unlock(&mMutex); + + return res; +} + + +int PdrawBackend::stop(void) +{ + int res; + struct cmd_msg *cmd = nullptr; + + if (!mStarted) + return 0; + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + res = mPdraw->stop(); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_STOP; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + res = mRetStatus; + mRetStatus = false; + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + mStarted = false; + pthread_mutex_unlock(&mApiMutex); + return res; +} + + +struct pomp_loop *PdrawBackend::getLoop(void) +{ + ULOG_ERRNO_RETURN_VAL_IF(!mStarted, EPROTO, nullptr); + + return mLoop; +} + + +int PdrawBackend::createDemuxer(const std::string &url, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF( + url.length() > sizeof(cmd->demuxer_create_url.url) - 1, + ENOBUFS); + ULOG_ERRNO_RETURN_ERR_IF(!mStarted, EPROTO); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + res = doCreateDemuxer(url, listener, retObj); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_DEMUXER_CREATE_URL; + strncpy(cmd->demuxer_create_url.url, + url.c_str(), + sizeof(cmd->demuxer_create_url.url)); + cmd->demuxer_create_url.url[sizeof(cmd->demuxer_create_url.url) - 1] = + '\0'; + cmd->demuxer_create_url.listener = listener; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + res = mRetStatus; + *retObj = mRetDemuxer; + mRetStatus = 0; + mRetDemuxer = nullptr; + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); + return res; +} + + +int PdrawBackend::createDemuxer(const std::string &localAddr, + uint16_t localStreamPort, + uint16_t localControlPort, + const std::string &remoteAddr, + uint16_t remoteStreamPort, + uint16_t remoteControlPort, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF( + localAddr.length() > + sizeof(cmd->demuxer_create_single.local_addr) - 1, + ENOBUFS); + ULOG_ERRNO_RETURN_ERR_IF( + remoteAddr.length() > + sizeof(cmd->demuxer_create_single.remote_addr) - 1, + ENOBUFS); + ULOG_ERRNO_RETURN_ERR_IF(!mStarted, EPROTO); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + res = doCreateDemuxer(localAddr, + localStreamPort, + localControlPort, + remoteAddr, + remoteStreamPort, + remoteControlPort, + listener, + retObj); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_DEMUXER_CREATE_SINGLE; + strncpy(cmd->demuxer_create_single.local_addr, + localAddr.c_str(), + sizeof(cmd->demuxer_create_single.local_addr)); + cmd->demuxer_create_single + .local_addr[sizeof(cmd->demuxer_create_single.local_addr) - 1] = + '\0'; + cmd->demuxer_create_single.local_stream_port = localStreamPort; + cmd->demuxer_create_single.local_control_port = localControlPort; + strncpy(cmd->demuxer_create_single.remote_addr, + remoteAddr.c_str(), + sizeof(cmd->demuxer_create_single.remote_addr)); + cmd->demuxer_create_single + .remote_addr[sizeof(cmd->demuxer_create_single.remote_addr) - + 1] = '\0'; + cmd->demuxer_create_single.remote_stream_port = remoteStreamPort; + cmd->demuxer_create_single.remote_control_port = remoteControlPort; + cmd->demuxer_create_single.listener = listener; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + res = mRetStatus; + *retObj = mRetDemuxer; + mRetStatus = 0; + mRetDemuxer = nullptr; + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); + return res; +} + + +int PdrawBackend::createDemuxer(const std::string &url, + struct mux_ctx *mux, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF( + url.length() > sizeof(cmd->demuxer_create_url_mux.url) - 1, + ENOBUFS); + ULOG_ERRNO_RETURN_ERR_IF(!mStarted, EPROTO); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + res = doCreateDemuxer(url, mux, listener, retObj); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_DEMUXER_CREATE_URL_MUX; + strncpy(cmd->demuxer_create_url_mux.url, + url.c_str(), + sizeof(cmd->demuxer_create_url_mux.url)); + cmd->demuxer_create_url_mux + .url[sizeof(cmd->demuxer_create_url_mux.url) - 1] = '\0'; + cmd->demuxer_create_url_mux.mux = mux; + cmd->demuxer_create_url_mux.listener = listener; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + res = mRetStatus; + *retObj = mRetDemuxer; + mRetStatus = 0; + mRetDemuxer = nullptr; + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); + return res; +} + + +PdrawBackend::Demuxer::~Demuxer(void) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_IF(mDemuxer == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + delete mDemuxer; + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_DEMUXER_DESTROY; + cmd->generic.ptr = this; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); +} + + +int PdrawBackend::Demuxer::close(void) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + res = mDemuxer->close(); + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_DEMUXER_CLOSE; + cmd->generic.ptr = this; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + res = mBackend->mRetStatus; + mBackend->mRetStatus = 0; + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); + return res; +} + + +uint16_t PdrawBackend::Demuxer::getSingleStreamLocalStreamPort(void) +{ + int res; + uint16_t ret = 0; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_VAL_IF(mBackend == nullptr, EPROTO, 0); + ULOG_ERRNO_RETURN_VAL_IF(!mBackend->mStarted, EPROTO, 0); + ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EPROTO, 0); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + ret = mDemuxer->getSingleStreamLocalStreamPort(); + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_DEMUXER_GET_SINGLE_STREAM_LOCAL_STREAM_PORT; + cmd->generic.ptr = this; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + ret = mBackend->mRetUint16; + mBackend->mRetUint16 = 0; + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); + return ret; +} + + +uint16_t PdrawBackend::Demuxer::getSingleStreamLocalControlPort(void) +{ + int res; + uint16_t ret = 0; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_VAL_IF(mBackend == nullptr, EPROTO, 0); + ULOG_ERRNO_RETURN_VAL_IF(!mBackend->mStarted, EPROTO, 0); + ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EPROTO, 0); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + ret = mDemuxer->getSingleStreamLocalControlPort(); + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_DEMUXER_GET_SINGLE_STREAM_LOCAL_CONTROL_PORT; + cmd->generic.ptr = this; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + ret = mBackend->mRetUint16; + mBackend->mRetUint16 = 0; + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); + return ret; +} + + +bool PdrawBackend::Demuxer::isReadyToPlay(void) +{ + int res; + bool ret = false; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_VAL_IF(mBackend == nullptr, EPROTO, false); + ULOG_ERRNO_RETURN_VAL_IF(!mBackend->mStarted, EPROTO, false); + ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EPROTO, false); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + ret = mDemuxer->isReadyToPlay(); + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_DEMUXER_IS_READY_TO_PLAY; + cmd->generic.ptr = this; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + ret = mBackend->mRetBool; + mBackend->mRetBool = false; + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); + return ret; +} + + +bool PdrawBackend::Demuxer::isPaused(void) +{ + int res; + bool ret = false; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_VAL_IF(mBackend == nullptr, EPROTO, false); + ULOG_ERRNO_RETURN_VAL_IF(!mBackend->mStarted, EPROTO, false); + ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EPROTO, false); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + ret = mDemuxer->isPaused(); + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_DEMUXER_IS_PAUSED; + cmd->generic.ptr = this; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + ret = mBackend->mRetBool; + mBackend->mRetBool = false; + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); + return ret; +} + + +int PdrawBackend::Demuxer::play(float speed) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + res = mDemuxer->play(speed); + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_DEMUXER_PLAY; + cmd->demuxer_play.demuxer = this; + cmd->demuxer_play.speed = speed; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + res = mBackend->mRetStatus; + mBackend->mRetStatus = 0; + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); + return res; +} + + +int PdrawBackend::Demuxer::pause(void) +{ + return play(0.); +} + + +int PdrawBackend::Demuxer::previousFrame(void) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + res = mDemuxer->previousFrame(); + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_DEMUXER_PREVIOUS_FRAME; + cmd->generic.ptr = this; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + res = mBackend->mRetStatus; + mBackend->mRetStatus = 0; + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); + return res; +} + + +int PdrawBackend::Demuxer::nextFrame(void) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + res = mDemuxer->nextFrame(); + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_DEMUXER_NEXT_FRAME; + cmd->generic.ptr = this; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + res = mBackend->mRetStatus; + mBackend->mRetStatus = 0; + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); + return res; +} + + +int PdrawBackend::Demuxer::seek(int64_t delta, bool exact) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + res = mDemuxer->seek(delta, exact); + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_DEMUXER_SEEK; + cmd->demuxer_seek.demuxer = this; + cmd->demuxer_seek.delta = delta; + cmd->demuxer_seek.exact = exact; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + res = mBackend->mRetStatus; + mBackend->mRetStatus = 0; + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); + return res; +} + + +int PdrawBackend::Demuxer::seekForward(uint64_t delta, bool exact) +{ + return seek((int64_t)delta); +} + + +int PdrawBackend::Demuxer::seekBack(uint64_t delta, bool exact) +{ + return seek(-((int64_t)delta)); +} + + +int PdrawBackend::Demuxer::seekTo(uint64_t timestamp, bool exact) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + res = mDemuxer->seekTo(timestamp, exact); + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_DEMUXER_SEEK_TO; + cmd->demuxer_seek_to.demuxer = this; + cmd->demuxer_seek_to.timestamp = timestamp; + cmd->demuxer_seek_to.exact = exact; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + res = mBackend->mRetStatus; + mBackend->mRetStatus = 0; + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); + return res; +} + + +uint64_t PdrawBackend::Demuxer::getDuration(void) +{ + int res; + uint64_t ret = 0; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_VAL_IF(mBackend == nullptr, EPROTO, 0); + ULOG_ERRNO_RETURN_VAL_IF(!mBackend->mStarted, EPROTO, 0); + ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EPROTO, 0); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + ret = mDemuxer->getDuration(); + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_DEMUXER_GET_DURATION; + cmd->generic.ptr = this; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + ret = mBackend->mRetUint64; + mBackend->mRetUint64 = 0; + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); + return ret; +} + + +uint64_t PdrawBackend::Demuxer::getCurrentTime(void) +{ + int res; + uint64_t ret = 0; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_VAL_IF(mBackend == nullptr, EPROTO, 0); + ULOG_ERRNO_RETURN_VAL_IF(!mBackend->mStarted, EPROTO, 0); + ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EPROTO, 0); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + ret = mDemuxer->getCurrentTime(); + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_DEMUXER_GET_CURRENT_TIME; + cmd->generic.ptr = this; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + ret = mBackend->mRetUint64; + mBackend->mRetUint64 = 0; + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); + return ret; +} + + +int PdrawBackend::createMuxer(const std::string &url, IPdraw::IMuxer **retObj) +{ + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF( + url.length() > sizeof(cmd->muxer_create.url) - 1, ENOBUFS); + ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(!mStarted, EPROTO); + +#if MUXER_TEST + int res; + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + res = doCreateMuxer(url, retObj); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_MUXER_CREATE; + strncpy(cmd->muxer_create.url, + url.c_str(), + sizeof(cmd->muxer_create.url)); + cmd->muxer_create.url[sizeof(cmd->muxer_create.url) - 1] = '\0'; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + res = mRetStatus; + *retObj = mRetMuxer; + mRetStatus = 0; + mRetMuxer = nullptr; + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); + return res; + +#else + /* Not supported yet */ + return -ENOSYS; +#endif +} + + +PdrawBackend::Muxer::Muxer(PdrawBackend *backend, const std::string &url) +{ + mBackend = backend; + mMuxer = nullptr; +} + + +PdrawBackend::Muxer::~Muxer(void) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_IF(mMuxer == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + delete mMuxer; + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_MUXER_DESTROY; + cmd->generic.ptr = this; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); +} + + +int PdrawBackend::Muxer::addMedia( + unsigned int mediaId, + const struct pdraw_muxer_video_media_params *params) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(mMuxer == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + res = mMuxer->addMedia(mediaId, params); + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_MUXER_ADD_MEDIA; + cmd->muxer_add_media.muxer = this; + cmd->muxer_add_media.media_id = mediaId; + if (params != nullptr) + cmd->muxer_add_media.params = *params; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + res = mBackend->mRetStatus; + mBackend->mRetStatus = 0; + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); + return res; +} + + +/* Called on the rendering thread */ +int PdrawBackend::createVideoRenderer( + unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + IPdraw::IVideoRenderer::Listener *listener, + IPdraw::IVideoRenderer **retObj, + struct egl_display *eglDisplay) +{ + int res = 0; + PdrawBackend::VideoRenderer *renderer = nullptr; + IPdraw::IVideoRenderer *rnd = nullptr; + std::pair::iterator, + bool> + inserted; + + ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(!mStarted, EPROTO); + + pthread_mutex_lock(&mApiMutex); + + renderer = new PdrawBackend::VideoRenderer( + this, renderPos, params, listener, eglDisplay); + if (renderer == nullptr) + goto out; + + mPendingVideoRendererAndListener = { + .r = renderer, + .l = listener, + }; + + res = mPdraw->createVideoRenderer( + mediaId, renderPos, params, this, &rnd, eglDisplay); + if (res < 0) { + ULOG_ERRNO("pdraw->createVideoRenderer", -res); + delete renderer; + renderer = nullptr; + goto out; + } + renderer->setRenderer(rnd); + + pthread_mutex_lock(&mMapsMutex); + inserted = mVideoRendererListenersMap.insert( + std::pair( + renderer->getRenderer(), + mPendingVideoRendererAndListener)); + if (inserted.second == false) { + ULOGW("failed to insert the video renderer listener " + "in the map"); + } + pthread_mutex_unlock(&mMapsMutex); + +out: + mPendingVideoRendererAndListener = {}; + *retObj = renderer; + + pthread_mutex_unlock(&mApiMutex); + + return res; +} + + +/* Called on the rendering thread */ +PdrawBackend::VideoRenderer::VideoRenderer( + PdrawBackend *backend, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + IPdraw::IVideoRenderer::Listener *listener, + struct egl_display *eglDisplay) +{ + mBackend = backend; + mListener = listener; + mRenderer = nullptr; +} + + +/* Called on the rendering thread */ +PdrawBackend::VideoRenderer::~VideoRenderer(void) +{ + size_t erased; + + ULOG_ERRNO_RETURN_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_IF(!mBackend->mStarted, EPROTO); + + if (mRenderer == nullptr) + return; + + pthread_mutex_lock(&mBackend->mApiMutex); + + pthread_mutex_lock(&mBackend->mMapsMutex); + erased = mBackend->mVideoRendererListenersMap.erase(mRenderer); + if (erased != 1) { + ULOGW("failed to erase the video renderer listener " + "from the map"); + } + pthread_mutex_unlock(&mBackend->mMapsMutex); + + delete mRenderer; + mRenderer = nullptr; + + pthread_mutex_unlock(&mBackend->mApiMutex); +} + + +/* Called on the rendering thread */ +int PdrawBackend::VideoRenderer::resize(const struct pdraw_rect *renderPos) +{ + int res; + + ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(mRenderer == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + res = mRenderer->resize(renderPos); + + pthread_mutex_unlock(&mBackend->mApiMutex); + + return res; +} + + +/* Called on the rendering thread */ +int PdrawBackend::VideoRenderer::setMediaId(unsigned int mediaId) +{ + int res; + + ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(mRenderer == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + res = mRenderer->setMediaId(mediaId); + + pthread_mutex_unlock(&mBackend->mApiMutex); + + return res; +} + + +/* Called on the rendering thread */ +unsigned int PdrawBackend::VideoRenderer::getMediaId(void) +{ + unsigned int res; + + ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(mRenderer == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + res = mRenderer->getMediaId(); + + pthread_mutex_unlock(&mBackend->mApiMutex); + + return res; +} + + +/* Called on the rendering thread */ +int PdrawBackend::VideoRenderer::setParams( + const struct pdraw_video_renderer_params *params) +{ + int res; + + ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(mRenderer == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + res = mRenderer->setParams(params); + + pthread_mutex_unlock(&mBackend->mApiMutex); + + return res; +} + + +/* Called on the rendering thread */ +int PdrawBackend::VideoRenderer::getParams( + struct pdraw_video_renderer_params *params) +{ + int res; + + ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(mRenderer == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + res = mRenderer->getParams(params); + + pthread_mutex_unlock(&mBackend->mApiMutex); + + return res; +} + + +/* Called on the rendering thread */ +int PdrawBackend::VideoRenderer::render(struct pdraw_rect *contentPos, + const float *viewMat, + const float *projMat) +{ + int res; + + ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(mRenderer == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + res = mRenderer->render(contentPos, viewMat, projMat); + + pthread_mutex_unlock(&mBackend->mApiMutex); + + return res; +} + + +int PdrawBackend::createCodedVideoSink( + unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::ICodedVideoSink::Listener *listener, + IPdraw::ICodedVideoSink **retObj) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(!mStarted, EPROTO); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + res = doCreateCodedVideoSink(mediaId, params, listener, retObj); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_CODED_VIDEO_SINK_CREATE; + cmd->coded_video_sink_create.media_id = mediaId; + cmd->coded_video_sink_create.params = *params; + cmd->coded_video_sink_create.listener = listener; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + res = mRetStatus; + *retObj = mRetCodedVideoSink; + mRetStatus = 0; + mRetCodedVideoSink = nullptr; + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); + return res; +} + + +PdrawBackend::CodedVideoSink::CodedVideoSink( + PdrawBackend *backend, + unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::ICodedVideoSink::Listener *listener) +{ + mBackend = backend; + mListener = listener; + mSink = nullptr; +} + + +PdrawBackend::CodedVideoSink::~CodedVideoSink(void) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_IF(mSink == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + delete mSink; + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_CODED_VIDEO_SINK_DESTROY; + cmd->generic.ptr = this; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); +} + + +int PdrawBackend::CodedVideoSink::resync(void) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(mSink == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + res = mSink->resync(); + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_CODED_VIDEO_SINK_RESYNC; + cmd->generic.ptr = this; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + res = mBackend->mRetStatus; + mBackend->mRetStatus = 0; + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); + return res; +} + + +struct mbuf_coded_video_frame_queue * +PdrawBackend::CodedVideoSink::getQueue(void) +{ + int res; + struct mbuf_coded_video_frame_queue *ret = nullptr; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_VAL_IF(mBackend == nullptr, EPROTO, nullptr); + ULOG_ERRNO_RETURN_VAL_IF(!mBackend->mStarted, EPROTO, nullptr); + ULOG_ERRNO_RETURN_VAL_IF(mSink == nullptr, EPROTO, nullptr); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + ret = mSink->getQueue(); + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_CODED_VIDEO_SINK_GET_QUEUE; + cmd->generic.ptr = this; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + ret = mBackend->mRetCodedQueue; + mBackend->mRetCodedQueue = 0; + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); + return ret; +} + + +int PdrawBackend::CodedVideoSink::queueFlushed(void) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(mSink == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + res = mSink->queueFlushed(); + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_CODED_VIDEO_SINK_QUEUE_FLUSHED; + cmd->generic.ptr = this; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + res = mBackend->mRetStatus; + mBackend->mRetStatus = 0; + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); + return res; +} + + +int PdrawBackend::createRawVideoSink( + unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::IRawVideoSink::Listener *listener, + IPdraw::IRawVideoSink **retObj) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(!mStarted, EPROTO); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + res = doCreateRawVideoSink(mediaId, params, listener, retObj); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_RAW_VIDEO_SINK_CREATE; + cmd->raw_video_sink_create.media_id = mediaId; + cmd->raw_video_sink_create.params = *params; + cmd->raw_video_sink_create.listener = listener; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + res = mRetStatus; + *retObj = mRetRawVideoSink; + mRetStatus = 0; + mRetRawVideoSink = nullptr; + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); + return res; +} + + +PdrawBackend::RawVideoSink::RawVideoSink( + PdrawBackend *backend, + unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::IRawVideoSink::Listener *listener) +{ + mBackend = backend; + mListener = listener; + mSink = nullptr; +} + + +PdrawBackend::RawVideoSink::~RawVideoSink(void) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_IF(mSink == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + delete mSink; + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_RAW_VIDEO_SINK_DESTROY; + cmd->generic.ptr = this; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); +} + + +struct mbuf_raw_video_frame_queue *PdrawBackend::RawVideoSink::getQueue(void) +{ + int res; + struct mbuf_raw_video_frame_queue *ret = nullptr; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_VAL_IF(mBackend == nullptr, EPROTO, nullptr); + ULOG_ERRNO_RETURN_VAL_IF(!mBackend->mStarted, EPROTO, nullptr); + ULOG_ERRNO_RETURN_VAL_IF(mSink == nullptr, EPROTO, nullptr); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + ret = mSink->getQueue(); + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_RAW_VIDEO_SINK_GET_QUEUE; + cmd->generic.ptr = this; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + ret = mBackend->mRetRawQueue; + mBackend->mRetRawQueue = 0; + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); + return ret; +} + + +int PdrawBackend::RawVideoSink::queueFlushed(void) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(mBackend == nullptr, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(!mBackend->mStarted, EPROTO); + ULOG_ERRNO_RETURN_ERR_IF(mSink == nullptr, EPROTO); + + pthread_mutex_lock(&mBackend->mApiMutex); + + if (pthread_self() == mBackend->mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + res = mSink->queueFlushed(); + goto out2; + } + + pthread_mutex_lock(&mBackend->mMutex); + mBackend->mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_RAW_VIDEO_SINK_QUEUE_FLUSHED; + cmd->generic.ptr = this; + res = mbox_push(mBackend->mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mBackend->mRetValReady) + pthread_cond_wait(&mBackend->mCond, &mBackend->mMutex); + + res = mBackend->mRetStatus; + mBackend->mRetStatus = 0; + mBackend->mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mBackend->mMutex); + +out2: + pthread_mutex_unlock(&mBackend->mApiMutex); + return res; +} + + +void PdrawBackend::getFriendlyNameSetting(std::string *friendlyName) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + mPdraw->getFriendlyNameSetting(friendlyName); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_GET_FRIENDLY_NAME_SETTING; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + if (friendlyName) + *friendlyName = mRetString; + mRetString = ""; + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); +} + + +void PdrawBackend::setFriendlyNameSetting(const std::string &friendlyName) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); + ULOG_ERRNO_RETURN_IF(friendlyName.length() > + sizeof(cmd->generic.string) - 1, + ENOBUFS); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + mPdraw->setFriendlyNameSetting(friendlyName); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_SET_FRIENDLY_NAME_SETTING; + strncpy(cmd->generic.string, + friendlyName.c_str(), + sizeof(cmd->generic.string)); + cmd->generic.string[sizeof(cmd->generic.string) - 1] = '\0'; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); +} + + +void PdrawBackend::getSerialNumberSetting(std::string *serialNumber) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + mPdraw->getSerialNumberSetting(serialNumber); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_GET_SERIAL_NUMBER_SETTING; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + if (serialNumber) + *serialNumber = mRetString; + mRetString = ""; + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); +} + + +void PdrawBackend::setSerialNumberSetting(const std::string &serialNumber) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); + ULOG_ERRNO_RETURN_IF(serialNumber.length() > + sizeof(cmd->generic.string) - 1, + ENOBUFS); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + mPdraw->setSerialNumberSetting(serialNumber); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_SET_SERIAL_NUMBER_SETTING; + strncpy(cmd->generic.string, + serialNumber.c_str(), + sizeof(cmd->generic.string)); + cmd->generic.string[sizeof(cmd->generic.string) - 1] = '\0'; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); +} + + +void PdrawBackend::getSoftwareVersionSetting(std::string *softwareVersion) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + mPdraw->getSoftwareVersionSetting(softwareVersion); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_GET_SOFTWARE_VERSION_SETTING; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + if (softwareVersion) + *softwareVersion = mRetString; + mRetString = ""; + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); +} + + +void PdrawBackend::setSoftwareVersionSetting(const std::string &softwareVersion) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); + ULOG_ERRNO_RETURN_IF(softwareVersion.length() > + sizeof(cmd->generic.string) - 1, + ENOBUFS); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + mPdraw->setSoftwareVersionSetting(softwareVersion); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_SET_SOFTWARE_VERSION_SETTING; + strncpy(cmd->generic.string, + softwareVersion.c_str(), + sizeof(cmd->generic.string)); + cmd->generic.string[sizeof(cmd->generic.string) - 1] = '\0'; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); +} + + +enum pdraw_pipeline_mode PdrawBackend::getPipelineModeSetting(void) +{ + int res; + enum pdraw_pipeline_mode ret = PDRAW_PIPELINE_MODE_DECODE_ALL; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_VAL_IF( + !mStarted, EPROTO, PDRAW_PIPELINE_MODE_DECODE_ALL); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + ret = mPdraw->getPipelineModeSetting(); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_GET_PIPELINE_MODE_SETTING; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + ret = mRetPipelineMode; + mRetPipelineMode = PDRAW_PIPELINE_MODE_DECODE_ALL; + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); + return ret; +} + + +void PdrawBackend::setPipelineModeSetting(enum pdraw_pipeline_mode mode) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + mPdraw->setPipelineModeSetting(mode); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_SET_PIPELINE_MODE_SETTING; + cmd->generic.uint = mode; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); +} + + +void PdrawBackend::getDisplayScreenSettings(float *xdpi, + float *ydpi, + float *deviceMarginTop, + float *deviceMarginBottom, + float *deviceMarginLeft, + float *deviceMarginRight) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + mPdraw->getDisplayScreenSettings(xdpi, + ydpi, + deviceMarginTop, + deviceMarginBottom, + deviceMarginLeft, + deviceMarginRight); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_GET_DISPLAY_SCREEN_SETTINGS; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + if (xdpi) + *xdpi = mRetFloat[0]; + if (ydpi) + *ydpi = mRetFloat[1]; + if (deviceMarginTop) + *deviceMarginTop = mRetFloat[2]; + if (deviceMarginBottom) + *deviceMarginBottom = mRetFloat[3]; + if (deviceMarginLeft) + *deviceMarginLeft = mRetFloat[4]; + if (deviceMarginRight) + *deviceMarginRight = mRetFloat[5]; + memset(mRetFloat, 0, sizeof(mRetFloat)); + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); +} + + +void PdrawBackend::setDisplayScreenSettings(float xdpi, + float ydpi, + float deviceMarginTop, + float deviceMarginBottom, + float deviceMarginLeft, + float deviceMarginRight) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + mPdraw->setDisplayScreenSettings(xdpi, + ydpi, + deviceMarginTop, + deviceMarginBottom, + deviceMarginLeft, + deviceMarginRight); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_SET_DISPLAY_SCREEN_SETTINGS; + cmd->set_display_screen_settings.xdpi = xdpi; + cmd->set_display_screen_settings.ydpi = ydpi; + cmd->set_display_screen_settings.device_margin_top = deviceMarginTop; + cmd->set_display_screen_settings.device_margin_bottom = + deviceMarginBottom; + cmd->set_display_screen_settings.device_margin_left = deviceMarginLeft; + cmd->set_display_screen_settings.device_margin_right = + deviceMarginRight; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); +} + + +enum pdraw_hmd_model PdrawBackend::getHmdModelSetting(void) +{ + int res; + enum pdraw_hmd_model ret = PDRAW_HMD_MODEL_UNKNOWN; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_VAL_IF(!mStarted, EPROTO, ret); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + ret = mPdraw->getHmdModelSetting(); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_GET_HMD_MODEL_SETTING; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + ret = mRetHmdModel; + mRetHmdModel = PDRAW_HMD_MODEL_UNKNOWN; + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); + return ret; +} + + +void PdrawBackend::setHmdModelSetting(enum pdraw_hmd_model hmdModel) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + mPdraw->setHmdModelSetting(hmdModel); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_SET_HMD_MODEL_SETTING; + cmd->generic.uint = hmdModel; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); +} + + +void PdrawBackend::setAndroidJvm(void *jvm) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_IF(!mStarted, EPROTO); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + mPdraw->setAndroidJvm(jvm); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_SET_ANDROID_JVM; + cmd->generic.ptr = jvm; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); +} + + +int PdrawBackend::dumpPipeline(const std::string &fileName) +{ + int res; + struct cmd_msg *cmd = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF( + fileName.length() > sizeof(cmd->dump_pipeline.file_name) - 1, + ENOBUFS); + ULOG_ERRNO_RETURN_ERR_IF(!mStarted, EPROTO); + + pthread_mutex_lock(&mApiMutex); + + if (pthread_self() == mLoopThread) { + /* Execution is on the loop thread, + * call the function directly */ + res = mPdraw->dumpPipeline(fileName); + goto out2; + } + + pthread_mutex_lock(&mMutex); + mRetValReady = false; + + cmd = (struct cmd_msg *)calloc(1, sizeof(cmd_msg)); + if (cmd == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("calloc", -res); + goto out; + } + + /* Send a message to the loop */ + cmd->type = CMD_TYPE_DUMP_PIPELINE; + strncpy(cmd->dump_pipeline.file_name, + fileName.c_str(), + sizeof(cmd->dump_pipeline.file_name)); + cmd->dump_pipeline.file_name[sizeof(cmd->dump_pipeline.file_name) - 1] = + '\0'; + res = mbox_push(mMbox, cmd); + if (res < 0) { + ULOG_ERRNO("mbox_push", -res); + goto out; + } + + while (!mRetValReady) + pthread_cond_wait(&mCond, &mMutex); + + res = mRetStatus; + mRetStatus = 0; + mRetValReady = false; + +out: + free(cmd); + pthread_mutex_unlock(&mMutex); + +out2: + pthread_mutex_unlock(&mApiMutex); + return res; +} + + +/** + * Private functions + */ + +void PdrawBackend::stopResponse(IPdraw *pdraw, int status) +{ + if (pthread_self() != mLoopThread) + ULOGW("%s not called from the loop thread", __func__); + mListener->stopResponse(this, status); + mThreadShouldStop = true; + if (mLoop) { + int err = pomp_loop_wakeup(mLoop); + if (err < 0) + ULOG_ERRNO("pomp_loop_wakeup", -err); + } +} + + +void PdrawBackend::onMediaAdded(IPdraw *pdraw, + const struct pdraw_media_info *info) +{ + if (pthread_self() != mLoopThread) + ULOGW("%s not called from the loop thread", __func__); + mListener->onMediaAdded(this, info); +} + + +void PdrawBackend::onMediaRemoved(IPdraw *pdraw, + const struct pdraw_media_info *info) +{ + if (pthread_self() != mLoopThread) + ULOGW("%s not called from the loop thread", __func__); + mListener->onMediaRemoved(this, info); +} + + +void PdrawBackend::onSocketCreated(IPdraw *pdraw, int fd) +{ + if (pthread_self() != mLoopThread) + ULOGW("%s not called from the loop thread", __func__); + mListener->onSocketCreated(this, fd); +} + + +void PdrawBackend::demuxerOpenResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status) +{ + std::map::iterator it; + struct demuxerAndListener dl; + + if (pthread_self() != mLoopThread) + ULOGW("%s not called from the loop thread", __func__); + + pthread_mutex_lock(&mMapsMutex); + it = mDemuxerListenersMap.find(demuxer); + if (it == mDemuxerListenersMap.end()) { + dl = mPendingDemuxerAndListener; + } else { + dl = it->second; + } + pthread_mutex_unlock(&mMapsMutex); + if (dl.l == nullptr) { + ULOGE("%s: failed to find the demuxer listener in the map", + __func__); + return; + } + if (dl.d == nullptr) { + ULOGE("%s: failed to find the demuxer in the map", __func__); + return; + } + + dl.l->demuxerOpenResponse(this, dl.d, status); +} + + +void PdrawBackend::demuxerCloseResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status) +{ + std::map::iterator it; + struct demuxerAndListener dl; + + if (pthread_self() != mLoopThread) + ULOGW("%s not called from the loop thread", __func__); + + pthread_mutex_lock(&mMapsMutex); + it = mDemuxerListenersMap.find(demuxer); + if (it == mDemuxerListenersMap.end()) { + dl = mPendingDemuxerAndListener; + } else { + dl = it->second; + } + pthread_mutex_unlock(&mMapsMutex); + if (dl.l == nullptr) { + ULOGE("%s: failed to find the demuxer listener in the map", + __func__); + return; + } + if (dl.d == nullptr) { + ULOGE("%s: failed to find the demuxer in the map", __func__); + return; + } + + dl.l->demuxerCloseResponse(this, dl.d, status); +} + + +void PdrawBackend::onDemuxerUnrecoverableError(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer) +{ + std::map::iterator it; + struct demuxerAndListener dl; + + if (pthread_self() != mLoopThread) + ULOGW("%s not called from the loop thread", __func__); + + pthread_mutex_lock(&mMapsMutex); + it = mDemuxerListenersMap.find(demuxer); + if (it == mDemuxerListenersMap.end()) { + dl = mPendingDemuxerAndListener; + } else { + dl = it->second; + } + pthread_mutex_unlock(&mMapsMutex); + if (dl.l == nullptr) { + ULOGE("%s: failed to find the demuxer listener in the map", + __func__); + return; + } + if (dl.d == nullptr) { + ULOGE("%s: failed to find the demuxer in the map", __func__); + return; + } + + dl.l->onDemuxerUnrecoverableError(this, dl.d); +} + + +int PdrawBackend::demuxerSelectMedia(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + const struct pdraw_demuxer_media *medias, + size_t count) +{ + std::map::iterator it; + struct demuxerAndListener dl; + + if (pthread_self() != mLoopThread) + ULOGW("%s not called from the loop thread", __func__); + + pthread_mutex_lock(&mMapsMutex); + it = mDemuxerListenersMap.find(demuxer); + if (it == mDemuxerListenersMap.end()) { + dl = mPendingDemuxerAndListener; + } else { + dl = it->second; + } + pthread_mutex_unlock(&mMapsMutex); + if (dl.l == nullptr) { + ULOGE("%s: failed to find the demuxer listener in the map", + __func__); + return -ENOENT; + } + if (dl.d == nullptr) { + ULOGE("%s: failed to find the demuxer in the map", __func__); + return -ENOENT; + } + + return dl.l->demuxerSelectMedia(this, dl.d, medias, count); +} + + +void PdrawBackend::demuxerReadyToPlay(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + bool ready) +{ + std::map::iterator it; + struct demuxerAndListener dl; + + if (pthread_self() != mLoopThread) + ULOGW("%s not called from the loop thread", __func__); + + pthread_mutex_lock(&mMapsMutex); + it = mDemuxerListenersMap.find(demuxer); + if (it == mDemuxerListenersMap.end()) { + dl = mPendingDemuxerAndListener; + } else { + dl = it->second; + } + pthread_mutex_unlock(&mMapsMutex); + if (dl.l == nullptr) { + ULOGE("%s: failed to find the demuxer listener in the map", + __func__); + return; + } + if (dl.d == nullptr) { + ULOGE("%s: failed to find the demuxer in the map", __func__); + return; + } + + dl.l->demuxerReadyToPlay(this, dl.d, ready); +} + + +void PdrawBackend::onDemuxerEndOfRange(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + uint64_t timestamp) +{ + std::map::iterator it; + struct demuxerAndListener dl; + + if (pthread_self() != mLoopThread) + ULOGW("%s not called from the loop thread", __func__); + + pthread_mutex_lock(&mMapsMutex); + it = mDemuxerListenersMap.find(demuxer); + if (it == mDemuxerListenersMap.end()) { + dl = mPendingDemuxerAndListener; + } else { + dl = it->second; + } + pthread_mutex_unlock(&mMapsMutex); + if (dl.l == nullptr) { + ULOGE("%s: failed to find the demuxer listener in the map", + __func__); + return; + } + if (dl.d == nullptr) { + ULOGE("%s: failed to find the demuxer in the map", __func__); + return; + } + + dl.l->onDemuxerEndOfRange(this, dl.d, timestamp); +} + + +void PdrawBackend::demuxerPlayResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp, + float speed) +{ + std::map::iterator it; + struct demuxerAndListener dl; + + if (pthread_self() != mLoopThread) + ULOGW("%s not called from the loop thread", __func__); + + pthread_mutex_lock(&mMapsMutex); + it = mDemuxerListenersMap.find(demuxer); + if (it == mDemuxerListenersMap.end()) { + dl = mPendingDemuxerAndListener; + } else { + dl = it->second; + } + pthread_mutex_unlock(&mMapsMutex); + if (dl.l == nullptr) { + ULOGE("%s: failed to find the demuxer listener in the map", + __func__); + return; + } + if (dl.d == nullptr) { + ULOGE("%s: failed to find the demuxer in the map", __func__); + return; + } + + dl.l->demuxerPlayResponse(this, dl.d, status, timestamp, speed); +} + + +void PdrawBackend::demuxerPauseResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp) +{ + std::map::iterator it; + struct demuxerAndListener dl; + + if (pthread_self() != mLoopThread) + ULOGW("%s not called from the loop thread", __func__); + + pthread_mutex_lock(&mMapsMutex); + it = mDemuxerListenersMap.find(demuxer); + if (it == mDemuxerListenersMap.end()) { + dl = mPendingDemuxerAndListener; + } else { + dl = it->second; + } + pthread_mutex_unlock(&mMapsMutex); + if (dl.l == nullptr) { + ULOGE("%s: failed to find the demuxer listener in the map", + __func__); + return; + } + if (dl.d == nullptr) { + ULOGE("%s: failed to find the demuxer in the map", __func__); + return; + } + + dl.l->demuxerPauseResponse(this, dl.d, status, timestamp); +} + + +void PdrawBackend::demuxerSeekResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp, + float speed) +{ + std::map::iterator it; + struct demuxerAndListener dl; + + if (pthread_self() != mLoopThread) + ULOGW("%s not called from the loop thread", __func__); + + pthread_mutex_lock(&mMapsMutex); + it = mDemuxerListenersMap.find(demuxer); + if (it == mDemuxerListenersMap.end()) { + dl = mPendingDemuxerAndListener; + } else { + dl = it->second; + } + pthread_mutex_unlock(&mMapsMutex); + if (dl.l == nullptr) { + ULOGE("%s: failed to find the demuxer listener in the map", + __func__); + return; + } + if (dl.d == nullptr) { + ULOGE("%s: failed to find the demuxer in the map", __func__); + return; + } + + dl.l->demuxerSeekResponse(this, dl.d, status, timestamp, speed); +} + + +void PdrawBackend::onVideoRendererMediaAdded( + Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IVideoRenderer *renderer, + const struct pdraw_media_info *info) +{ + std::map::iterator it; + struct videoRendererAndListener rl; + + if (pthread_self() != mLoopThread) + ULOGW("%s not called from the loop thread", __func__); + + pthread_mutex_lock(&mMapsMutex); + it = mVideoRendererListenersMap.find(renderer); + if (it == mVideoRendererListenersMap.end()) { + rl = mPendingVideoRendererAndListener; + } else { + rl = it->second; + } + pthread_mutex_unlock(&mMapsMutex); + if (rl.l == nullptr) { + ULOGE("%s: failed to find the video renderer " + "listener in the map", + __func__); + return; + } + if (rl.r == nullptr) { + ULOGE("%s: failed to find the video renderer in the map", + __func__); + return; + } + + rl.l->onVideoRendererMediaAdded(this, rl.r, info); +} + + +void PdrawBackend::onVideoRendererMediaRemoved( + Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IVideoRenderer *renderer, + const struct pdraw_media_info *info) +{ + std::map::iterator it; + struct videoRendererAndListener rl; + + if (pthread_self() != mLoopThread) + ULOGW("%s not called from the loop thread", __func__); + + pthread_mutex_lock(&mMapsMutex); + it = mVideoRendererListenersMap.find(renderer); + if (it == mVideoRendererListenersMap.end()) { + rl = mPendingVideoRendererAndListener; + } else { + rl = it->second; + } + pthread_mutex_unlock(&mMapsMutex); + if (rl.l == nullptr) { + ULOGE("%s: failed to find the video renderer " + "listener in the map", + __func__); + return; + } + if (rl.r == nullptr) { + ULOGE("%s: failed to find the video renderer in the map", + __func__); + return; + } + + rl.l->onVideoRendererMediaRemoved(this, rl.r, info); +} + + +void PdrawBackend::onVideoRenderReady(IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer) +{ + std::map::iterator it; + struct videoRendererAndListener rl; + + if (pthread_self() != mLoopThread) + ULOGW("%s not called from the loop thread", __func__); + + pthread_mutex_lock(&mMapsMutex); + it = mVideoRendererListenersMap.find(renderer); + if (it == mVideoRendererListenersMap.end()) { + rl = mPendingVideoRendererAndListener; + } else { + rl = it->second; + } + pthread_mutex_unlock(&mMapsMutex); + if (rl.l == nullptr) { + ULOGE("%s: failed to find the video renderer " + "listener in the map", + __func__); + return; + } + if (rl.r == nullptr) { + ULOGE("%s: failed to find the video renderer in the map", + __func__); + return; + } + + rl.l->onVideoRenderReady(this, rl.r); +} + + +int PdrawBackend::loadVideoTexture(IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer, + unsigned int textureWidth, + unsigned int textureHeight, + const struct pdraw_media_info *mediaInfo, + struct mbuf_raw_video_frame *frame, + const void *frameUserdata, + size_t frameUserdataLen) +{ + std::map::iterator it; + struct videoRendererAndListener rl; + + pthread_mutex_lock(&mMapsMutex); + it = mVideoRendererListenersMap.find(renderer); + if (it == mVideoRendererListenersMap.end()) { + rl = mPendingVideoRendererAndListener; + } else { + rl = it->second; + } + pthread_mutex_unlock(&mMapsMutex); + if (rl.l == nullptr) { + ULOGE("%s: failed to find the video renderer " + "listener in the map", + __func__); + return -ENOENT; + } + if (rl.r == nullptr) { + ULOGE("%s: failed to find the video renderer in the map", + __func__); + return -ENOENT; + } + + return rl.l->loadVideoTexture(this, + rl.r, + textureWidth, + textureHeight, + mediaInfo, + frame, + frameUserdata, + frameUserdataLen); +} + + +int PdrawBackend::renderVideoOverlay( + IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer, + const struct pdraw_rect *renderPos, + const struct pdraw_rect *contentPos, + const float *viewMat, + const float *projMat, + const struct pdraw_media_info *mediaInfo, + struct vmeta_frame *frameMeta, + const struct pdraw_video_frame_extra *frameExtra) +{ + std::map::iterator it; + struct videoRendererAndListener rl; + + pthread_mutex_lock(&mMapsMutex); + it = mVideoRendererListenersMap.find(renderer); + if (it == mVideoRendererListenersMap.end()) { + rl = mPendingVideoRendererAndListener; + } else { + rl = it->second; + } + pthread_mutex_unlock(&mMapsMutex); + if (rl.l == nullptr) { + ULOGE("%s: failed to find the video renderer " + "listener in the map", + __func__); + return -ENOENT; + } + if (rl.r == nullptr) { + ULOGE("%s: failed to find the video renderer in the map", + __func__); + return -ENOENT; + } + + return rl.l->renderVideoOverlay(this, + rl.r, + renderPos, + contentPos, + viewMat, + projMat, + mediaInfo, + frameMeta, + frameExtra); +} + + +void PdrawBackend::onCodedVideoSinkFlush(IPdraw *pdraw, + IPdraw::ICodedVideoSink *sink) +{ + std::map::iterator it; + struct codedVideoSinkAndListener sl; + + if (pthread_self() != mLoopThread) + ULOGW("%s not called from the loop thread", __func__); + + pthread_mutex_lock(&mMapsMutex); + it = mCodedVideoSinkListenersMap.find(sink); + if (it == mCodedVideoSinkListenersMap.end()) { + sl = mPendingCodedVideoSinkAndListener; + } else { + sl = it->second; + } + pthread_mutex_unlock(&mMapsMutex); + if (sl.l == nullptr) { + ULOGE("%s: failed to find the video sink listener in the map", + __func__); + return; + } + if (sl.s == nullptr) { + ULOGE("%s: failed to find the video sink in the map", __func__); + return; + } + + sl.l->onCodedVideoSinkFlush(this, sl.s); +} + + +void PdrawBackend::onRawVideoSinkFlush(IPdraw *pdraw, + IPdraw::IRawVideoSink *sink) +{ + std::map::iterator it; + struct rawVideoSinkAndListener sl; + + if (pthread_self() != mLoopThread) + ULOGW("%s not called from the loop thread", __func__); + + pthread_mutex_lock(&mMapsMutex); + it = mRawVideoSinkListenersMap.find(sink); + if (it == mRawVideoSinkListenersMap.end()) { + sl = mPendingRawVideoSinkAndListener; + } else { + sl = it->second; + } + pthread_mutex_unlock(&mMapsMutex); + if (sl.l == nullptr) { + ULOGE("%s: failed to find the video sink listener in the map", + __func__); + return; + } + if (sl.s == nullptr) { + ULOGE("%s: failed to find the video sink in the map", __func__); + return; + } + + sl.l->onRawVideoSinkFlush(this, sl.s); +} + + +void *PdrawBackend::loopThread(void *ptr) +{ + PdrawBackend *self = (PdrawBackend *)ptr; + int res = 0, err; + + pthread_mutex_lock(&self->mMutex); + + if (self->mMbox == nullptr) { + res = -EPROTO; + ULOGE("invalid mailbox"); + goto error; + } + + self->mLoop = pomp_loop_new(); + if (self->mLoop == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("pomp_loop_new", -res); + goto error; + } + + res = pomp_loop_add(self->mLoop, + mbox_get_read_fd(self->mMbox), + POMP_FD_EVENT_IN, + &mboxCb, + self); + if (res < 0) { + ULOG_ERRNO("pomp_loop_add", -res); + goto error; + } + + res = createPdraw(self->mLoop, self, &self->mPdraw); + if (res < 0) { + ULOG_ERRNO("createPdraw", -res); + goto error; + } + + self->mRetStatus = 0; + self->mRetValReady = true; + pthread_mutex_unlock(&self->mMutex); + pthread_cond_signal(&self->mCond); + + while (!self->mThreadShouldStop) { + pomp_loop_wait_and_process(self->mLoop, -1); + } + + goto out; + +error: + self->mRetStatus = res; + self->mRetValReady = true; + pthread_mutex_unlock(&self->mMutex); + pthread_cond_signal(&self->mCond); +out: + if (self->mPdraw != nullptr) { + delete self->mPdraw; + self->mPdraw = nullptr; + } + if (self->mLoop != nullptr) { + if (self->mMbox != nullptr) { + err = pomp_loop_remove(self->mLoop, + mbox_get_read_fd(self->mMbox)); + if (err < 0) + ULOG_ERRNO("pomp_loop_remove", -err); + if (res == 0) + res = err; + } + + err = pomp_loop_destroy(self->mLoop); + if (err < 0) + ULOG_ERRNO("pomp_loop_destroy", -err); + if (res == 0) + res = err; + self->mLoop = nullptr; + } + + return (void *)(intptr_t)res; +} + + +void PdrawBackend::mboxCb(int fd, uint32_t revents, void *userdata) +{ + PdrawBackend *self = (PdrawBackend *)userdata; + int res; + void *message; + + message = malloc(sizeof(cmd_msg)); + if (message == nullptr) { + ULOG_ERRNO("malloc", ENOMEM); + return; + } + + do { + struct cmd_msg *msg = (struct cmd_msg *)message; + + /* Read from the mailbox */ + res = mbox_peek(self->mMbox, msg); + if (res < 0) { + if (res != -EAGAIN) + ULOG_ERRNO("mbox_peek", -res); + break; + } + + switch (msg->type) { + case CMD_TYPE_STOP: { + self->internalStop(); + break; + } + case CMD_TYPE_DEMUXER_CREATE_SINGLE: { + std::string l(msg->demuxer_create_single.local_addr); + std::string r(msg->demuxer_create_single.remote_addr); + self->internalDemuxerCreate( + l, + msg->demuxer_create_single.local_stream_port, + msg->demuxer_create_single.local_control_port, + r, + msg->demuxer_create_single.remote_stream_port, + msg->demuxer_create_single.remote_control_port, + msg->demuxer_create_single.listener); + break; + } + case CMD_TYPE_DEMUXER_CREATE_URL: { + std::string u(msg->demuxer_create_url.url); + self->internalDemuxerCreate( + u, msg->demuxer_create_url.listener); + break; + } + case CMD_TYPE_DEMUXER_CREATE_URL_MUX: { + std::string u(msg->demuxer_create_url_mux.url); + self->internalDemuxerCreate( + u, + msg->demuxer_create_url_mux.mux, + msg->demuxer_create_url_mux.listener); + break; + } + case CMD_TYPE_DEMUXER_DESTROY: { + self->internalDemuxerDestroy( + reinterpret_cast( + msg->generic.ptr)); + break; + } + case CMD_TYPE_DEMUXER_CLOSE: { + self->internalDemuxerClose( + reinterpret_cast( + msg->generic.ptr)); + break; + } + case CMD_TYPE_DEMUXER_GET_SINGLE_STREAM_LOCAL_STREAM_PORT: { + self->internalDemuxerGetSingleStreamLocalStreamPort( + reinterpret_cast( + msg->generic.ptr)); + break; + } + case CMD_TYPE_DEMUXER_GET_SINGLE_STREAM_LOCAL_CONTROL_PORT: { + self->internalDemuxerGetSingleStreamLocalControlPort( + reinterpret_cast( + msg->generic.ptr)); + break; + } + case CMD_TYPE_DEMUXER_IS_READY_TO_PLAY: { + self->internalDemuxerIsReadyToPlay( + reinterpret_cast( + msg->generic.ptr)); + break; + } + case CMD_TYPE_DEMUXER_IS_PAUSED: { + self->internalDemuxerIsPaused( + reinterpret_cast( + msg->generic.ptr)); + break; + } + case CMD_TYPE_DEMUXER_PLAY: { + self->internalDemuxerPlay(msg->demuxer_play.demuxer, + msg->demuxer_play.speed); + break; + } + case CMD_TYPE_DEMUXER_PREVIOUS_FRAME: { + self->internalDemuxerPreviousFrame( + reinterpret_cast( + msg->generic.ptr)); + break; + } + case CMD_TYPE_DEMUXER_NEXT_FRAME: { + self->internalDemuxerNextFrame( + reinterpret_cast( + msg->generic.ptr)); + break; + } + case CMD_TYPE_DEMUXER_SEEK: { + self->internalDemuxerSeek(msg->demuxer_seek.demuxer, + msg->demuxer_seek.delta, + msg->demuxer_seek.exact); + break; + } + case CMD_TYPE_DEMUXER_SEEK_TO: { + self->internalDemuxerSeekTo( + msg->demuxer_seek_to.demuxer, + msg->demuxer_seek_to.timestamp, + msg->demuxer_seek_to.exact); + break; + } + case CMD_TYPE_DEMUXER_GET_DURATION: { + self->internalDemuxerGetDuration( + reinterpret_cast( + msg->generic.ptr)); + break; + } + case CMD_TYPE_DEMUXER_GET_CURRENT_TIME: { + self->internalDemuxerGetCurrentTime( + reinterpret_cast( + msg->generic.ptr)); + break; + } + case CMD_TYPE_MUXER_CREATE: { + std::string u(msg->muxer_create.url); + self->internalMuxerCreate(u); + break; + } + case CMD_TYPE_MUXER_DESTROY: { + self->internalMuxerDestroy( + reinterpret_cast( + msg->generic.ptr)); + break; + } + case CMD_TYPE_MUXER_ADD_MEDIA: { + self->internalMuxerAddMedia( + reinterpret_cast( + msg->muxer_add_media.muxer), + msg->muxer_add_media.media_id, + &msg->muxer_add_media.params); + break; + } + case CMD_TYPE_CODED_VIDEO_SINK_CREATE: { + self->internalCodedVideoSinkCreate( + msg->coded_video_sink_create.media_id, + &msg->coded_video_sink_create.params, + msg->coded_video_sink_create.listener); + break; + } + case CMD_TYPE_CODED_VIDEO_SINK_DESTROY: { + self->internalCodedVideoSinkDestroy( + reinterpret_cast(msg->generic.ptr)); + break; + } + case CMD_TYPE_CODED_VIDEO_SINK_RESYNC: { + self->internalCodedVideoSinkResync( + reinterpret_cast(msg->generic.ptr)); + break; + } + case CMD_TYPE_CODED_VIDEO_SINK_GET_QUEUE: { + self->internalCodedVideoSinkGetQueue( + reinterpret_cast(msg->generic.ptr)); + break; + } + case CMD_TYPE_CODED_VIDEO_SINK_QUEUE_FLUSHED: { + self->internalCodedVideoSinkQueueFlushed( + reinterpret_cast(msg->generic.ptr)); + break; + } + case CMD_TYPE_RAW_VIDEO_SINK_CREATE: { + self->internalRawVideoSinkCreate( + msg->raw_video_sink_create.media_id, + &msg->raw_video_sink_create.params, + msg->raw_video_sink_create.listener); + break; + } + case CMD_TYPE_RAW_VIDEO_SINK_DESTROY: { + self->internalRawVideoSinkDestroy( + reinterpret_cast( + msg->generic.ptr)); + break; + } + case CMD_TYPE_RAW_VIDEO_SINK_GET_QUEUE: { + self->internalRawVideoSinkGetQueue( + reinterpret_cast( + msg->generic.ptr)); + break; + } + case CMD_TYPE_RAW_VIDEO_SINK_QUEUE_FLUSHED: { + self->internalRawVideoSinkQueueFlushed( + reinterpret_cast( + msg->generic.ptr)); + break; + } + case CMD_TYPE_GET_FRIENDLY_NAME_SETTING: { + self->internalGetFriendlyNameSetting(); + break; + } + case CMD_TYPE_SET_FRIENDLY_NAME_SETTING: { + std::string str = msg->generic.string; + self->internalSetFriendlyNameSetting(str); + break; + } + case CMD_TYPE_GET_SERIAL_NUMBER_SETTING: { + self->internalGetSerialNumberSetting(); + break; + } + case CMD_TYPE_SET_SERIAL_NUMBER_SETTING: { + std::string str = msg->generic.string; + self->internalSetSerialNumberSetting(str); + break; + } + case CMD_TYPE_GET_SOFTWARE_VERSION_SETTING: { + self->internalGetSoftwareVersionSetting(); + break; + } + case CMD_TYPE_SET_SOFTWARE_VERSION_SETTING: { + std::string str = msg->generic.string; + self->internalSetSoftwareVersionSetting(str); + break; + } + case CMD_TYPE_GET_PIPELINE_MODE_SETTING: { + self->internalGetPipelineModeSetting(); + break; + } + case CMD_TYPE_SET_PIPELINE_MODE_SETTING: { + self->internalSetPipelineModeSetting( + (enum pdraw_pipeline_mode)msg->generic.uint); + break; + } + case CMD_TYPE_GET_DISPLAY_SCREEN_SETTINGS: { + self->internalGetDisplayScreenSettings(); + break; + } + case CMD_TYPE_SET_DISPLAY_SCREEN_SETTINGS: { + self->internalSetDisplayScreenSettings( + msg->set_display_screen_settings.xdpi, + msg->set_display_screen_settings.ydpi, + msg->set_display_screen_settings + .device_margin_top, + msg->set_display_screen_settings + .device_margin_bottom, + msg->set_display_screen_settings + .device_margin_left, + msg->set_display_screen_settings + .device_margin_right); + break; + } + case CMD_TYPE_GET_HMD_MODEL_SETTING: { + self->internalGetHmdModelSetting(); + break; + } + case CMD_TYPE_SET_HMD_MODEL_SETTING: { + self->internalSetHmdModelSetting( + (enum pdraw_hmd_model)msg->generic.uint); + break; + } + case CMD_TYPE_SET_ANDROID_JVM: { + self->internalSetAndroidJvm(msg->generic.ptr); + break; + } + case CMD_TYPE_DUMP_PIPELINE: { + std::string f(msg->dump_pipeline.file_name); + self->internalDumpPipeline(f); + break; + } + default: + ULOGE("unknown command: %d", msg->type); + break; + } + } while (res == 0); + + free(message); +} + + +int PdrawBackend::doCreateDemuxer(const std::string &url, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj) +{ + int res; + PdrawBackend::Demuxer *demuxer = nullptr; + IPdraw::IDemuxer *d = nullptr; + std::pair::iterator, + bool> + inserted; + + demuxer = new PdrawBackend::Demuxer(this, listener); + if (demuxer == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("PdrawBackend::Demuxer", -res); + return res; + } + + mPendingDemuxerAndListener = { + .d = demuxer, + .l = listener, + }; + + res = mPdraw->createDemuxer(url, this, &d); + if (res < 0) { + ULOG_ERRNO("pdraw->createDemuxer", -res); + delete demuxer; + demuxer = nullptr; + return res; + } + demuxer->setDemuxer(d); + + pthread_mutex_lock(&mMapsMutex); + inserted = mDemuxerListenersMap.insert( + std::pair( + demuxer->getDemuxer(), mPendingDemuxerAndListener)); + if (inserted.second == false) + ULOGW("failed to insert the demuxer listener in the map"); + pthread_mutex_unlock(&mMapsMutex); + + *retObj = demuxer; + return 0; +} + + +int PdrawBackend::doCreateDemuxer(const std::string &localAddr, + uint16_t localStreamPort, + uint16_t localControlPort, + const std::string &remoteAddr, + uint16_t remoteStreamPort, + uint16_t remoteControlPort, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj) +{ + int res; + PdrawBackend::Demuxer *demuxer = nullptr; + IPdraw::IDemuxer *d = nullptr; + std::pair::iterator, + bool> + inserted; + + demuxer = new PdrawBackend::Demuxer(this, listener); + if (demuxer == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("PdrawBackend::Demuxer", -res); + return res; + } + + mPendingDemuxerAndListener = { + .d = demuxer, + .l = listener, + }; + + res = mPdraw->createDemuxer(localAddr, + localStreamPort, + localControlPort, + remoteAddr, + remoteStreamPort, + remoteControlPort, + this, + &d); + if (res < 0) { + ULOG_ERRNO("pdraw->createDemuxer", -res); + delete demuxer; + demuxer = nullptr; + return res; + } + demuxer->setDemuxer(d); + + pthread_mutex_lock(&mMapsMutex); + inserted = mDemuxerListenersMap.insert( + std::pair( + demuxer->getDemuxer(), mPendingDemuxerAndListener)); + if (inserted.second == false) + ULOGW("failed to insert the demuxer listener in the map"); + pthread_mutex_unlock(&mMapsMutex); + + *retObj = demuxer; + return 0; +} + + +int PdrawBackend::doCreateDemuxer(const std::string &url, + struct mux_ctx *mux, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj) +{ + int res; + PdrawBackend::Demuxer *demuxer = nullptr; + IPdraw::IDemuxer *d = nullptr; + std::pair::iterator, + bool> + inserted; + + demuxer = new PdrawBackend::Demuxer(this, listener); + if (demuxer == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("PdrawBackend::Demuxer", -res); + return res; + } + + mPendingDemuxerAndListener = { + .d = demuxer, + .l = listener, + }; + + res = mPdraw->createDemuxer(url, mux, this, &d); + if (res < 0) { + ULOG_ERRNO("pdraw->createDemuxer", -res); + delete demuxer; + demuxer = nullptr; + return res; + } + demuxer->setDemuxer(d); + + pthread_mutex_lock(&mMapsMutex); + inserted = mDemuxerListenersMap.insert( + std::pair( + demuxer->getDemuxer(), mPendingDemuxerAndListener)); + if (inserted.second == false) + ULOGW("failed to insert the demuxer listener in the map"); + pthread_mutex_unlock(&mMapsMutex); + + *retObj = demuxer; + return 0; +} + + +int PdrawBackend::doCreateMuxer(const std::string &url, IPdraw::IMuxer **retObj) +{ + int res; + PdrawBackend::Muxer *muxer = nullptr; + IPdraw::IMuxer *m = nullptr; + + muxer = new PdrawBackend::Muxer(this, url); + if (muxer == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("PdrawBackend::Muxer", -res); + return res; + } + + res = mPdraw->createMuxer(url, &m); + if (res < 0) { + ULOG_ERRNO("pdraw->createMuxer", -res); + delete muxer; + muxer = nullptr; + return res; + } + muxer->setMuxer(m); + + *retObj = muxer; + return 0; +} + + +int PdrawBackend::doCreateCodedVideoSink( + unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::ICodedVideoSink::Listener *listener, + IPdraw::ICodedVideoSink **retObj) +{ + int res; + PdrawBackend::CodedVideoSink *sink = nullptr; + IPdraw::ICodedVideoSink *s = nullptr; + std::pair::iterator, + bool> + inserted; + + sink = new PdrawBackend::CodedVideoSink( + this, mediaId, params, listener); + if (sink == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("PdrawBackend::CodedVideoSink", -res); + return res; + } + + mPendingCodedVideoSinkAndListener = { + .s = sink, + .l = listener, + }; + + res = mPdraw->createCodedVideoSink(mediaId, params, this, &s); + if (res < 0) { + ULOG_ERRNO("pdraw->createCodedVideoSink", -res); + delete sink; + sink = nullptr; + return res; + } + sink->setCodedVideoSink(s); + + pthread_mutex_lock(&mMapsMutex); + inserted = mCodedVideoSinkListenersMap.insert( + std::pair( + sink->getCodedVideoSink(), + mPendingCodedVideoSinkAndListener)); + if (inserted.second == false) + ULOGW("failed to insert the video sink listener in the map"); + pthread_mutex_unlock(&mMapsMutex); + + *retObj = sink; + return 0; +} + + +int PdrawBackend::doCreateRawVideoSink( + unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::IRawVideoSink::Listener *listener, + IPdraw::IRawVideoSink **retObj) +{ + int res; + PdrawBackend::RawVideoSink *sink = nullptr; + IPdraw::IRawVideoSink *s = nullptr; + std::pair::iterator, + bool> + inserted; + + sink = new PdrawBackend::RawVideoSink(this, mediaId, params, listener); + if (sink == nullptr) { + res = -ENOMEM; + ULOG_ERRNO("PdrawBackend::RawVideoSink", -res); + return res; + } + + mPendingRawVideoSinkAndListener = { + .s = sink, + .l = listener, + }; + + res = mPdraw->createRawVideoSink(mediaId, params, this, &s); + if (res < 0) { + ULOG_ERRNO("pdraw->createRawVideoSink", -res); + delete sink; + sink = nullptr; + return res; + } + sink->setRawVideoSink(s); + + pthread_mutex_lock(&mMapsMutex); + inserted = mRawVideoSinkListenersMap.insert( + std::pair( + sink->getRawVideoSink(), + mPendingRawVideoSinkAndListener)); + if (inserted.second == false) + ULOGW("failed to insert the video sink listener in the map"); + pthread_mutex_unlock(&mMapsMutex); + + *retObj = sink; + return 0; +} + + +void PdrawBackend::internalStop() +{ + int res = mPdraw->stop(); + + pthread_mutex_lock(&mMutex); + mRetStatus = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalDemuxerCreate(const std::string &url, + IPdraw::IDemuxer::Listener *listener) +{ + int res; + IPdraw::IDemuxer *demuxer = nullptr; + + res = doCreateDemuxer(url, listener, &demuxer); + + mPendingDemuxerAndListener = {}; + pthread_mutex_lock(&mMutex); + mRetStatus = res; + mRetDemuxer = demuxer; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalDemuxerCreate(const std::string &localAddr, + uint16_t localStreamPort, + uint16_t localControlPort, + const std::string &remoteAddr, + uint16_t remoteStreamPort, + uint16_t remoteControlPort, + IPdraw::IDemuxer::Listener *listener) +{ + int res; + IPdraw::IDemuxer *demuxer = nullptr; + + res = doCreateDemuxer(localAddr, + localStreamPort, + localControlPort, + remoteAddr, + remoteStreamPort, + remoteControlPort, + listener, + &demuxer); + + mPendingDemuxerAndListener = {}; + pthread_mutex_lock(&mMutex); + mRetStatus = res; + mRetDemuxer = demuxer; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalDemuxerCreate(const std::string &url, + struct mux_ctx *mux, + IPdraw::IDemuxer::Listener *listener) +{ + int res; + IPdraw::IDemuxer *demuxer = nullptr; + + res = doCreateDemuxer(url, mux, listener, &demuxer); + + mPendingDemuxerAndListener = {}; + pthread_mutex_lock(&mMutex); + mRetStatus = res; + mRetDemuxer = demuxer; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalDemuxerDestroy(PdrawBackend::Demuxer *demuxer) +{ + size_t erased; + + pthread_mutex_lock(&mMapsMutex); + erased = mDemuxerListenersMap.erase(demuxer->getDemuxer()); + if (erased != 1) + ULOGW("failed to erase the demuxer listener from the map"); + pthread_mutex_unlock(&mMapsMutex); + + delete demuxer->getDemuxer(); + + pthread_mutex_lock(&mMutex); + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalDemuxerClose(PdrawBackend::Demuxer *demuxer) +{ + int res = demuxer->getDemuxer()->close(); + + pthread_mutex_lock(&mMutex); + mRetStatus = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalDemuxerGetSingleStreamLocalStreamPort( + PdrawBackend::Demuxer *demuxer) +{ + uint16_t res = demuxer->getDemuxer()->getSingleStreamLocalStreamPort(); + + pthread_mutex_lock(&mMutex); + mRetUint16 = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalDemuxerGetSingleStreamLocalControlPort( + PdrawBackend::Demuxer *demuxer) +{ + uint16_t res = demuxer->getDemuxer()->getSingleStreamLocalControlPort(); + + pthread_mutex_lock(&mMutex); + mRetUint16 = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalDemuxerIsReadyToPlay(PdrawBackend::Demuxer *demuxer) +{ + bool res = demuxer->getDemuxer()->isReadyToPlay(); + + pthread_mutex_lock(&mMutex); + mRetBool = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalDemuxerIsPaused(PdrawBackend::Demuxer *demuxer) +{ + bool res = demuxer->getDemuxer()->isPaused(); + + pthread_mutex_lock(&mMutex); + mRetBool = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalDemuxerPlay(PdrawBackend::Demuxer *demuxer, + float speed) +{ + int res = demuxer->getDemuxer()->play(speed); + + pthread_mutex_lock(&mMutex); + mRetStatus = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalDemuxerPreviousFrame(PdrawBackend::Demuxer *demuxer) +{ + int res = demuxer->getDemuxer()->previousFrame(); + + pthread_mutex_lock(&mMutex); + mRetStatus = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalDemuxerNextFrame(PdrawBackend::Demuxer *demuxer) +{ + int res = demuxer->getDemuxer()->nextFrame(); + + pthread_mutex_lock(&mMutex); + mRetStatus = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalDemuxerSeek(PdrawBackend::Demuxer *demuxer, + int64_t delta, + bool exact) +{ + int res = demuxer->getDemuxer()->seek(delta, exact); + + pthread_mutex_lock(&mMutex); + mRetStatus = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalDemuxerSeekTo(PdrawBackend::Demuxer *demuxer, + uint64_t timestamp, + bool exact) +{ + int res = demuxer->getDemuxer()->seekTo(timestamp, exact); + + pthread_mutex_lock(&mMutex); + mRetStatus = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalDemuxerGetDuration(PdrawBackend::Demuxer *demuxer) +{ + uint64_t res = demuxer->getDemuxer()->getDuration(); + + pthread_mutex_lock(&mMutex); + mRetUint64 = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalDemuxerGetCurrentTime(PdrawBackend::Demuxer *demuxer) +{ + uint64_t res = demuxer->getDemuxer()->getCurrentTime(); + + pthread_mutex_lock(&mMutex); + mRetUint64 = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalMuxerCreate(const std::string &url) +{ + int res; + IPdraw::IMuxer *muxer = nullptr; + + res = doCreateMuxer(url, &muxer); + + pthread_mutex_lock(&mMutex); + mRetStatus = res; + mRetMuxer = muxer; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalMuxerDestroy(PdrawBackend::Muxer *muxer) +{ + delete muxer->getMuxer(); + + pthread_mutex_lock(&mMutex); + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalMuxerAddMedia( + PdrawBackend::Muxer *muxer, + unsigned int mediaId, + const struct pdraw_muxer_video_media_params *params) +{ + int res = muxer->getMuxer()->addMedia(mediaId, params); + + pthread_mutex_lock(&mMutex); + mRetStatus = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalCodedVideoSinkCreate( + unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::ICodedVideoSink::Listener *listener) +{ + int res; + IPdraw::ICodedVideoSink *sink = nullptr; + + res = doCreateCodedVideoSink(mediaId, params, listener, &sink); + + mPendingCodedVideoSinkAndListener = {}; + pthread_mutex_lock(&mMutex); + mRetStatus = res; + mRetCodedVideoSink = sink; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalCodedVideoSinkDestroy( + PdrawBackend::CodedVideoSink *sink) +{ + size_t erased; + + pthread_mutex_lock(&mMapsMutex); + erased = mCodedVideoSinkListenersMap.erase(sink->getCodedVideoSink()); + if (erased != 1) + ULOGW("failed to erase the video sink listener from the map"); + pthread_mutex_unlock(&mMapsMutex); + + delete sink->getCodedVideoSink(); + + pthread_mutex_lock(&mMutex); + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalCodedVideoSinkResync( + PdrawBackend::CodedVideoSink *sink) +{ + int res = sink->getCodedVideoSink()->resync(); + + pthread_mutex_lock(&mMutex); + mRetStatus = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalCodedVideoSinkGetQueue( + PdrawBackend::CodedVideoSink *sink) +{ + struct mbuf_coded_video_frame_queue *res = + sink->getCodedVideoSink()->getQueue(); + + pthread_mutex_lock(&mMutex); + mRetCodedQueue = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalCodedVideoSinkQueueFlushed( + PdrawBackend::CodedVideoSink *sink) +{ + int res = sink->getCodedVideoSink()->queueFlushed(); + + pthread_mutex_lock(&mMutex); + mRetStatus = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalRawVideoSinkCreate( + unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::IRawVideoSink::Listener *listener) +{ + int res; + IPdraw::IRawVideoSink *sink = nullptr; + + res = doCreateRawVideoSink(mediaId, params, listener, &sink); + + mPendingRawVideoSinkAndListener = {}; + pthread_mutex_lock(&mMutex); + mRetStatus = res; + mRetRawVideoSink = sink; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalRawVideoSinkDestroy(PdrawBackend::RawVideoSink *sink) +{ + size_t erased; + + pthread_mutex_lock(&mMapsMutex); + erased = mRawVideoSinkListenersMap.erase(sink->getRawVideoSink()); + if (erased != 1) + ULOGW("failed to erase the video sink listener from the map"); + pthread_mutex_unlock(&mMapsMutex); + + delete sink->getRawVideoSink(); + + pthread_mutex_lock(&mMutex); + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalRawVideoSinkGetQueue( + PdrawBackend::RawVideoSink *sink) +{ + struct mbuf_raw_video_frame_queue *res = + sink->getRawVideoSink()->getQueue(); + + pthread_mutex_lock(&mMutex); + mRetRawQueue = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalRawVideoSinkQueueFlushed( + PdrawBackend::RawVideoSink *sink) +{ + int res = sink->getRawVideoSink()->queueFlushed(); + + pthread_mutex_lock(&mMutex); + mRetStatus = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalGetFriendlyNameSetting(void) +{ + std::string friendlyName; + mPdraw->getFriendlyNameSetting(&friendlyName); + + pthread_mutex_lock(&mMutex); + mRetString = friendlyName; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalSetFriendlyNameSetting( + const std::string &friendlyName) +{ + mPdraw->setFriendlyNameSetting(friendlyName); + + pthread_mutex_lock(&mMutex); + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalGetSerialNumberSetting(void) +{ + std::string serialNumber; + mPdraw->getSerialNumberSetting(&serialNumber); + + pthread_mutex_lock(&mMutex); + mRetString = serialNumber; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalSetSerialNumberSetting( + const std::string &serialNumber) +{ + mPdraw->setSerialNumberSetting(serialNumber); + + pthread_mutex_lock(&mMutex); + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalGetSoftwareVersionSetting(void) +{ + std::string softwareVersion; + mPdraw->getSoftwareVersionSetting(&softwareVersion); + + pthread_mutex_lock(&mMutex); + mRetString = softwareVersion; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalSetSoftwareVersionSetting( + const std::string &softwareVersion) +{ + mPdraw->setSoftwareVersionSetting(softwareVersion); + + pthread_mutex_lock(&mMutex); + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalGetPipelineModeSetting(void) +{ + enum pdraw_pipeline_mode res = mPdraw->getPipelineModeSetting(); + + pthread_mutex_lock(&mMutex); + mRetPipelineMode = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalSetPipelineModeSetting(enum pdraw_pipeline_mode mode) +{ + mPdraw->setPipelineModeSetting(mode); + + pthread_mutex_lock(&mMutex); + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalGetDisplayScreenSettings(void) +{ + float xdpi = 0.f, ydpi = 0.f; + float deviceMarginTop = 0.f, deviceMarginBottom = 0.f; + float deviceMarginLeft = 0.f, deviceMarginRight = 0.f; + mPdraw->getDisplayScreenSettings(&xdpi, + &ydpi, + &deviceMarginTop, + &deviceMarginBottom, + &deviceMarginLeft, + &deviceMarginRight); + + pthread_mutex_lock(&mMutex); + mRetFloat[0] = xdpi; + mRetFloat[1] = ydpi; + mRetFloat[2] = deviceMarginTop; + mRetFloat[3] = deviceMarginBottom; + mRetFloat[4] = deviceMarginLeft; + mRetFloat[5] = deviceMarginRight; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalSetDisplayScreenSettings(float xdpi, + float ydpi, + float deviceMarginTop, + float deviceMarginBottom, + float deviceMarginLeft, + float deviceMarginRight) +{ + mPdraw->setDisplayScreenSettings(xdpi, + ydpi, + deviceMarginTop, + deviceMarginBottom, + deviceMarginLeft, + deviceMarginRight); + + pthread_mutex_lock(&mMutex); + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalGetHmdModelSetting(void) +{ + enum pdraw_hmd_model res = mPdraw->getHmdModelSetting(); + + pthread_mutex_lock(&mMutex); + mRetHmdModel = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalSetHmdModelSetting(enum pdraw_hmd_model hmdModel) +{ + mPdraw->setHmdModelSetting(hmdModel); + + pthread_mutex_lock(&mMutex); + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalSetAndroidJvm(void *jvm) +{ + mPdraw->setAndroidJvm(jvm); + + pthread_mutex_lock(&mMutex); + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + + +void PdrawBackend::internalDumpPipeline(const std::string &fileName) +{ + int res = mPdraw->dumpPipeline(fileName); + + pthread_mutex_lock(&mMutex); + mRetStatus = res; + mRetValReady = true; + pthread_mutex_unlock(&mMutex); + pthread_cond_signal(&mCond); +} + +} /* namespace PdrawBackend */ diff --git a/libpdraw-backend/src/pdraw_backend_impl.hpp b/libpdraw-backend/src/pdraw_backend_impl.hpp index 6dbca26..f00f57a 100644 --- a/libpdraw-backend/src/pdraw_backend_impl.hpp +++ b/libpdraw-backend/src/pdraw_backend_impl.hpp @@ -1,639 +1,639 @@ -/** - * Parrot Drones Awesome Video Viewer - * PDrAW back-end library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_BACKEND_IMPL_HPP_ -#define _PDRAW_BACKEND_IMPL_HPP_ - -#include - -#include -#include - -#include -#include -#include - -namespace PdrawBackend { - - -class PdrawBackend : public IPdrawBackend, - public IPdraw::Listener, - public IPdraw::IDemuxer::Listener, - public IPdraw::IVideoRenderer::Listener, - public IPdraw::ICodedVideoSink::Listener, - public IPdraw::IRawVideoSink::Listener { -public: - class Demuxer : public IPdraw::IDemuxer { - public: - Demuxer(PdrawBackend *backend, - IPdraw::IDemuxer::Listener *listener) : - mBackend(backend), - mListener(listener), mDemuxer(nullptr) - { - } - - ~Demuxer(void); - - int close(void); - - uint16_t getSingleStreamLocalStreamPort(void); - - uint16_t getSingleStreamLocalControlPort(void); - - bool isReadyToPlay(void); - - bool isPaused(void); - - int play(float speed = 1.0f); - - int pause(void); - - int previousFrame(void); - - int nextFrame(void); - - int seek(int64_t delta, bool exact = false); - - int seekForward(uint64_t delta, bool exact = false); - - int seekBack(uint64_t delta, bool exact = false); - - int seekTo(uint64_t timestamp, bool exact = false); - - uint64_t getDuration(void); - - uint64_t getCurrentTime(void); - - IPdraw::IDemuxer *getDemuxer() - { - return mDemuxer; - } - - void setDemuxer(IPdraw::IDemuxer *demuxer) - { - mDemuxer = demuxer; - } - - private: - PdrawBackend *mBackend; - IPdraw::IDemuxer::Listener *mListener; - IPdraw::IDemuxer *mDemuxer; - }; - - class Muxer : public IPdraw::IMuxer { - public: - Muxer(PdrawBackend *backend, const std::string &url); - - ~Muxer(void); - - int - addMedia(unsigned int mediaId, - const struct pdraw_muxer_video_media_params *params); - - IPdraw::IMuxer *getMuxer() - { - return mMuxer; - } - - void setMuxer(IPdraw::IMuxer *muxer) - { - mMuxer = muxer; - } - - private: - PdrawBackend *mBackend; - IPdraw::IMuxer *mMuxer; - }; - - class VideoRenderer : public IPdraw::IVideoRenderer { - public: - /* Called on the rendering thread */ - VideoRenderer(PdrawBackend *backend, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - IPdraw::IVideoRenderer::Listener *listener, - struct egl_display *eglDisplay = nullptr); - - /* Called on the rendering thread */ - ~VideoRenderer(void); - - /* Called on the rendering thread */ - int resize(const struct pdraw_rect *renderPos); - - /* Called on the rendering thread */ - int setMediaId(unsigned int mediaId); - - /* Called on the rendering thread */ - unsigned int getMediaId(void); - - /* Called on the rendering thread */ - int setParams(const struct pdraw_video_renderer_params *params); - - /* Called on the rendering thread */ - int getParams(struct pdraw_video_renderer_params *params); - - /* Called on the rendering thread */ - int render(struct pdraw_rect *contentPos, - const float *viewMat = nullptr, - const float *projMat = nullptr); - - IPdraw::IVideoRenderer *getRenderer() - { - return mRenderer; - } - - void setRenderer(IPdraw::IVideoRenderer *renderer) - { - mRenderer = renderer; - } - - private: - PdrawBackend *mBackend; - IPdraw::IVideoRenderer::Listener *mListener; - IPdraw::IVideoRenderer *mRenderer; - }; - - class CodedVideoSink : public IPdraw::ICodedVideoSink { - public: - CodedVideoSink(PdrawBackend *backend, - unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::ICodedVideoSink::Listener *listener); - - ~CodedVideoSink(void); - - int resync(void); - - struct mbuf_coded_video_frame_queue *getQueue(void); - - int queueFlushed(void); - - IPdraw::ICodedVideoSink *getCodedVideoSink() - { - return mSink; - } - - void setCodedVideoSink(IPdraw::ICodedVideoSink *sink) - { - mSink = sink; - } - - private: - PdrawBackend *mBackend; - IPdraw::ICodedVideoSink::Listener *mListener; - IPdraw::ICodedVideoSink *mSink; - }; - - class RawVideoSink : public IPdraw::IRawVideoSink { - public: - RawVideoSink(PdrawBackend *backend, - unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::IRawVideoSink::Listener *listener); - - ~RawVideoSink(void); - - int resync(void); - - struct mbuf_raw_video_frame_queue *getQueue(void); - - int queueFlushed(void); - - IPdraw::IRawVideoSink *getRawVideoSink() - { - return mSink; - } - - void setRawVideoSink(IPdraw::IRawVideoSink *sink) - { - mSink = sink; - } - - private: - PdrawBackend *mBackend; - IPdraw::IRawVideoSink::Listener *mListener; - IPdraw::IRawVideoSink *mSink; - }; - - PdrawBackend(IPdrawBackend::Listener *listener); - - ~PdrawBackend(void); - - int start(void); - - int stop(void); - - struct pomp_loop *getLoop(void); - - int createDemuxer(const std::string &url, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj); - - int createDemuxer(const std::string &localAddr, - uint16_t localStreamPort, - uint16_t localControlPort, - const std::string &remoteAddr, - uint16_t remoteStreamPort, - uint16_t remoteControlPort, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj); - - int createDemuxer(const std::string &url, - struct mux_ctx *mux, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj); - - int createMuxer(const std::string &url, IPdraw::IMuxer **retObj); - - /* Called on the rendering thread */ - int - createVideoRenderer(unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - IPdraw::IVideoRenderer::Listener *listener, - IPdraw::IVideoRenderer **retObj, - struct egl_display *eglDisplay = nullptr); - - int createCodedVideoSink(unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::ICodedVideoSink::Listener *listener, - IPdraw::ICodedVideoSink **retObj); - - int createRawVideoSink(unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::IRawVideoSink::Listener *listener, - IPdraw::IRawVideoSink **retObj); - - void getFriendlyNameSetting(std::string *friendlyName); - - void setFriendlyNameSetting(const std::string &friendlyName); - - void getSerialNumberSetting(std::string *serialNumber); - - void setSerialNumberSetting(const std::string &serialNumber); - - void getSoftwareVersionSetting(std::string *softwareVersion); - - void setSoftwareVersionSetting(const std::string &softwareVersion); - - enum pdraw_pipeline_mode getPipelineModeSetting(void); - - void setPipelineModeSetting(enum pdraw_pipeline_mode mode); - - void getDisplayScreenSettings(float *xdpi, - float *ydpi, - float *deviceMarginTop, - float *deviceMarginBottom, - float *deviceMarginLeft, - float *deviceMarginRight); - - void setDisplayScreenSettings(float xdpi, - float ydpi, - float deviceMarginTop, - float deviceMarginBottom, - float deviceMarginLeft, - float deviceMarginRight); - - enum pdraw_hmd_model getHmdModelSetting(void); - - void setHmdModelSetting(enum pdraw_hmd_model hmdModel); - - void setAndroidJvm(void *jvm); - - int dumpPipeline(const std::string &fileName); - -private: - void stopResponse(IPdraw *pdraw, int status); - - void onMediaAdded(IPdraw *pdraw, const struct pdraw_media_info *info); - - void onMediaRemoved(IPdraw *pdraw, const struct pdraw_media_info *info); - - void onSocketCreated(IPdraw *pdraw, int fd); - - void demuxerOpenResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status); - - void demuxerCloseResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status); - - void onDemuxerUnrecoverableError(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer); - - int demuxerSelectMedia(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - const struct pdraw_demuxer_media *medias, - size_t count); - - void demuxerReadyToPlay(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - bool ready); - - void onDemuxerEndOfRange(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - uint64_t timestamp); - - void demuxerPlayResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp, - float speed); - - void demuxerPauseResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp); - - void demuxerSeekResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp, - float speed); - - void onVideoRendererMediaAdded(IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer, - const struct pdraw_media_info *info); - - void onVideoRendererMediaRemoved(IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer, - const struct pdraw_media_info *info); - - void onVideoRenderReady(IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer); - - int loadVideoTexture(IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer, - unsigned int textureWidth, - unsigned int textureHeight, - const struct pdraw_media_info *mediaInfo, - struct mbuf_raw_video_frame *frame, - const void *frameUserdata, - size_t frameUserdataLen); - - int - renderVideoOverlay(IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer, - const struct pdraw_rect *renderPos, - const struct pdraw_rect *contentPos, - const float *viewMat, - const float *projMat, - const struct pdraw_media_info *mediaInfo, - struct vmeta_frame *frameMeta, - const struct pdraw_video_frame_extra *frameExtra); - - void onCodedVideoSinkFlush(IPdraw *pdraw, - IPdraw::ICodedVideoSink *sink); - - void onRawVideoSinkFlush(IPdraw *pdraw, IPdraw::IRawVideoSink *sink); - - static void *loopThread(void *ptr); - - static void mboxCb(int fd, uint32_t revents, void *userdata); - - int doCreateDemuxer(const std::string &url, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj); - - int doCreateDemuxer(const std::string &localAddr, - uint16_t localStreamPort, - uint16_t localControlPort, - const std::string &remoteAddr, - uint16_t remoteStreamPort, - uint16_t remoteControlPort, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj); - - int doCreateDemuxer(const std::string &url, - struct mux_ctx *mux, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj); - - int doCreateMuxer(const std::string &url, IPdraw::IMuxer **retObj); - - int doCreateCodedVideoSink(unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::ICodedVideoSink::Listener *listener, - IPdraw::ICodedVideoSink **retObj); - - int doCreateRawVideoSink(unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::IRawVideoSink::Listener *listener, - IPdraw::IRawVideoSink **retObj); - - void internalStop(void); - - void internalDemuxerCreate(const std::string &url, - IPdraw::IDemuxer::Listener *listener); - - void internalDemuxerCreate(const std::string &localAddr, - uint16_t localStreamPort, - uint16_t localControlPort, - const std::string &remoteAddr, - uint16_t remoteStreamPort, - uint16_t remoteControlPort, - IPdraw::IDemuxer::Listener *listener); - - void internalDemuxerCreate(const std::string &url, - struct mux_ctx *mux, - IPdraw::IDemuxer::Listener *listener); - - void internalDemuxerDestroy(PdrawBackend::Demuxer *demuxer); - - void internalDemuxerClose(PdrawBackend::Demuxer *demuxer); - - void internalDemuxerGetSingleStreamLocalStreamPort( - PdrawBackend::Demuxer *demuxer); - - void internalDemuxerGetSingleStreamLocalControlPort( - PdrawBackend::Demuxer *demuxer); - - void internalDemuxerIsReadyToPlay(PdrawBackend::Demuxer *demuxer); - - void internalDemuxerIsPaused(PdrawBackend::Demuxer *demuxer); - - void internalDemuxerPlay(PdrawBackend::Demuxer *demuxer, - float speed = 1.0f); - - void internalDemuxerPreviousFrame(PdrawBackend::Demuxer *demuxer); - - void internalDemuxerNextFrame(PdrawBackend::Demuxer *demuxer); - - void internalDemuxerSeek(PdrawBackend::Demuxer *demuxer, - int64_t delta, - bool exact = false); - - void internalDemuxerSeekTo(PdrawBackend::Demuxer *demuxer, - uint64_t timestamp, - bool exact = false); - - void internalDemuxerGetDuration(PdrawBackend::Demuxer *demuxer); - - void internalDemuxerGetCurrentTime(PdrawBackend::Demuxer *demuxer); - - void internalMuxerCreate(const std::string &url); - - void internalMuxerDestroy(PdrawBackend::Muxer *muxer); - - void internalMuxerAddMedia( - PdrawBackend::Muxer *muxer, - unsigned int mediaId, - const struct pdraw_muxer_video_media_params *params); - - void internalCodedVideoSinkCreate( - unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::ICodedVideoSink::Listener *listener); - - void internalCodedVideoSinkDestroy(PdrawBackend::CodedVideoSink *sink); - - void internalCodedVideoSinkResync(PdrawBackend::CodedVideoSink *sink); - - void internalCodedVideoSinkGetQueue(PdrawBackend::CodedVideoSink *sink); - - void - internalCodedVideoSinkQueueFlushed(PdrawBackend::CodedVideoSink *sink); - - void - internalRawVideoSinkCreate(unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::IRawVideoSink::Listener *listener); - - void internalRawVideoSinkDestroy(PdrawBackend::RawVideoSink *sink); - - void internalRawVideoSinkGetQueue(PdrawBackend::RawVideoSink *sink); - - void internalRawVideoSinkQueueFlushed(PdrawBackend::RawVideoSink *sink); - - void internalGetFriendlyNameSetting(void); - - void internalSetFriendlyNameSetting(const std::string &friendlyName); - - void internalGetSerialNumberSetting(void); - - void internalSetSerialNumberSetting(const std::string &serialNumber); - - void internalGetSoftwareVersionSetting(void); - - void - internalSetSoftwareVersionSetting(const std::string &softwareVersion); - - void internalGetPipelineModeSetting(void); - - void internalSetPipelineModeSetting(enum pdraw_pipeline_mode mode); - - void internalGetDisplayScreenSettings(void); - - void internalSetDisplayScreenSettings(float xdpi, - float ydpi, - float deviceMarginTop, - float deviceMarginBottom, - float deviceMarginLeft, - float deviceMarginRight); - - void internalGetHmdModelSetting(void); - - void internalSetHmdModelSetting(enum pdraw_hmd_model hmdModel); - - void internalSetAndroidJvm(void *jvm); - - void internalDumpPipeline(const std::string &fileName); - - struct demuxerAndListener { - IPdraw::IDemuxer *d; - IPdraw::IDemuxer::Listener *l; - }; - - struct videoRendererAndListener { - IPdraw::IVideoRenderer *r; - IPdraw::IVideoRenderer::Listener *l; - }; - - struct codedVideoSinkAndListener { - IPdraw::ICodedVideoSink *s; - IPdraw::ICodedVideoSink::Listener *l; - }; - - struct rawVideoSinkAndListener { - IPdraw::IRawVideoSink *s; - IPdraw::IRawVideoSink::Listener *l; - }; - - pthread_mutex_t mApiMutex; - bool mApiMutexCreated; - bool mApiReady; - pthread_mutex_t mMutex; - bool mMutexCreated; - pthread_cond_t mCond; - bool mCondCreated; - pthread_t mLoopThread; - bool mLoopThreadLaunched; - bool mThreadShouldStop; - struct pomp_loop *mLoop; - struct mbox *mMbox; - bool mStarted; - bool mRetValReady; - int mRetStatus; - bool mRetBool; - uint16_t mRetUint16; - uint64_t mRetUint64; - float mRetFloat[6]; - std::string mRetString; - enum pdraw_pipeline_mode mRetPipelineMode; - enum pdraw_hmd_model mRetHmdModel; - struct mbuf_coded_video_frame_queue *mRetCodedQueue; - struct mbuf_raw_video_frame_queue *mRetRawQueue; - struct pdraw_media_info mRetMediaInfo; - IPdraw::ICodedVideoSink *mRetCodedVideoSink; - IPdraw::IRawVideoSink *mRetRawVideoSink; - IPdraw::IDemuxer *mRetDemuxer; - IPdraw::IMuxer *mRetMuxer; - IPdraw *mPdraw; - IPdraw::Listener *mListener; - pthread_mutex_t mMapsMutex; - bool mMapsMutexCreated; - std::map - mDemuxerListenersMap; - struct demuxerAndListener mPendingDemuxerAndListener; - std::map - mVideoRendererListenersMap; - struct videoRendererAndListener mPendingVideoRendererAndListener; - std::map - mCodedVideoSinkListenersMap; - struct codedVideoSinkAndListener mPendingCodedVideoSinkAndListener; - std::map - mRawVideoSinkListenersMap; - struct rawVideoSinkAndListener mPendingRawVideoSinkAndListener; -}; - -} /* namespace PdrawBackend */ - -#endif /* !_PDRAW_BACKEND_IMPL_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer + * PDrAW back-end library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_BACKEND_IMPL_HPP_ +#define _PDRAW_BACKEND_IMPL_HPP_ + +#include + +#include +#include + +#include +#include +#include + +namespace PdrawBackend { + + +class PdrawBackend : public IPdrawBackend, + public IPdraw::Listener, + public IPdraw::IDemuxer::Listener, + public IPdraw::IVideoRenderer::Listener, + public IPdraw::ICodedVideoSink::Listener, + public IPdraw::IRawVideoSink::Listener { +public: + class Demuxer : public IPdraw::IDemuxer { + public: + Demuxer(PdrawBackend *backend, + IPdraw::IDemuxer::Listener *listener) : + mBackend(backend), + mListener(listener), mDemuxer(nullptr) + { + } + + ~Demuxer(void); + + int close(void); + + uint16_t getSingleStreamLocalStreamPort(void); + + uint16_t getSingleStreamLocalControlPort(void); + + bool isReadyToPlay(void); + + bool isPaused(void); + + int play(float speed = 1.0f); + + int pause(void); + + int previousFrame(void); + + int nextFrame(void); + + int seek(int64_t delta, bool exact = false); + + int seekForward(uint64_t delta, bool exact = false); + + int seekBack(uint64_t delta, bool exact = false); + + int seekTo(uint64_t timestamp, bool exact = false); + + uint64_t getDuration(void); + + uint64_t getCurrentTime(void); + + IPdraw::IDemuxer *getDemuxer() + { + return mDemuxer; + } + + void setDemuxer(IPdraw::IDemuxer *demuxer) + { + mDemuxer = demuxer; + } + + private: + PdrawBackend *mBackend; + IPdraw::IDemuxer::Listener *mListener; + IPdraw::IDemuxer *mDemuxer; + }; + + class Muxer : public IPdraw::IMuxer { + public: + Muxer(PdrawBackend *backend, const std::string &url); + + ~Muxer(void); + + int + addMedia(unsigned int mediaId, + const struct pdraw_muxer_video_media_params *params); + + IPdraw::IMuxer *getMuxer() + { + return mMuxer; + } + + void setMuxer(IPdraw::IMuxer *muxer) + { + mMuxer = muxer; + } + + private: + PdrawBackend *mBackend; + IPdraw::IMuxer *mMuxer; + }; + + class VideoRenderer : public IPdraw::IVideoRenderer { + public: + /* Called on the rendering thread */ + VideoRenderer(PdrawBackend *backend, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + IPdraw::IVideoRenderer::Listener *listener, + struct egl_display *eglDisplay = nullptr); + + /* Called on the rendering thread */ + ~VideoRenderer(void); + + /* Called on the rendering thread */ + int resize(const struct pdraw_rect *renderPos); + + /* Called on the rendering thread */ + int setMediaId(unsigned int mediaId); + + /* Called on the rendering thread */ + unsigned int getMediaId(void); + + /* Called on the rendering thread */ + int setParams(const struct pdraw_video_renderer_params *params); + + /* Called on the rendering thread */ + int getParams(struct pdraw_video_renderer_params *params); + + /* Called on the rendering thread */ + int render(struct pdraw_rect *contentPos, + const float *viewMat = nullptr, + const float *projMat = nullptr); + + IPdraw::IVideoRenderer *getRenderer() + { + return mRenderer; + } + + void setRenderer(IPdraw::IVideoRenderer *renderer) + { + mRenderer = renderer; + } + + private: + PdrawBackend *mBackend; + IPdraw::IVideoRenderer::Listener *mListener; + IPdraw::IVideoRenderer *mRenderer; + }; + + class CodedVideoSink : public IPdraw::ICodedVideoSink { + public: + CodedVideoSink(PdrawBackend *backend, + unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::ICodedVideoSink::Listener *listener); + + ~CodedVideoSink(void); + + int resync(void); + + struct mbuf_coded_video_frame_queue *getQueue(void); + + int queueFlushed(void); + + IPdraw::ICodedVideoSink *getCodedVideoSink() + { + return mSink; + } + + void setCodedVideoSink(IPdraw::ICodedVideoSink *sink) + { + mSink = sink; + } + + private: + PdrawBackend *mBackend; + IPdraw::ICodedVideoSink::Listener *mListener; + IPdraw::ICodedVideoSink *mSink; + }; + + class RawVideoSink : public IPdraw::IRawVideoSink { + public: + RawVideoSink(PdrawBackend *backend, + unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::IRawVideoSink::Listener *listener); + + ~RawVideoSink(void); + + int resync(void); + + struct mbuf_raw_video_frame_queue *getQueue(void); + + int queueFlushed(void); + + IPdraw::IRawVideoSink *getRawVideoSink() + { + return mSink; + } + + void setRawVideoSink(IPdraw::IRawVideoSink *sink) + { + mSink = sink; + } + + private: + PdrawBackend *mBackend; + IPdraw::IRawVideoSink::Listener *mListener; + IPdraw::IRawVideoSink *mSink; + }; + + PdrawBackend(IPdrawBackend::Listener *listener); + + ~PdrawBackend(void); + + int start(void); + + int stop(void); + + struct pomp_loop *getLoop(void); + + int createDemuxer(const std::string &url, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj); + + int createDemuxer(const std::string &localAddr, + uint16_t localStreamPort, + uint16_t localControlPort, + const std::string &remoteAddr, + uint16_t remoteStreamPort, + uint16_t remoteControlPort, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj); + + int createDemuxer(const std::string &url, + struct mux_ctx *mux, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj); + + int createMuxer(const std::string &url, IPdraw::IMuxer **retObj); + + /* Called on the rendering thread */ + int + createVideoRenderer(unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + IPdraw::IVideoRenderer::Listener *listener, + IPdraw::IVideoRenderer **retObj, + struct egl_display *eglDisplay = nullptr); + + int createCodedVideoSink(unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::ICodedVideoSink::Listener *listener, + IPdraw::ICodedVideoSink **retObj); + + int createRawVideoSink(unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::IRawVideoSink::Listener *listener, + IPdraw::IRawVideoSink **retObj); + + void getFriendlyNameSetting(std::string *friendlyName); + + void setFriendlyNameSetting(const std::string &friendlyName); + + void getSerialNumberSetting(std::string *serialNumber); + + void setSerialNumberSetting(const std::string &serialNumber); + + void getSoftwareVersionSetting(std::string *softwareVersion); + + void setSoftwareVersionSetting(const std::string &softwareVersion); + + enum pdraw_pipeline_mode getPipelineModeSetting(void); + + void setPipelineModeSetting(enum pdraw_pipeline_mode mode); + + void getDisplayScreenSettings(float *xdpi, + float *ydpi, + float *deviceMarginTop, + float *deviceMarginBottom, + float *deviceMarginLeft, + float *deviceMarginRight); + + void setDisplayScreenSettings(float xdpi, + float ydpi, + float deviceMarginTop, + float deviceMarginBottom, + float deviceMarginLeft, + float deviceMarginRight); + + enum pdraw_hmd_model getHmdModelSetting(void); + + void setHmdModelSetting(enum pdraw_hmd_model hmdModel); + + void setAndroidJvm(void *jvm); + + int dumpPipeline(const std::string &fileName); + +private: + void stopResponse(IPdraw *pdraw, int status); + + void onMediaAdded(IPdraw *pdraw, const struct pdraw_media_info *info); + + void onMediaRemoved(IPdraw *pdraw, const struct pdraw_media_info *info); + + void onSocketCreated(IPdraw *pdraw, int fd); + + void demuxerOpenResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status); + + void demuxerCloseResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status); + + void onDemuxerUnrecoverableError(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer); + + int demuxerSelectMedia(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + const struct pdraw_demuxer_media *medias, + size_t count); + + void demuxerReadyToPlay(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + bool ready); + + void onDemuxerEndOfRange(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + uint64_t timestamp); + + void demuxerPlayResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp, + float speed); + + void demuxerPauseResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp); + + void demuxerSeekResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp, + float speed); + + void onVideoRendererMediaAdded(IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer, + const struct pdraw_media_info *info); + + void onVideoRendererMediaRemoved(IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer, + const struct pdraw_media_info *info); + + void onVideoRenderReady(IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer); + + int loadVideoTexture(IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer, + unsigned int textureWidth, + unsigned int textureHeight, + const struct pdraw_media_info *mediaInfo, + struct mbuf_raw_video_frame *frame, + const void *frameUserdata, + size_t frameUserdataLen); + + int + renderVideoOverlay(IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer, + const struct pdraw_rect *renderPos, + const struct pdraw_rect *contentPos, + const float *viewMat, + const float *projMat, + const struct pdraw_media_info *mediaInfo, + struct vmeta_frame *frameMeta, + const struct pdraw_video_frame_extra *frameExtra); + + void onCodedVideoSinkFlush(IPdraw *pdraw, + IPdraw::ICodedVideoSink *sink); + + void onRawVideoSinkFlush(IPdraw *pdraw, IPdraw::IRawVideoSink *sink); + + static void *loopThread(void *ptr); + + static void mboxCb(int fd, uint32_t revents, void *userdata); + + int doCreateDemuxer(const std::string &url, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj); + + int doCreateDemuxer(const std::string &localAddr, + uint16_t localStreamPort, + uint16_t localControlPort, + const std::string &remoteAddr, + uint16_t remoteStreamPort, + uint16_t remoteControlPort, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj); + + int doCreateDemuxer(const std::string &url, + struct mux_ctx *mux, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj); + + int doCreateMuxer(const std::string &url, IPdraw::IMuxer **retObj); + + int doCreateCodedVideoSink(unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::ICodedVideoSink::Listener *listener, + IPdraw::ICodedVideoSink **retObj); + + int doCreateRawVideoSink(unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::IRawVideoSink::Listener *listener, + IPdraw::IRawVideoSink **retObj); + + void internalStop(void); + + void internalDemuxerCreate(const std::string &url, + IPdraw::IDemuxer::Listener *listener); + + void internalDemuxerCreate(const std::string &localAddr, + uint16_t localStreamPort, + uint16_t localControlPort, + const std::string &remoteAddr, + uint16_t remoteStreamPort, + uint16_t remoteControlPort, + IPdraw::IDemuxer::Listener *listener); + + void internalDemuxerCreate(const std::string &url, + struct mux_ctx *mux, + IPdraw::IDemuxer::Listener *listener); + + void internalDemuxerDestroy(PdrawBackend::Demuxer *demuxer); + + void internalDemuxerClose(PdrawBackend::Demuxer *demuxer); + + void internalDemuxerGetSingleStreamLocalStreamPort( + PdrawBackend::Demuxer *demuxer); + + void internalDemuxerGetSingleStreamLocalControlPort( + PdrawBackend::Demuxer *demuxer); + + void internalDemuxerIsReadyToPlay(PdrawBackend::Demuxer *demuxer); + + void internalDemuxerIsPaused(PdrawBackend::Demuxer *demuxer); + + void internalDemuxerPlay(PdrawBackend::Demuxer *demuxer, + float speed = 1.0f); + + void internalDemuxerPreviousFrame(PdrawBackend::Demuxer *demuxer); + + void internalDemuxerNextFrame(PdrawBackend::Demuxer *demuxer); + + void internalDemuxerSeek(PdrawBackend::Demuxer *demuxer, + int64_t delta, + bool exact = false); + + void internalDemuxerSeekTo(PdrawBackend::Demuxer *demuxer, + uint64_t timestamp, + bool exact = false); + + void internalDemuxerGetDuration(PdrawBackend::Demuxer *demuxer); + + void internalDemuxerGetCurrentTime(PdrawBackend::Demuxer *demuxer); + + void internalMuxerCreate(const std::string &url); + + void internalMuxerDestroy(PdrawBackend::Muxer *muxer); + + void internalMuxerAddMedia( + PdrawBackend::Muxer *muxer, + unsigned int mediaId, + const struct pdraw_muxer_video_media_params *params); + + void internalCodedVideoSinkCreate( + unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::ICodedVideoSink::Listener *listener); + + void internalCodedVideoSinkDestroy(PdrawBackend::CodedVideoSink *sink); + + void internalCodedVideoSinkResync(PdrawBackend::CodedVideoSink *sink); + + void internalCodedVideoSinkGetQueue(PdrawBackend::CodedVideoSink *sink); + + void + internalCodedVideoSinkQueueFlushed(PdrawBackend::CodedVideoSink *sink); + + void + internalRawVideoSinkCreate(unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::IRawVideoSink::Listener *listener); + + void internalRawVideoSinkDestroy(PdrawBackend::RawVideoSink *sink); + + void internalRawVideoSinkGetQueue(PdrawBackend::RawVideoSink *sink); + + void internalRawVideoSinkQueueFlushed(PdrawBackend::RawVideoSink *sink); + + void internalGetFriendlyNameSetting(void); + + void internalSetFriendlyNameSetting(const std::string &friendlyName); + + void internalGetSerialNumberSetting(void); + + void internalSetSerialNumberSetting(const std::string &serialNumber); + + void internalGetSoftwareVersionSetting(void); + + void + internalSetSoftwareVersionSetting(const std::string &softwareVersion); + + void internalGetPipelineModeSetting(void); + + void internalSetPipelineModeSetting(enum pdraw_pipeline_mode mode); + + void internalGetDisplayScreenSettings(void); + + void internalSetDisplayScreenSettings(float xdpi, + float ydpi, + float deviceMarginTop, + float deviceMarginBottom, + float deviceMarginLeft, + float deviceMarginRight); + + void internalGetHmdModelSetting(void); + + void internalSetHmdModelSetting(enum pdraw_hmd_model hmdModel); + + void internalSetAndroidJvm(void *jvm); + + void internalDumpPipeline(const std::string &fileName); + + struct demuxerAndListener { + IPdraw::IDemuxer *d; + IPdraw::IDemuxer::Listener *l; + }; + + struct videoRendererAndListener { + IPdraw::IVideoRenderer *r; + IPdraw::IVideoRenderer::Listener *l; + }; + + struct codedVideoSinkAndListener { + IPdraw::ICodedVideoSink *s; + IPdraw::ICodedVideoSink::Listener *l; + }; + + struct rawVideoSinkAndListener { + IPdraw::IRawVideoSink *s; + IPdraw::IRawVideoSink::Listener *l; + }; + + pthread_mutex_t mApiMutex; + bool mApiMutexCreated; + bool mApiReady; + pthread_mutex_t mMutex; + bool mMutexCreated; + pthread_cond_t mCond; + bool mCondCreated; + pthread_t mLoopThread; + bool mLoopThreadLaunched; + bool mThreadShouldStop; + struct pomp_loop *mLoop; + struct mbox *mMbox; + bool mStarted; + bool mRetValReady; + int mRetStatus; + bool mRetBool; + uint16_t mRetUint16; + uint64_t mRetUint64; + float mRetFloat[6]; + std::string mRetString; + enum pdraw_pipeline_mode mRetPipelineMode; + enum pdraw_hmd_model mRetHmdModel; + struct mbuf_coded_video_frame_queue *mRetCodedQueue; + struct mbuf_raw_video_frame_queue *mRetRawQueue; + struct pdraw_media_info mRetMediaInfo; + IPdraw::ICodedVideoSink *mRetCodedVideoSink; + IPdraw::IRawVideoSink *mRetRawVideoSink; + IPdraw::IDemuxer *mRetDemuxer; + IPdraw::IMuxer *mRetMuxer; + IPdraw *mPdraw; + IPdraw::Listener *mListener; + pthread_mutex_t mMapsMutex; + bool mMapsMutexCreated; + std::map + mDemuxerListenersMap; + struct demuxerAndListener mPendingDemuxerAndListener; + std::map + mVideoRendererListenersMap; + struct videoRendererAndListener mPendingVideoRendererAndListener; + std::map + mCodedVideoSinkListenersMap; + struct codedVideoSinkAndListener mPendingCodedVideoSinkAndListener; + std::map + mRawVideoSinkListenersMap; + struct rawVideoSinkAndListener mPendingRawVideoSinkAndListener; +}; + +} /* namespace PdrawBackend */ + +#endif /* !_PDRAW_BACKEND_IMPL_HPP_ */ diff --git a/libpdraw-backend/src/pdraw_backend_wrapper.cpp b/libpdraw-backend/src/pdraw_backend_wrapper.cpp index f2c87a2..e023d07 100644 --- a/libpdraw-backend/src/pdraw_backend_wrapper.cpp +++ b/libpdraw-backend/src/pdraw_backend_wrapper.cpp @@ -1,1620 +1,1620 @@ -/** - * Parrot Drones Awesome Video Viewer - * PDrAW back-end library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include - -#include -#include - -#define ULOG_TAG pdraw_backend -#include - -#include - -#include "pdraw_backend_impl.hpp" - - -class PdrawBackendListener : public Pdraw::IPdraw::Listener { -public: - PdrawBackendListener(struct pdraw_backend *pdraw, - const struct pdraw_backend_cbs *cbs, - void *userdata) : - mPdraw(pdraw), - mCbs(*cbs), mUserdata(userdata) - { - } - - ~PdrawBackendListener() {} - - void stopResponse(Pdraw::IPdraw *pdraw, int status) - { - if (mCbs.stop_resp) { - (*mCbs.stop_resp)(mPdraw, status, mUserdata); - } - } - - void onMediaAdded(Pdraw::IPdraw *pdraw, - const struct pdraw_media_info *info) - { - if (mCbs.media_added) { - (*mCbs.media_added)(mPdraw, info, mUserdata); - } - } - - void onMediaRemoved(Pdraw::IPdraw *pdraw, - const struct pdraw_media_info *info) - { - if (mCbs.media_removed) { - (*mCbs.media_removed)(mPdraw, info, mUserdata); - } - } - - void onSocketCreated(Pdraw::IPdraw *pdraw, int fd) - { - if (mCbs.socket_created) - (*mCbs.socket_created)(mPdraw, fd, mUserdata); - } - -private: - struct pdraw_backend *mPdraw; - struct pdraw_backend_cbs mCbs; - void *mUserdata; -}; - - -class PdrawBackendDemuxerListener : public Pdraw::IPdraw::IDemuxer::Listener { -public: - PdrawBackendDemuxerListener(struct pdraw_backend *pdraw, - const struct pdraw_backend_demuxer_cbs *cbs, - void *userdata) : - mPdraw(pdraw), - mCbs(*cbs), mUserdata(userdata), mDemuxer(nullptr) - { - } - - ~PdrawBackendDemuxerListener() {} - - void demuxerOpenResponse(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IDemuxer *demuxer, - int status) - { - if (mCbs.open_resp) { - (*mCbs.open_resp)( - mPdraw, - reinterpret_cast( - demuxer), - status, - mUserdata); - } - } - - void demuxerCloseResponse(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IDemuxer *demuxer, - int status) - { - if (mCbs.close_resp) { - (*mCbs.close_resp)( - mPdraw, - reinterpret_cast( - demuxer), - status, - mUserdata); - } - } - - void onDemuxerUnrecoverableError(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IDemuxer *demuxer) - { - if (mCbs.unrecoverable_error) { - (*mCbs.unrecoverable_error)( - mPdraw, - reinterpret_cast( - demuxer), - mUserdata); - } - } - - int demuxerSelectMedia(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IDemuxer *demuxer, - const struct pdraw_demuxer_media *medias, - size_t count) - { - if (mCbs.select_media) { - return (*mCbs.select_media)( - mPdraw, - reinterpret_cast( - demuxer), - medias, - count, - mUserdata); - } - return -ENOSYS; - } - - void demuxerReadyToPlay(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IDemuxer *demuxer, - bool ready) - { - if (mCbs.ready_to_play) { - (*mCbs.ready_to_play)( - mPdraw, - reinterpret_cast( - demuxer), - ready ? 1 : 0, - mUserdata); - } - } - - void onDemuxerEndOfRange(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IDemuxer *demuxer, - uint64_t timestamp) - { - if (mCbs.end_of_range) { - (*mCbs.end_of_range)( - mPdraw, - reinterpret_cast( - demuxer), - timestamp, - mUserdata); - } - } - - void demuxerPlayResponse(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp, - float speed) - { - if (mCbs.play_resp) { - (*mCbs.play_resp)( - mPdraw, - reinterpret_cast( - demuxer), - status, - timestamp, - speed, - mUserdata); - } - } - - void demuxerPauseResponse(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp) - { - if (mCbs.pause_resp) { - (*mCbs.pause_resp)( - mPdraw, - reinterpret_cast( - demuxer), - status, - timestamp, - mUserdata); - } - } - - void demuxerSeekResponse(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp, - float speed) - { - if (mCbs.seek_resp) { - (*mCbs.seek_resp)( - mPdraw, - reinterpret_cast( - demuxer), - status, - timestamp, - speed, - mUserdata); - } - } - - Pdraw::IPdraw::IDemuxer *getDemuxer() - { - return mDemuxer; - } - - void setDemuxer(Pdraw::IPdraw::IDemuxer *demuxer) - { - mDemuxer = demuxer; - } - -private: - struct pdraw_backend *mPdraw; - struct pdraw_backend_demuxer_cbs mCbs; - void *mUserdata; - Pdraw::IPdraw::IDemuxer *mDemuxer; -}; - - -class PdrawBackendVideoRendererListener - : public Pdraw::IPdraw::IVideoRenderer::Listener { -public: - PdrawBackendVideoRendererListener( - struct pdraw_backend *pdraw, - const struct pdraw_backend_video_renderer_cbs *cbs, - void *userdata) : - mPdraw(pdraw), - mCbs(*cbs), mUserdata(userdata), mRenderer(nullptr) - { - } - - ~PdrawBackendVideoRendererListener() {} - - void onVideoRendererMediaAdded(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IVideoRenderer *renderer, - const struct pdraw_media_info *info) - { - if (mCbs.media_added) - (*mCbs.media_added)( - mPdraw, - reinterpret_cast( - renderer), - info, - mUserdata); - } - - void - onVideoRendererMediaRemoved(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IVideoRenderer *renderer, - const struct pdraw_media_info *info) - { - if (mCbs.media_removed) - (*mCbs.media_removed)( - mPdraw, - reinterpret_cast( - renderer), - info, - mUserdata); - } - - void onVideoRenderReady(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IVideoRenderer *renderer) - { - if (mCbs.render_ready) - (*mCbs.render_ready)( - mPdraw, - reinterpret_cast( - renderer), - mUserdata); - } - - int loadVideoTexture(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IVideoRenderer *renderer, - unsigned int textureWidth, - unsigned int textureHeight, - const struct pdraw_media_info *mediaInfo, - struct mbuf_raw_video_frame *frame, - const void *frameUserdata, - size_t frameUserdataLen) - { - if (mCbs.load_texture == nullptr) - return -ENOSYS; - return (*mCbs.load_texture)( - mPdraw, - reinterpret_cast( - renderer), - textureWidth, - textureHeight, - mediaInfo, - frame, - frameUserdata, - frameUserdataLen, - mUserdata); - } - - int renderVideoOverlay(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IVideoRenderer *renderer, - const struct pdraw_rect *renderPos, - const struct pdraw_rect *contentPos, - const float *viewMat, - const float *projMat, - const struct pdraw_media_info *mediaInfo, - struct vmeta_frame *frameMeta, - const struct pdraw_video_frame_extra *frameExtra) - { - if (mCbs.render_overlay == nullptr) - return -ENOSYS; - if ((renderer == nullptr) || (renderPos == nullptr) || - (contentPos == nullptr) || (viewMat == nullptr) || - (projMat == nullptr) || (mediaInfo == nullptr)) - return -EINVAL; - (*mCbs.render_overlay)( - mPdraw, - reinterpret_cast( - renderer), - renderPos, - contentPos, - viewMat, - projMat, - mediaInfo, - frameMeta, - frameExtra, - mUserdata); - return 0; - } - - Pdraw::IPdraw::IVideoRenderer *getVideoRenderer() - { - return mRenderer; - } - - void setVideoRenderer(Pdraw::IPdraw::IVideoRenderer *renderer) - { - mRenderer = renderer; - } - -private: - struct pdraw_backend *mPdraw; - struct pdraw_backend_video_renderer_cbs mCbs; - void *mUserdata; - Pdraw::IPdraw::IVideoRenderer *mRenderer; -}; - - -class PdrawBackendCodedVideoSinkListener - : public Pdraw::IPdraw::ICodedVideoSink::Listener { -public: - PdrawBackendCodedVideoSinkListener( - struct pdraw_backend *pdraw, - const struct pdraw_backend_coded_video_sink_cbs *cbs, - void *userdata) : - mPdraw(pdraw), - mCbs(*cbs), mUserdata(userdata), mSink(nullptr) - { - } - - ~PdrawBackendCodedVideoSinkListener() {} - - void onCodedVideoSinkFlush(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::ICodedVideoSink *sink) - { - if (mCbs.flush) - (*mCbs.flush)( - mPdraw, - reinterpret_cast< - struct pdraw_coded_video_sink *>(sink), - mUserdata); - } - - Pdraw::IPdraw::ICodedVideoSink *getCodedVideoSink() - { - return mSink; - } - - void setCodedVideoSink(Pdraw::IPdraw::ICodedVideoSink *sink) - { - mSink = sink; - } - -private: - struct pdraw_backend *mPdraw; - struct pdraw_backend_coded_video_sink_cbs mCbs; - void *mUserdata; - Pdraw::IPdraw::ICodedVideoSink *mSink; -}; - - -class PdrawBackendRawVideoSinkListener - : public Pdraw::IPdraw::IRawVideoSink::Listener { -public: - PdrawBackendRawVideoSinkListener( - struct pdraw_backend *pdraw, - const struct pdraw_backend_raw_video_sink_cbs *cbs, - void *userdata) : - mPdraw(pdraw), - mCbs(*cbs), mUserdata(userdata), mSink(nullptr) - { - } - - ~PdrawBackendRawVideoSinkListener() {} - - void onRawVideoSinkFlush(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IRawVideoSink *sink) - { - if (mCbs.flush) - (*mCbs.flush)( - mPdraw, - reinterpret_cast( - sink), - mUserdata); - } - - Pdraw::IPdraw::IRawVideoSink *getRawVideoSink() - { - return mSink; - } - - void setRawVideoSink(Pdraw::IPdraw::IRawVideoSink *sink) - { - mSink = sink; - } - -private: - struct pdraw_backend *mPdraw; - struct pdraw_backend_raw_video_sink_cbs mCbs; - void *mUserdata; - Pdraw::IPdraw::IRawVideoSink *mSink; -}; - - -struct pdraw_backend { - PdrawBackend::IPdrawBackend *pdraw; - PdrawBackendListener *listener; - std::vector *demuxerListeners; - std::vector - *videoRendererListeners; - std::vector - *codedVideoSinkListeners; - std::vector *rawVideoSinkListeners; -}; - - -int pdraw_be_new(const struct pdraw_backend_cbs *cbs, - void *userdata, - struct pdraw_backend **ret_obj) -{ - int res = 0; - struct pdraw_backend *self; - - ULOG_ERRNO_RETURN_ERR_IF(cbs == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(ret_obj == nullptr, EINVAL); - - self = (struct pdraw_backend *)calloc(1, sizeof(*self)); - if (self == nullptr) - return -ENOMEM; - - self->demuxerListeners = - new std::vector(); - if (self->demuxerListeners == nullptr) { - res = -ENOMEM; - goto error; - } - - self->videoRendererListeners = - new std::vector(); - if (self->videoRendererListeners == nullptr) { - res = -ENOMEM; - goto error; - } - - self->codedVideoSinkListeners = - new std::vector(); - if (self->codedVideoSinkListeners == nullptr) { - res = -ENOMEM; - goto error; - } - - self->rawVideoSinkListeners = - new std::vector(); - if (self->rawVideoSinkListeners == nullptr) { - res = -ENOMEM; - goto error; - } - - self->listener = new PdrawBackendListener(self, cbs, userdata); - if (self->listener == nullptr) { - res = -ENOMEM; - goto error; - } - - res = createPdrawBackend(self->listener, &self->pdraw); - if (res < 0) - goto error; - - res = self->pdraw->start(); - if (res < 0) - goto error; - - *ret_obj = self; - return 0; - -error: - pdraw_be_destroy(self); - *ret_obj = nullptr; - return res; -} - - -int pdraw_be_destroy(struct pdraw_backend *self) -{ - if (self == nullptr) - return 0; - - if (self->pdraw != nullptr) { - self->pdraw->stop(); - delete self->pdraw; - self->pdraw = nullptr; - } - - if (self->listener != nullptr) - delete self->listener; - - if (self->codedVideoSinkListeners != nullptr) { - std::vector::iterator l = - self->codedVideoSinkListeners->begin(); - while (l != self->codedVideoSinkListeners->end()) { - if (*l != nullptr) - delete (*l); - l++; - } - self->codedVideoSinkListeners->clear(); - delete self->codedVideoSinkListeners; - } - - if (self->rawVideoSinkListeners != nullptr) { - std::vector::iterator l = - self->rawVideoSinkListeners->begin(); - while (l != self->rawVideoSinkListeners->end()) { - if (*l != nullptr) - delete (*l); - l++; - } - self->rawVideoSinkListeners->clear(); - delete self->rawVideoSinkListeners; - } - - if (self->videoRendererListeners != nullptr) { - std::vector::iterator r = - self->videoRendererListeners->begin(); - while (r != self->videoRendererListeners->end()) { - if (*r != nullptr) - delete (*r); - r++; - } - self->videoRendererListeners->clear(); - delete self->videoRendererListeners; - } - - if (self->demuxerListeners != nullptr) { - std::vector::iterator l = - self->demuxerListeners->begin(); - while (l != self->demuxerListeners->end()) { - if (*l != nullptr) - delete (*l); - l++; - } - self->demuxerListeners->clear(); - delete self->demuxerListeners; - } - - free(self); - return 0; -} - - -int pdraw_be_stop(struct pdraw_backend *self) -{ - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - - return self->pdraw->stop(); -} - - -struct pomp_loop *pdraw_be_get_loop(struct pdraw_backend *self) -{ - ULOG_ERRNO_RETURN_VAL_IF(self == nullptr, EINVAL, nullptr); - - return self->pdraw->getLoop(); -} - - -int pdraw_be_demuxer_new_from_url(struct pdraw_backend *self, - const char *url, - const struct pdraw_backend_demuxer_cbs *cbs, - void *userdata, - struct pdraw_demuxer **ret_obj) -{ - int res; - Pdraw::IPdraw::IDemuxer *demuxer = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(cbs == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(ret_obj == nullptr, EINVAL); - - PdrawBackendDemuxerListener *l = - new PdrawBackendDemuxerListener(self, cbs, userdata); - if (l == nullptr) { - ULOGE("failed to create demuxer listener"); - return -ENOMEM; - } - - std::string u(url ? url : ""); - res = self->pdraw->createDemuxer(u, l, &demuxer); - if (res < 0) { - delete l; - return res; - } - - l->setDemuxer(demuxer); - self->demuxerListeners->push_back(l); - - *ret_obj = reinterpret_cast(demuxer); - return 0; -} - - -int pdraw_be_demuxer_new_single_stream( - struct pdraw_backend *self, - const char *local_addr, - uint16_t local_stream_port, - uint16_t local_control_port, - const char *remote_addr, - uint16_t remote_stream_port, - uint16_t remote_control_port, - const struct pdraw_backend_demuxer_cbs *cbs, - void *userdata, - struct pdraw_demuxer **ret_obj) -{ - int res; - Pdraw::IPdraw::IDemuxer *demuxer = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(cbs == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(ret_obj == nullptr, EINVAL); - - PdrawBackendDemuxerListener *l = - new PdrawBackendDemuxerListener(self, cbs, userdata); - if (l == nullptr) { - ULOGE("failed to create demuxer listener"); - return -ENOMEM; - } - - std::string local(local_addr ? local_addr : ""); - std::string remote(remote_addr ? remote_addr : ""); - res = self->pdraw->createDemuxer(local, - local_stream_port, - local_control_port, - remote, - remote_stream_port, - remote_control_port, - l, - &demuxer); - if (res < 0) { - delete l; - return res; - } - - l->setDemuxer(demuxer); - self->demuxerListeners->push_back(l); - - *ret_obj = reinterpret_cast(demuxer); - return 0; -} - - -int pdraw_be_demuxer_new_from_url_on_mux( - struct pdraw_backend *self, - const char *url, - struct mux_ctx *mux, - const struct pdraw_backend_demuxer_cbs *cbs, - void *userdata, - struct pdraw_demuxer **ret_obj) -{ - int res; - Pdraw::IPdraw::IDemuxer *demuxer = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(cbs == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(ret_obj == nullptr, EINVAL); - - PdrawBackendDemuxerListener *l = - new PdrawBackendDemuxerListener(self, cbs, userdata); - if (l == nullptr) { - ULOGE("failed to create demuxer listener"); - return -ENOMEM; - } - - std::string u(url ? url : ""); - res = self->pdraw->createDemuxer(u, mux, l, &demuxer); - if (res < 0) { - delete l; - return res; - } - - l->setDemuxer(demuxer); - self->demuxerListeners->push_back(l); - - *ret_obj = reinterpret_cast(demuxer); - return 0; -} - - -int pdraw_be_demuxer_destroy(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer) -{ - std::vector::iterator l; - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); - - l = self->demuxerListeners->begin(); - while (l != self->demuxerListeners->end()) { - if ((*l)->getDemuxer() != d) { - l++; - continue; - } - delete *l; - self->demuxerListeners->erase(l); - break; - } - - delete d; - - return 0; -} - - -int pdraw_be_demuxer_close(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); - - return d->close(); -} - - -uint16_t pdraw_be_demuxer_get_single_stream_local_stream_port( - struct pdraw_backend *self, - struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - ULOG_ERRNO_RETURN_VAL_IF(self == nullptr, EINVAL, 0); - ULOG_ERRNO_RETURN_VAL_IF(demuxer == nullptr, EINVAL, 0); - - return d->getSingleStreamLocalStreamPort(); -} - - -uint16_t pdraw_be_demuxer_get_single_stream_local_control_port( - struct pdraw_backend *self, - struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - ULOG_ERRNO_RETURN_VAL_IF(self == nullptr, EINVAL, 0); - ULOG_ERRNO_RETURN_VAL_IF(demuxer == nullptr, EINVAL, 0); - - return d->getSingleStreamLocalControlPort(); -} - - -int pdraw_be_demuxer_play(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); - - return d->play(); -} - - -int pdraw_be_demuxer_play_with_speed(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer, - float speed) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); - - return d->play(speed); -} - - -int pdraw_be_demuxer_is_ready_to_play(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); - - return (d->isReadyToPlay()) ? 1 : 0; -} - - -int pdraw_be_demuxer_pause(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); - - return d->pause(); -} - - -int pdraw_be_demuxer_is_paused(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); - - return (d->isPaused()) ? 1 : 0; -} - - -int pdraw_be_demuxer_previous_frame(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); - - return d->previousFrame(); -} - - -int pdraw_be_demuxer_next_frame(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); - - return d->nextFrame(); -} - - -int pdraw_be_demuxer_seek(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer, - int64_t delta, - int exact) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); - - return d->seek(delta, exact ? true : false); -} - - -int pdraw_be_demuxer_seek_forward(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer, - uint64_t delta, - int exact) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); - - return d->seekForward(delta, exact ? true : false); -} - - -int pdraw_be_demuxer_seek_back(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer, - uint64_t delta, - int exact) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); - - return d->seekBack(delta, exact ? true : false); -} - - -int pdraw_be_demuxer_seek_to(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer, - uint64_t timestamp, - int exact) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); - - return d->seekTo(timestamp, exact ? true : false); -} - - -uint64_t pdraw_be_demuxer_get_duration(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - ULOG_ERRNO_RETURN_VAL_IF(self == nullptr, EINVAL, 0); - ULOG_ERRNO_RETURN_VAL_IF(demuxer == nullptr, EINVAL, 0); - - return d->getDuration(); -} - - -uint64_t pdraw_be_demuxer_get_current_time(struct pdraw_backend *self, - struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - ULOG_ERRNO_RETURN_VAL_IF(self == nullptr, EINVAL, 0); - ULOG_ERRNO_RETURN_VAL_IF(demuxer == nullptr, EINVAL, 0); - - return d->getCurrentTime(); -} - - -int pdraw_be_muxer_new(struct pdraw_backend *self, - const char *url, - struct pdraw_muxer **ret_obj) -{ - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(ret_obj == nullptr, EINVAL); - -#if MUXER_TEST - int res; - Pdraw::IPdraw::IMuxer *muxer = nullptr; - - std::string u(url ? url : ""); - - res = self->pdraw->createMuxer(u, &muxer); - if (res < 0) - return res; - - *ret_obj = reinterpret_cast(muxer); - return 0; -#else - /* Not supported yet */ - return -ENOSYS; -#endif -} - - -int pdraw_be_muxer_destroy(struct pdraw_backend *self, - struct pdraw_muxer *muxer) -{ - Pdraw::IPdraw::IMuxer *m = - reinterpret_cast(muxer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(muxer == nullptr, EINVAL); - - delete m; - - return 0; -} - - -int pdraw_be_muxer_add_media( - struct pdraw_backend *self, - struct pdraw_muxer *muxer, - unsigned int media_id, - const struct pdraw_muxer_video_media_params *params) -{ - Pdraw::IPdraw::IMuxer *m = - reinterpret_cast(muxer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(muxer == nullptr, EINVAL); - - return m->addMedia(media_id, params); -} - - -int pdraw_be_video_renderer_new( - struct pdraw_backend *self, - unsigned int media_id, - const struct pdraw_rect *render_pos, - const struct pdraw_video_renderer_params *params, - const struct pdraw_backend_video_renderer_cbs *cbs, - void *userdata, - struct pdraw_video_renderer **ret_obj) -{ - return pdraw_be_video_renderer_new_egl(self, - media_id, - render_pos, - params, - cbs, - userdata, - nullptr, - ret_obj); -} - - -int pdraw_be_video_renderer_new_egl( - struct pdraw_backend *self, - unsigned int media_id, - const struct pdraw_rect *render_pos, - const struct pdraw_video_renderer_params *params, - const struct pdraw_backend_video_renderer_cbs *cbs, - void *userdata, - struct egl_display *egl_display, - struct pdraw_video_renderer **ret_obj) -{ - int res; - Pdraw::IPdraw::IVideoRenderer *renderer = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(ret_obj == nullptr, EINVAL); - - PdrawBackendVideoRendererListener *l = - new PdrawBackendVideoRendererListener(self, cbs, userdata); - if (l == nullptr) { - ULOGE("failed to create video renderer listener"); - return -ENOMEM; - } - - res = self->pdraw->createVideoRenderer( - media_id, render_pos, params, l, &renderer, egl_display); - if (res < 0) { - delete l; - return res; - } - - self->videoRendererListeners->push_back(l); - l->setVideoRenderer(renderer); - - *ret_obj = reinterpret_cast(renderer); - return 0; -} - - -int pdraw_be_video_renderer_destroy(struct pdraw_backend *self, - struct pdraw_video_renderer *renderer) -{ - Pdraw::IPdraw::IVideoRenderer *rnd = - reinterpret_cast(renderer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(renderer == nullptr, EINVAL); - - std::vector::iterator l = - self->videoRendererListeners->begin(); - while (l != self->videoRendererListeners->end()) { - if ((*l)->getVideoRenderer() != rnd) { - l++; - continue; - } - delete *l; - self->videoRendererListeners->erase(l); - break; - } - - delete rnd; - - return 0; -} - - -int pdraw_be_video_renderer_resize(struct pdraw_backend *self, - struct pdraw_video_renderer *renderer, - const struct pdraw_rect *render_pos) -{ - Pdraw::IPdraw::IVideoRenderer *rnd = - reinterpret_cast(renderer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(renderer == nullptr, EINVAL); - - return rnd->resize(render_pos); -} - - -int pdraw_be_video_renderer_set_media_id(struct pdraw_backend *self, - struct pdraw_video_renderer *renderer, - unsigned int media_id) -{ - Pdraw::IPdraw::IVideoRenderer *rnd = - reinterpret_cast(renderer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(renderer == nullptr, EINVAL); - - return rnd->setMediaId(media_id); -} - - -unsigned int -pdraw_be_video_renderer_get_media_id(struct pdraw_backend *self, - struct pdraw_video_renderer *renderer) -{ - Pdraw::IPdraw::IVideoRenderer *rnd = - reinterpret_cast(renderer); - - ULOG_ERRNO_RETURN_VAL_IF(self == nullptr, EINVAL, 0); - ULOG_ERRNO_RETURN_VAL_IF(renderer == nullptr, EINVAL, 0); - - return rnd->getMediaId(); -} - - -int pdraw_be_video_renderer_set_params( - struct pdraw_backend *self, - struct pdraw_video_renderer *renderer, - const struct pdraw_video_renderer_params *params) -{ - Pdraw::IPdraw::IVideoRenderer *rnd = - reinterpret_cast(renderer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(renderer == nullptr, EINVAL); - - return rnd->setParams(params); -} - - -int pdraw_be_video_renderer_get_params( - struct pdraw_backend *self, - struct pdraw_video_renderer *renderer, - struct pdraw_video_renderer_params *params) -{ - Pdraw::IPdraw::IVideoRenderer *rnd = - reinterpret_cast(renderer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(renderer == nullptr, EINVAL); - - return rnd->getParams(params); -} - - -int pdraw_be_video_renderer_render(struct pdraw_backend *self, - struct pdraw_video_renderer *renderer, - struct pdraw_rect *content_pos) -{ - Pdraw::IPdraw::IVideoRenderer *rnd = - reinterpret_cast(renderer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(renderer == nullptr, EINVAL); - - return rnd->render(content_pos, nullptr, nullptr); -} - - -int pdraw_be_video_renderer_render_mat(struct pdraw_backend *self, - struct pdraw_video_renderer *renderer, - struct pdraw_rect *content_pos, - const float *view_mat, - const float *proj_mat) -{ - Pdraw::IPdraw::IVideoRenderer *rnd = - reinterpret_cast(renderer); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(renderer == nullptr, EINVAL); - - return rnd->render(content_pos, view_mat, proj_mat); -} - - -int pdraw_be_coded_video_sink_new( - struct pdraw_backend *self, - unsigned int media_id, - const struct pdraw_video_sink_params *params, - const struct pdraw_backend_coded_video_sink_cbs *cbs, - void *userdata, - struct pdraw_coded_video_sink **ret_obj) -{ - int res; - Pdraw::IPdraw::ICodedVideoSink *sink = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(params == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(cbs == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(cbs->flush == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(ret_obj == nullptr, EINVAL); - - PdrawBackendCodedVideoSinkListener *l = - new PdrawBackendCodedVideoSinkListener(self, cbs, userdata); - if (l == nullptr) { - ULOGE("failed to create video sink listener"); - return -ENOMEM; - } - - res = self->pdraw->createCodedVideoSink(media_id, params, l, &sink); - if (res < 0) { - delete l; - return res; - } - - l->setCodedVideoSink(sink); - self->codedVideoSinkListeners->push_back(l); - - *ret_obj = reinterpret_cast(sink); - return 0; -} - - -int pdraw_be_coded_video_sink_destroy(struct pdraw_backend *self, - struct pdraw_coded_video_sink *sink) -{ - std::vector::iterator l; - Pdraw::IPdraw::ICodedVideoSink *s = - reinterpret_cast(sink); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(sink == nullptr, EINVAL); - - l = self->codedVideoSinkListeners->begin(); - while (l != self->codedVideoSinkListeners->end()) { - if ((*l)->getCodedVideoSink() != s) { - l++; - continue; - } - delete *l; - self->codedVideoSinkListeners->erase(l); - break; - } - - delete s; - - return 0; -} - - -int pdraw_be_coded_video_sink_resync(struct pdraw_backend *self, - struct pdraw_coded_video_sink *sink) -{ - Pdraw::IPdraw::ICodedVideoSink *s = - reinterpret_cast(sink); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(sink == nullptr, EINVAL); - - return s->resync(); -} - - -struct mbuf_coded_video_frame_queue * -pdraw_be_coded_video_sink_get_queue(struct pdraw_backend *self, - struct pdraw_coded_video_sink *sink) -{ - Pdraw::IPdraw::ICodedVideoSink *s = - reinterpret_cast(sink); - - ULOG_ERRNO_RETURN_VAL_IF(self == nullptr, EINVAL, nullptr); - ULOG_ERRNO_RETURN_VAL_IF(sink == nullptr, EINVAL, nullptr); - - return s->getQueue(); -} - - -int pdraw_be_coded_video_sink_queue_flushed(struct pdraw_backend *self, - struct pdraw_coded_video_sink *sink) -{ - Pdraw::IPdraw::ICodedVideoSink *s = - reinterpret_cast(sink); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(sink == nullptr, EINVAL); - - return s->queueFlushed(); -} - - -int pdraw_be_raw_video_sink_new( - struct pdraw_backend *self, - unsigned int media_id, - const struct pdraw_video_sink_params *params, - const struct pdraw_backend_raw_video_sink_cbs *cbs, - void *userdata, - struct pdraw_raw_video_sink **ret_obj) -{ - int res; - Pdraw::IPdraw::IRawVideoSink *sink = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(params == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(cbs == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(cbs->flush == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(ret_obj == nullptr, EINVAL); - - PdrawBackendRawVideoSinkListener *l = - new PdrawBackendRawVideoSinkListener(self, cbs, userdata); - if (l == nullptr) { - ULOGE("failed to create video sink listener"); - return -ENOMEM; - } - - res = self->pdraw->createRawVideoSink(media_id, params, l, &sink); - if (res < 0) { - delete l; - return res; - } - - l->setRawVideoSink(sink); - self->rawVideoSinkListeners->push_back(l); - - *ret_obj = reinterpret_cast(sink); - return 0; -} - - -int pdraw_be_raw_video_sink_destroy(struct pdraw_backend *self, - struct pdraw_raw_video_sink *sink) -{ - std::vector::iterator l; - Pdraw::IPdraw::IRawVideoSink *s = - reinterpret_cast(sink); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(sink == nullptr, EINVAL); - - l = self->rawVideoSinkListeners->begin(); - while (l != self->rawVideoSinkListeners->end()) { - if ((*l)->getRawVideoSink() != s) { - l++; - continue; - } - delete *l; - self->rawVideoSinkListeners->erase(l); - break; - } - - delete s; - - return 0; -} - - -struct mbuf_raw_video_frame_queue * -pdraw_be_raw_video_sink_get_queue(struct pdraw_backend *self, - struct pdraw_raw_video_sink *sink) -{ - Pdraw::IPdraw::IRawVideoSink *s = - reinterpret_cast(sink); - - ULOG_ERRNO_RETURN_VAL_IF(self == nullptr, EINVAL, nullptr); - ULOG_ERRNO_RETURN_VAL_IF(sink == nullptr, EINVAL, nullptr); - - return s->getQueue(); -} - - -int pdraw_be_raw_video_sink_queue_flushed(struct pdraw_backend *self, - struct pdraw_raw_video_sink *sink) -{ - Pdraw::IPdraw::IRawVideoSink *s = - reinterpret_cast(sink); - - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(sink == nullptr, EINVAL); - - return s->queueFlushed(); -} - - -int pdraw_be_get_friendly_name_setting(struct pdraw_backend *self, - char *str, - size_t len) -{ - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - - std::string fn; - self->pdraw->getFriendlyNameSetting(&fn); - if ((str) && (fn.length() >= len)) - return -ENOBUFS; - - if (str) - strcpy(str, fn.c_str()); - return 0; -} - - -int pdraw_be_set_friendly_name_setting(struct pdraw_backend *self, - const char *friendly_name) -{ - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(friendly_name == nullptr, EINVAL); - - std::string fn(friendly_name); - self->pdraw->setFriendlyNameSetting(fn); - return 0; -} - - -int pdraw_be_get_serial_number_setting(struct pdraw_backend *self, - char *str, - size_t len) -{ - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - - std::string sn; - self->pdraw->getSerialNumberSetting(&sn); - if ((str) && (sn.length() >= len)) - return -ENOBUFS; - - if (str) - strcpy(str, sn.c_str()); - return 0; -} - - -int pdraw_be_set_serial_number_setting(struct pdraw_backend *self, - const char *serial_number) -{ - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(serial_number == nullptr, EINVAL); - - std::string sn(serial_number); - self->pdraw->setSerialNumberSetting(sn); - return 0; -} - - -int pdraw_be_get_software_version_setting(struct pdraw_backend *self, - char *str, - size_t len) -{ - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - - std::string sv; - self->pdraw->getSoftwareVersionSetting(&sv); - if ((str) && (sv.length() >= len)) - return -ENOBUFS; - - if (str) - strcpy(str, sv.c_str()); - return 0; -} - - -int pdraw_be_set_software_version_setting(struct pdraw_backend *self, - const char *software_version) -{ - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(software_version == nullptr, EINVAL); - - std::string sv(software_version); - self->pdraw->setSoftwareVersionSetting(sv); - return 0; -} - - -enum pdraw_pipeline_mode -pdraw_be_get_pipeline_mode_setting(struct pdraw_backend *self) -{ - ULOG_ERRNO_RETURN_VAL_IF( - self == nullptr, EINVAL, PDRAW_PIPELINE_MODE_DECODE_ALL); - - return self->pdraw->getPipelineModeSetting(); -} - - -int pdraw_be_set_pipeline_mode_setting(struct pdraw_backend *self, - enum pdraw_pipeline_mode mode) -{ - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - - self->pdraw->setPipelineModeSetting(mode); - return 0; -} - - -int pdraw_be_get_display_screen_settings(struct pdraw_backend *self, - float *xdpi, - float *ydpi, - float *device_margin_top, - float *device_margin_bottom, - float *device_margin_left, - float *device_margin_right) -{ - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - - self->pdraw->getDisplayScreenSettings(xdpi, - ydpi, - device_margin_top, - device_margin_bottom, - device_margin_left, - device_margin_right); - return 0; -} - - -int pdraw_be_set_display_screen_settings(struct pdraw_backend *self, - float xdpi, - float ydpi, - float device_margin_top, - float device_margin_bottom, - float device_margin_left, - float device_margin_right) -{ - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - - self->pdraw->setDisplayScreenSettings(xdpi, - ydpi, - device_margin_top, - device_margin_bottom, - device_margin_left, - device_margin_right); - return 0; -} - - -enum pdraw_hmd_model pdraw_be_get_hmd_model_setting(struct pdraw_backend *self) -{ - ULOG_ERRNO_RETURN_VAL_IF( - self == nullptr, EINVAL, PDRAW_HMD_MODEL_UNKNOWN); - - return self->pdraw->getHmdModelSetting(); -} - - -int pdraw_be_set_hmd_model_setting(struct pdraw_backend *self, - enum pdraw_hmd_model hmd_model) -{ - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - - self->pdraw->setHmdModelSetting(hmd_model); - return 0; -} - - -int pdraw_be_set_android_jvm(struct pdraw_backend *self, void *jvm) -{ - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - - self->pdraw->setAndroidJvm(jvm); - return 0; -} - - -int pdraw_be_dump_pipeline(struct pdraw_backend *self, const char *file_name) -{ - ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); - - std::string f(file_name ? file_name : ""); - return self->pdraw->dumpPipeline(f); -} +/** + * Parrot Drones Awesome Video Viewer + * PDrAW back-end library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#define ULOG_TAG pdraw_backend +#include + +#include + +#include "pdraw_backend_impl.hpp" + + +class PdrawBackendListener : public Pdraw::IPdraw::Listener { +public: + PdrawBackendListener(struct pdraw_backend *pdraw, + const struct pdraw_backend_cbs *cbs, + void *userdata) : + mPdraw(pdraw), + mCbs(*cbs), mUserdata(userdata) + { + } + + ~PdrawBackendListener() {} + + void stopResponse(Pdraw::IPdraw *pdraw, int status) + { + if (mCbs.stop_resp) { + (*mCbs.stop_resp)(mPdraw, status, mUserdata); + } + } + + void onMediaAdded(Pdraw::IPdraw *pdraw, + const struct pdraw_media_info *info) + { + if (mCbs.media_added) { + (*mCbs.media_added)(mPdraw, info, mUserdata); + } + } + + void onMediaRemoved(Pdraw::IPdraw *pdraw, + const struct pdraw_media_info *info) + { + if (mCbs.media_removed) { + (*mCbs.media_removed)(mPdraw, info, mUserdata); + } + } + + void onSocketCreated(Pdraw::IPdraw *pdraw, int fd) + { + if (mCbs.socket_created) + (*mCbs.socket_created)(mPdraw, fd, mUserdata); + } + +private: + struct pdraw_backend *mPdraw; + struct pdraw_backend_cbs mCbs; + void *mUserdata; +}; + + +class PdrawBackendDemuxerListener : public Pdraw::IPdraw::IDemuxer::Listener { +public: + PdrawBackendDemuxerListener(struct pdraw_backend *pdraw, + const struct pdraw_backend_demuxer_cbs *cbs, + void *userdata) : + mPdraw(pdraw), + mCbs(*cbs), mUserdata(userdata), mDemuxer(nullptr) + { + } + + ~PdrawBackendDemuxerListener() {} + + void demuxerOpenResponse(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IDemuxer *demuxer, + int status) + { + if (mCbs.open_resp) { + (*mCbs.open_resp)( + mPdraw, + reinterpret_cast( + demuxer), + status, + mUserdata); + } + } + + void demuxerCloseResponse(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IDemuxer *demuxer, + int status) + { + if (mCbs.close_resp) { + (*mCbs.close_resp)( + mPdraw, + reinterpret_cast( + demuxer), + status, + mUserdata); + } + } + + void onDemuxerUnrecoverableError(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IDemuxer *demuxer) + { + if (mCbs.unrecoverable_error) { + (*mCbs.unrecoverable_error)( + mPdraw, + reinterpret_cast( + demuxer), + mUserdata); + } + } + + int demuxerSelectMedia(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IDemuxer *demuxer, + const struct pdraw_demuxer_media *medias, + size_t count) + { + if (mCbs.select_media) { + return (*mCbs.select_media)( + mPdraw, + reinterpret_cast( + demuxer), + medias, + count, + mUserdata); + } + return -ENOSYS; + } + + void demuxerReadyToPlay(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IDemuxer *demuxer, + bool ready) + { + if (mCbs.ready_to_play) { + (*mCbs.ready_to_play)( + mPdraw, + reinterpret_cast( + demuxer), + ready ? 1 : 0, + mUserdata); + } + } + + void onDemuxerEndOfRange(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IDemuxer *demuxer, + uint64_t timestamp) + { + if (mCbs.end_of_range) { + (*mCbs.end_of_range)( + mPdraw, + reinterpret_cast( + demuxer), + timestamp, + mUserdata); + } + } + + void demuxerPlayResponse(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp, + float speed) + { + if (mCbs.play_resp) { + (*mCbs.play_resp)( + mPdraw, + reinterpret_cast( + demuxer), + status, + timestamp, + speed, + mUserdata); + } + } + + void demuxerPauseResponse(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp) + { + if (mCbs.pause_resp) { + (*mCbs.pause_resp)( + mPdraw, + reinterpret_cast( + demuxer), + status, + timestamp, + mUserdata); + } + } + + void demuxerSeekResponse(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp, + float speed) + { + if (mCbs.seek_resp) { + (*mCbs.seek_resp)( + mPdraw, + reinterpret_cast( + demuxer), + status, + timestamp, + speed, + mUserdata); + } + } + + Pdraw::IPdraw::IDemuxer *getDemuxer() + { + return mDemuxer; + } + + void setDemuxer(Pdraw::IPdraw::IDemuxer *demuxer) + { + mDemuxer = demuxer; + } + +private: + struct pdraw_backend *mPdraw; + struct pdraw_backend_demuxer_cbs mCbs; + void *mUserdata; + Pdraw::IPdraw::IDemuxer *mDemuxer; +}; + + +class PdrawBackendVideoRendererListener + : public Pdraw::IPdraw::IVideoRenderer::Listener { +public: + PdrawBackendVideoRendererListener( + struct pdraw_backend *pdraw, + const struct pdraw_backend_video_renderer_cbs *cbs, + void *userdata) : + mPdraw(pdraw), + mCbs(*cbs), mUserdata(userdata), mRenderer(nullptr) + { + } + + ~PdrawBackendVideoRendererListener() {} + + void onVideoRendererMediaAdded(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IVideoRenderer *renderer, + const struct pdraw_media_info *info) + { + if (mCbs.media_added) + (*mCbs.media_added)( + mPdraw, + reinterpret_cast( + renderer), + info, + mUserdata); + } + + void + onVideoRendererMediaRemoved(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IVideoRenderer *renderer, + const struct pdraw_media_info *info) + { + if (mCbs.media_removed) + (*mCbs.media_removed)( + mPdraw, + reinterpret_cast( + renderer), + info, + mUserdata); + } + + void onVideoRenderReady(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IVideoRenderer *renderer) + { + if (mCbs.render_ready) + (*mCbs.render_ready)( + mPdraw, + reinterpret_cast( + renderer), + mUserdata); + } + + int loadVideoTexture(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IVideoRenderer *renderer, + unsigned int textureWidth, + unsigned int textureHeight, + const struct pdraw_media_info *mediaInfo, + struct mbuf_raw_video_frame *frame, + const void *frameUserdata, + size_t frameUserdataLen) + { + if (mCbs.load_texture == nullptr) + return -ENOSYS; + return (*mCbs.load_texture)( + mPdraw, + reinterpret_cast( + renderer), + textureWidth, + textureHeight, + mediaInfo, + frame, + frameUserdata, + frameUserdataLen, + mUserdata); + } + + int renderVideoOverlay(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IVideoRenderer *renderer, + const struct pdraw_rect *renderPos, + const struct pdraw_rect *contentPos, + const float *viewMat, + const float *projMat, + const struct pdraw_media_info *mediaInfo, + struct vmeta_frame *frameMeta, + const struct pdraw_video_frame_extra *frameExtra) + { + if (mCbs.render_overlay == nullptr) + return -ENOSYS; + if ((renderer == nullptr) || (renderPos == nullptr) || + (contentPos == nullptr) || (viewMat == nullptr) || + (projMat == nullptr) || (mediaInfo == nullptr)) + return -EINVAL; + (*mCbs.render_overlay)( + mPdraw, + reinterpret_cast( + renderer), + renderPos, + contentPos, + viewMat, + projMat, + mediaInfo, + frameMeta, + frameExtra, + mUserdata); + return 0; + } + + Pdraw::IPdraw::IVideoRenderer *getVideoRenderer() + { + return mRenderer; + } + + void setVideoRenderer(Pdraw::IPdraw::IVideoRenderer *renderer) + { + mRenderer = renderer; + } + +private: + struct pdraw_backend *mPdraw; + struct pdraw_backend_video_renderer_cbs mCbs; + void *mUserdata; + Pdraw::IPdraw::IVideoRenderer *mRenderer; +}; + + +class PdrawBackendCodedVideoSinkListener + : public Pdraw::IPdraw::ICodedVideoSink::Listener { +public: + PdrawBackendCodedVideoSinkListener( + struct pdraw_backend *pdraw, + const struct pdraw_backend_coded_video_sink_cbs *cbs, + void *userdata) : + mPdraw(pdraw), + mCbs(*cbs), mUserdata(userdata), mSink(nullptr) + { + } + + ~PdrawBackendCodedVideoSinkListener() {} + + void onCodedVideoSinkFlush(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::ICodedVideoSink *sink) + { + if (mCbs.flush) + (*mCbs.flush)( + mPdraw, + reinterpret_cast< + struct pdraw_coded_video_sink *>(sink), + mUserdata); + } + + Pdraw::IPdraw::ICodedVideoSink *getCodedVideoSink() + { + return mSink; + } + + void setCodedVideoSink(Pdraw::IPdraw::ICodedVideoSink *sink) + { + mSink = sink; + } + +private: + struct pdraw_backend *mPdraw; + struct pdraw_backend_coded_video_sink_cbs mCbs; + void *mUserdata; + Pdraw::IPdraw::ICodedVideoSink *mSink; +}; + + +class PdrawBackendRawVideoSinkListener + : public Pdraw::IPdraw::IRawVideoSink::Listener { +public: + PdrawBackendRawVideoSinkListener( + struct pdraw_backend *pdraw, + const struct pdraw_backend_raw_video_sink_cbs *cbs, + void *userdata) : + mPdraw(pdraw), + mCbs(*cbs), mUserdata(userdata), mSink(nullptr) + { + } + + ~PdrawBackendRawVideoSinkListener() {} + + void onRawVideoSinkFlush(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IRawVideoSink *sink) + { + if (mCbs.flush) + (*mCbs.flush)( + mPdraw, + reinterpret_cast( + sink), + mUserdata); + } + + Pdraw::IPdraw::IRawVideoSink *getRawVideoSink() + { + return mSink; + } + + void setRawVideoSink(Pdraw::IPdraw::IRawVideoSink *sink) + { + mSink = sink; + } + +private: + struct pdraw_backend *mPdraw; + struct pdraw_backend_raw_video_sink_cbs mCbs; + void *mUserdata; + Pdraw::IPdraw::IRawVideoSink *mSink; +}; + + +struct pdraw_backend { + PdrawBackend::IPdrawBackend *pdraw; + PdrawBackendListener *listener; + std::vector *demuxerListeners; + std::vector + *videoRendererListeners; + std::vector + *codedVideoSinkListeners; + std::vector *rawVideoSinkListeners; +}; + + +int pdraw_be_new(const struct pdraw_backend_cbs *cbs, + void *userdata, + struct pdraw_backend **ret_obj) +{ + int res = 0; + struct pdraw_backend *self; + + ULOG_ERRNO_RETURN_ERR_IF(cbs == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(ret_obj == nullptr, EINVAL); + + self = (struct pdraw_backend *)calloc(1, sizeof(*self)); + if (self == nullptr) + return -ENOMEM; + + self->demuxerListeners = + new std::vector(); + if (self->demuxerListeners == nullptr) { + res = -ENOMEM; + goto error; + } + + self->videoRendererListeners = + new std::vector(); + if (self->videoRendererListeners == nullptr) { + res = -ENOMEM; + goto error; + } + + self->codedVideoSinkListeners = + new std::vector(); + if (self->codedVideoSinkListeners == nullptr) { + res = -ENOMEM; + goto error; + } + + self->rawVideoSinkListeners = + new std::vector(); + if (self->rawVideoSinkListeners == nullptr) { + res = -ENOMEM; + goto error; + } + + self->listener = new PdrawBackendListener(self, cbs, userdata); + if (self->listener == nullptr) { + res = -ENOMEM; + goto error; + } + + res = createPdrawBackend(self->listener, &self->pdraw); + if (res < 0) + goto error; + + res = self->pdraw->start(); + if (res < 0) + goto error; + + *ret_obj = self; + return 0; + +error: + pdraw_be_destroy(self); + *ret_obj = nullptr; + return res; +} + + +int pdraw_be_destroy(struct pdraw_backend *self) +{ + if (self == nullptr) + return 0; + + if (self->pdraw != nullptr) { + self->pdraw->stop(); + delete self->pdraw; + self->pdraw = nullptr; + } + + if (self->listener != nullptr) + delete self->listener; + + if (self->codedVideoSinkListeners != nullptr) { + std::vector::iterator l = + self->codedVideoSinkListeners->begin(); + while (l != self->codedVideoSinkListeners->end()) { + if (*l != nullptr) + delete (*l); + l++; + } + self->codedVideoSinkListeners->clear(); + delete self->codedVideoSinkListeners; + } + + if (self->rawVideoSinkListeners != nullptr) { + std::vector::iterator l = + self->rawVideoSinkListeners->begin(); + while (l != self->rawVideoSinkListeners->end()) { + if (*l != nullptr) + delete (*l); + l++; + } + self->rawVideoSinkListeners->clear(); + delete self->rawVideoSinkListeners; + } + + if (self->videoRendererListeners != nullptr) { + std::vector::iterator r = + self->videoRendererListeners->begin(); + while (r != self->videoRendererListeners->end()) { + if (*r != nullptr) + delete (*r); + r++; + } + self->videoRendererListeners->clear(); + delete self->videoRendererListeners; + } + + if (self->demuxerListeners != nullptr) { + std::vector::iterator l = + self->demuxerListeners->begin(); + while (l != self->demuxerListeners->end()) { + if (*l != nullptr) + delete (*l); + l++; + } + self->demuxerListeners->clear(); + delete self->demuxerListeners; + } + + free(self); + return 0; +} + + +int pdraw_be_stop(struct pdraw_backend *self) +{ + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + + return self->pdraw->stop(); +} + + +struct pomp_loop *pdraw_be_get_loop(struct pdraw_backend *self) +{ + ULOG_ERRNO_RETURN_VAL_IF(self == nullptr, EINVAL, nullptr); + + return self->pdraw->getLoop(); +} + + +int pdraw_be_demuxer_new_from_url(struct pdraw_backend *self, + const char *url, + const struct pdraw_backend_demuxer_cbs *cbs, + void *userdata, + struct pdraw_demuxer **ret_obj) +{ + int res; + Pdraw::IPdraw::IDemuxer *demuxer = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(cbs == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(ret_obj == nullptr, EINVAL); + + PdrawBackendDemuxerListener *l = + new PdrawBackendDemuxerListener(self, cbs, userdata); + if (l == nullptr) { + ULOGE("failed to create demuxer listener"); + return -ENOMEM; + } + + std::string u(url ? url : ""); + res = self->pdraw->createDemuxer(u, l, &demuxer); + if (res < 0) { + delete l; + return res; + } + + l->setDemuxer(demuxer); + self->demuxerListeners->push_back(l); + + *ret_obj = reinterpret_cast(demuxer); + return 0; +} + + +int pdraw_be_demuxer_new_single_stream( + struct pdraw_backend *self, + const char *local_addr, + uint16_t local_stream_port, + uint16_t local_control_port, + const char *remote_addr, + uint16_t remote_stream_port, + uint16_t remote_control_port, + const struct pdraw_backend_demuxer_cbs *cbs, + void *userdata, + struct pdraw_demuxer **ret_obj) +{ + int res; + Pdraw::IPdraw::IDemuxer *demuxer = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(cbs == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(ret_obj == nullptr, EINVAL); + + PdrawBackendDemuxerListener *l = + new PdrawBackendDemuxerListener(self, cbs, userdata); + if (l == nullptr) { + ULOGE("failed to create demuxer listener"); + return -ENOMEM; + } + + std::string local(local_addr ? local_addr : ""); + std::string remote(remote_addr ? remote_addr : ""); + res = self->pdraw->createDemuxer(local, + local_stream_port, + local_control_port, + remote, + remote_stream_port, + remote_control_port, + l, + &demuxer); + if (res < 0) { + delete l; + return res; + } + + l->setDemuxer(demuxer); + self->demuxerListeners->push_back(l); + + *ret_obj = reinterpret_cast(demuxer); + return 0; +} + + +int pdraw_be_demuxer_new_from_url_on_mux( + struct pdraw_backend *self, + const char *url, + struct mux_ctx *mux, + const struct pdraw_backend_demuxer_cbs *cbs, + void *userdata, + struct pdraw_demuxer **ret_obj) +{ + int res; + Pdraw::IPdraw::IDemuxer *demuxer = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(cbs == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(ret_obj == nullptr, EINVAL); + + PdrawBackendDemuxerListener *l = + new PdrawBackendDemuxerListener(self, cbs, userdata); + if (l == nullptr) { + ULOGE("failed to create demuxer listener"); + return -ENOMEM; + } + + std::string u(url ? url : ""); + res = self->pdraw->createDemuxer(u, mux, l, &demuxer); + if (res < 0) { + delete l; + return res; + } + + l->setDemuxer(demuxer); + self->demuxerListeners->push_back(l); + + *ret_obj = reinterpret_cast(demuxer); + return 0; +} + + +int pdraw_be_demuxer_destroy(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer) +{ + std::vector::iterator l; + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); + + l = self->demuxerListeners->begin(); + while (l != self->demuxerListeners->end()) { + if ((*l)->getDemuxer() != d) { + l++; + continue; + } + delete *l; + self->demuxerListeners->erase(l); + break; + } + + delete d; + + return 0; +} + + +int pdraw_be_demuxer_close(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); + + return d->close(); +} + + +uint16_t pdraw_be_demuxer_get_single_stream_local_stream_port( + struct pdraw_backend *self, + struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + ULOG_ERRNO_RETURN_VAL_IF(self == nullptr, EINVAL, 0); + ULOG_ERRNO_RETURN_VAL_IF(demuxer == nullptr, EINVAL, 0); + + return d->getSingleStreamLocalStreamPort(); +} + + +uint16_t pdraw_be_demuxer_get_single_stream_local_control_port( + struct pdraw_backend *self, + struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + ULOG_ERRNO_RETURN_VAL_IF(self == nullptr, EINVAL, 0); + ULOG_ERRNO_RETURN_VAL_IF(demuxer == nullptr, EINVAL, 0); + + return d->getSingleStreamLocalControlPort(); +} + + +int pdraw_be_demuxer_play(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); + + return d->play(); +} + + +int pdraw_be_demuxer_play_with_speed(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer, + float speed) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); + + return d->play(speed); +} + + +int pdraw_be_demuxer_is_ready_to_play(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); + + return (d->isReadyToPlay()) ? 1 : 0; +} + + +int pdraw_be_demuxer_pause(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); + + return d->pause(); +} + + +int pdraw_be_demuxer_is_paused(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); + + return (d->isPaused()) ? 1 : 0; +} + + +int pdraw_be_demuxer_previous_frame(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); + + return d->previousFrame(); +} + + +int pdraw_be_demuxer_next_frame(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); + + return d->nextFrame(); +} + + +int pdraw_be_demuxer_seek(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer, + int64_t delta, + int exact) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); + + return d->seek(delta, exact ? true : false); +} + + +int pdraw_be_demuxer_seek_forward(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer, + uint64_t delta, + int exact) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); + + return d->seekForward(delta, exact ? true : false); +} + + +int pdraw_be_demuxer_seek_back(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer, + uint64_t delta, + int exact) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); + + return d->seekBack(delta, exact ? true : false); +} + + +int pdraw_be_demuxer_seek_to(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer, + uint64_t timestamp, + int exact) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(demuxer == nullptr, EINVAL); + + return d->seekTo(timestamp, exact ? true : false); +} + + +uint64_t pdraw_be_demuxer_get_duration(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + ULOG_ERRNO_RETURN_VAL_IF(self == nullptr, EINVAL, 0); + ULOG_ERRNO_RETURN_VAL_IF(demuxer == nullptr, EINVAL, 0); + + return d->getDuration(); +} + + +uint64_t pdraw_be_demuxer_get_current_time(struct pdraw_backend *self, + struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + ULOG_ERRNO_RETURN_VAL_IF(self == nullptr, EINVAL, 0); + ULOG_ERRNO_RETURN_VAL_IF(demuxer == nullptr, EINVAL, 0); + + return d->getCurrentTime(); +} + + +int pdraw_be_muxer_new(struct pdraw_backend *self, + const char *url, + struct pdraw_muxer **ret_obj) +{ + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(ret_obj == nullptr, EINVAL); + +#if MUXER_TEST + int res; + Pdraw::IPdraw::IMuxer *muxer = nullptr; + + std::string u(url ? url : ""); + + res = self->pdraw->createMuxer(u, &muxer); + if (res < 0) + return res; + + *ret_obj = reinterpret_cast(muxer); + return 0; +#else + /* Not supported yet */ + return -ENOSYS; +#endif +} + + +int pdraw_be_muxer_destroy(struct pdraw_backend *self, + struct pdraw_muxer *muxer) +{ + Pdraw::IPdraw::IMuxer *m = + reinterpret_cast(muxer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(muxer == nullptr, EINVAL); + + delete m; + + return 0; +} + + +int pdraw_be_muxer_add_media( + struct pdraw_backend *self, + struct pdraw_muxer *muxer, + unsigned int media_id, + const struct pdraw_muxer_video_media_params *params) +{ + Pdraw::IPdraw::IMuxer *m = + reinterpret_cast(muxer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(muxer == nullptr, EINVAL); + + return m->addMedia(media_id, params); +} + + +int pdraw_be_video_renderer_new( + struct pdraw_backend *self, + unsigned int media_id, + const struct pdraw_rect *render_pos, + const struct pdraw_video_renderer_params *params, + const struct pdraw_backend_video_renderer_cbs *cbs, + void *userdata, + struct pdraw_video_renderer **ret_obj) +{ + return pdraw_be_video_renderer_new_egl(self, + media_id, + render_pos, + params, + cbs, + userdata, + nullptr, + ret_obj); +} + + +int pdraw_be_video_renderer_new_egl( + struct pdraw_backend *self, + unsigned int media_id, + const struct pdraw_rect *render_pos, + const struct pdraw_video_renderer_params *params, + const struct pdraw_backend_video_renderer_cbs *cbs, + void *userdata, + struct egl_display *egl_display, + struct pdraw_video_renderer **ret_obj) +{ + int res; + Pdraw::IPdraw::IVideoRenderer *renderer = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(ret_obj == nullptr, EINVAL); + + PdrawBackendVideoRendererListener *l = + new PdrawBackendVideoRendererListener(self, cbs, userdata); + if (l == nullptr) { + ULOGE("failed to create video renderer listener"); + return -ENOMEM; + } + + res = self->pdraw->createVideoRenderer( + media_id, render_pos, params, l, &renderer, egl_display); + if (res < 0) { + delete l; + return res; + } + + self->videoRendererListeners->push_back(l); + l->setVideoRenderer(renderer); + + *ret_obj = reinterpret_cast(renderer); + return 0; +} + + +int pdraw_be_video_renderer_destroy(struct pdraw_backend *self, + struct pdraw_video_renderer *renderer) +{ + Pdraw::IPdraw::IVideoRenderer *rnd = + reinterpret_cast(renderer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(renderer == nullptr, EINVAL); + + std::vector::iterator l = + self->videoRendererListeners->begin(); + while (l != self->videoRendererListeners->end()) { + if ((*l)->getVideoRenderer() != rnd) { + l++; + continue; + } + delete *l; + self->videoRendererListeners->erase(l); + break; + } + + delete rnd; + + return 0; +} + + +int pdraw_be_video_renderer_resize(struct pdraw_backend *self, + struct pdraw_video_renderer *renderer, + const struct pdraw_rect *render_pos) +{ + Pdraw::IPdraw::IVideoRenderer *rnd = + reinterpret_cast(renderer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(renderer == nullptr, EINVAL); + + return rnd->resize(render_pos); +} + + +int pdraw_be_video_renderer_set_media_id(struct pdraw_backend *self, + struct pdraw_video_renderer *renderer, + unsigned int media_id) +{ + Pdraw::IPdraw::IVideoRenderer *rnd = + reinterpret_cast(renderer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(renderer == nullptr, EINVAL); + + return rnd->setMediaId(media_id); +} + + +unsigned int +pdraw_be_video_renderer_get_media_id(struct pdraw_backend *self, + struct pdraw_video_renderer *renderer) +{ + Pdraw::IPdraw::IVideoRenderer *rnd = + reinterpret_cast(renderer); + + ULOG_ERRNO_RETURN_VAL_IF(self == nullptr, EINVAL, 0); + ULOG_ERRNO_RETURN_VAL_IF(renderer == nullptr, EINVAL, 0); + + return rnd->getMediaId(); +} + + +int pdraw_be_video_renderer_set_params( + struct pdraw_backend *self, + struct pdraw_video_renderer *renderer, + const struct pdraw_video_renderer_params *params) +{ + Pdraw::IPdraw::IVideoRenderer *rnd = + reinterpret_cast(renderer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(renderer == nullptr, EINVAL); + + return rnd->setParams(params); +} + + +int pdraw_be_video_renderer_get_params( + struct pdraw_backend *self, + struct pdraw_video_renderer *renderer, + struct pdraw_video_renderer_params *params) +{ + Pdraw::IPdraw::IVideoRenderer *rnd = + reinterpret_cast(renderer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(renderer == nullptr, EINVAL); + + return rnd->getParams(params); +} + + +int pdraw_be_video_renderer_render(struct pdraw_backend *self, + struct pdraw_video_renderer *renderer, + struct pdraw_rect *content_pos) +{ + Pdraw::IPdraw::IVideoRenderer *rnd = + reinterpret_cast(renderer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(renderer == nullptr, EINVAL); + + return rnd->render(content_pos, nullptr, nullptr); +} + + +int pdraw_be_video_renderer_render_mat(struct pdraw_backend *self, + struct pdraw_video_renderer *renderer, + struct pdraw_rect *content_pos, + const float *view_mat, + const float *proj_mat) +{ + Pdraw::IPdraw::IVideoRenderer *rnd = + reinterpret_cast(renderer); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(renderer == nullptr, EINVAL); + + return rnd->render(content_pos, view_mat, proj_mat); +} + + +int pdraw_be_coded_video_sink_new( + struct pdraw_backend *self, + unsigned int media_id, + const struct pdraw_video_sink_params *params, + const struct pdraw_backend_coded_video_sink_cbs *cbs, + void *userdata, + struct pdraw_coded_video_sink **ret_obj) +{ + int res; + Pdraw::IPdraw::ICodedVideoSink *sink = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(params == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(cbs == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(cbs->flush == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(ret_obj == nullptr, EINVAL); + + PdrawBackendCodedVideoSinkListener *l = + new PdrawBackendCodedVideoSinkListener(self, cbs, userdata); + if (l == nullptr) { + ULOGE("failed to create video sink listener"); + return -ENOMEM; + } + + res = self->pdraw->createCodedVideoSink(media_id, params, l, &sink); + if (res < 0) { + delete l; + return res; + } + + l->setCodedVideoSink(sink); + self->codedVideoSinkListeners->push_back(l); + + *ret_obj = reinterpret_cast(sink); + return 0; +} + + +int pdraw_be_coded_video_sink_destroy(struct pdraw_backend *self, + struct pdraw_coded_video_sink *sink) +{ + std::vector::iterator l; + Pdraw::IPdraw::ICodedVideoSink *s = + reinterpret_cast(sink); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(sink == nullptr, EINVAL); + + l = self->codedVideoSinkListeners->begin(); + while (l != self->codedVideoSinkListeners->end()) { + if ((*l)->getCodedVideoSink() != s) { + l++; + continue; + } + delete *l; + self->codedVideoSinkListeners->erase(l); + break; + } + + delete s; + + return 0; +} + + +int pdraw_be_coded_video_sink_resync(struct pdraw_backend *self, + struct pdraw_coded_video_sink *sink) +{ + Pdraw::IPdraw::ICodedVideoSink *s = + reinterpret_cast(sink); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(sink == nullptr, EINVAL); + + return s->resync(); +} + + +struct mbuf_coded_video_frame_queue * +pdraw_be_coded_video_sink_get_queue(struct pdraw_backend *self, + struct pdraw_coded_video_sink *sink) +{ + Pdraw::IPdraw::ICodedVideoSink *s = + reinterpret_cast(sink); + + ULOG_ERRNO_RETURN_VAL_IF(self == nullptr, EINVAL, nullptr); + ULOG_ERRNO_RETURN_VAL_IF(sink == nullptr, EINVAL, nullptr); + + return s->getQueue(); +} + + +int pdraw_be_coded_video_sink_queue_flushed(struct pdraw_backend *self, + struct pdraw_coded_video_sink *sink) +{ + Pdraw::IPdraw::ICodedVideoSink *s = + reinterpret_cast(sink); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(sink == nullptr, EINVAL); + + return s->queueFlushed(); +} + + +int pdraw_be_raw_video_sink_new( + struct pdraw_backend *self, + unsigned int media_id, + const struct pdraw_video_sink_params *params, + const struct pdraw_backend_raw_video_sink_cbs *cbs, + void *userdata, + struct pdraw_raw_video_sink **ret_obj) +{ + int res; + Pdraw::IPdraw::IRawVideoSink *sink = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(params == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(cbs == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(cbs->flush == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(ret_obj == nullptr, EINVAL); + + PdrawBackendRawVideoSinkListener *l = + new PdrawBackendRawVideoSinkListener(self, cbs, userdata); + if (l == nullptr) { + ULOGE("failed to create video sink listener"); + return -ENOMEM; + } + + res = self->pdraw->createRawVideoSink(media_id, params, l, &sink); + if (res < 0) { + delete l; + return res; + } + + l->setRawVideoSink(sink); + self->rawVideoSinkListeners->push_back(l); + + *ret_obj = reinterpret_cast(sink); + return 0; +} + + +int pdraw_be_raw_video_sink_destroy(struct pdraw_backend *self, + struct pdraw_raw_video_sink *sink) +{ + std::vector::iterator l; + Pdraw::IPdraw::IRawVideoSink *s = + reinterpret_cast(sink); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(sink == nullptr, EINVAL); + + l = self->rawVideoSinkListeners->begin(); + while (l != self->rawVideoSinkListeners->end()) { + if ((*l)->getRawVideoSink() != s) { + l++; + continue; + } + delete *l; + self->rawVideoSinkListeners->erase(l); + break; + } + + delete s; + + return 0; +} + + +struct mbuf_raw_video_frame_queue * +pdraw_be_raw_video_sink_get_queue(struct pdraw_backend *self, + struct pdraw_raw_video_sink *sink) +{ + Pdraw::IPdraw::IRawVideoSink *s = + reinterpret_cast(sink); + + ULOG_ERRNO_RETURN_VAL_IF(self == nullptr, EINVAL, nullptr); + ULOG_ERRNO_RETURN_VAL_IF(sink == nullptr, EINVAL, nullptr); + + return s->getQueue(); +} + + +int pdraw_be_raw_video_sink_queue_flushed(struct pdraw_backend *self, + struct pdraw_raw_video_sink *sink) +{ + Pdraw::IPdraw::IRawVideoSink *s = + reinterpret_cast(sink); + + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(sink == nullptr, EINVAL); + + return s->queueFlushed(); +} + + +int pdraw_be_get_friendly_name_setting(struct pdraw_backend *self, + char *str, + size_t len) +{ + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + + std::string fn; + self->pdraw->getFriendlyNameSetting(&fn); + if ((str) && (fn.length() >= len)) + return -ENOBUFS; + + if (str) + strcpy(str, fn.c_str()); + return 0; +} + + +int pdraw_be_set_friendly_name_setting(struct pdraw_backend *self, + const char *friendly_name) +{ + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(friendly_name == nullptr, EINVAL); + + std::string fn(friendly_name); + self->pdraw->setFriendlyNameSetting(fn); + return 0; +} + + +int pdraw_be_get_serial_number_setting(struct pdraw_backend *self, + char *str, + size_t len) +{ + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + + std::string sn; + self->pdraw->getSerialNumberSetting(&sn); + if ((str) && (sn.length() >= len)) + return -ENOBUFS; + + if (str) + strcpy(str, sn.c_str()); + return 0; +} + + +int pdraw_be_set_serial_number_setting(struct pdraw_backend *self, + const char *serial_number) +{ + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(serial_number == nullptr, EINVAL); + + std::string sn(serial_number); + self->pdraw->setSerialNumberSetting(sn); + return 0; +} + + +int pdraw_be_get_software_version_setting(struct pdraw_backend *self, + char *str, + size_t len) +{ + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + + std::string sv; + self->pdraw->getSoftwareVersionSetting(&sv); + if ((str) && (sv.length() >= len)) + return -ENOBUFS; + + if (str) + strcpy(str, sv.c_str()); + return 0; +} + + +int pdraw_be_set_software_version_setting(struct pdraw_backend *self, + const char *software_version) +{ + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(software_version == nullptr, EINVAL); + + std::string sv(software_version); + self->pdraw->setSoftwareVersionSetting(sv); + return 0; +} + + +enum pdraw_pipeline_mode +pdraw_be_get_pipeline_mode_setting(struct pdraw_backend *self) +{ + ULOG_ERRNO_RETURN_VAL_IF( + self == nullptr, EINVAL, PDRAW_PIPELINE_MODE_DECODE_ALL); + + return self->pdraw->getPipelineModeSetting(); +} + + +int pdraw_be_set_pipeline_mode_setting(struct pdraw_backend *self, + enum pdraw_pipeline_mode mode) +{ + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + + self->pdraw->setPipelineModeSetting(mode); + return 0; +} + + +int pdraw_be_get_display_screen_settings(struct pdraw_backend *self, + float *xdpi, + float *ydpi, + float *device_margin_top, + float *device_margin_bottom, + float *device_margin_left, + float *device_margin_right) +{ + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + + self->pdraw->getDisplayScreenSettings(xdpi, + ydpi, + device_margin_top, + device_margin_bottom, + device_margin_left, + device_margin_right); + return 0; +} + + +int pdraw_be_set_display_screen_settings(struct pdraw_backend *self, + float xdpi, + float ydpi, + float device_margin_top, + float device_margin_bottom, + float device_margin_left, + float device_margin_right) +{ + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + + self->pdraw->setDisplayScreenSettings(xdpi, + ydpi, + device_margin_top, + device_margin_bottom, + device_margin_left, + device_margin_right); + return 0; +} + + +enum pdraw_hmd_model pdraw_be_get_hmd_model_setting(struct pdraw_backend *self) +{ + ULOG_ERRNO_RETURN_VAL_IF( + self == nullptr, EINVAL, PDRAW_HMD_MODEL_UNKNOWN); + + return self->pdraw->getHmdModelSetting(); +} + + +int pdraw_be_set_hmd_model_setting(struct pdraw_backend *self, + enum pdraw_hmd_model hmd_model) +{ + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + + self->pdraw->setHmdModelSetting(hmd_model); + return 0; +} + + +int pdraw_be_set_android_jvm(struct pdraw_backend *self, void *jvm) +{ + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + + self->pdraw->setAndroidJvm(jvm); + return 0; +} + + +int pdraw_be_dump_pipeline(struct pdraw_backend *self, const char *file_name) +{ + ULOG_ERRNO_RETURN_ERR_IF(self == nullptr, EINVAL); + + std::string f(file_name ? file_name : ""); + return self->pdraw->dumpPipeline(f); +} diff --git a/libpdraw-backend/tests/pdraw_backend_test.c b/libpdraw-backend/tests/pdraw_backend_test.c index 97fbc62..24ab873 100644 --- a/libpdraw-backend/tests/pdraw_backend_test.c +++ b/libpdraw-backend/tests/pdraw_backend_test.c @@ -1,421 +1,421 @@ -/** - * Parrot Drones Awesome Video Viewer - * PDrAW back-end library test program - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include - -#define ULOG_TAG pdraw_backend_test -#include -ULOG_DECLARE_TAG(pdraw_backend_test); - -#include - - -struct pdraw_backend_app { - pthread_mutex_t mutex; - int mutex_created; - pthread_cond_t cond; - int cond_created; - struct pdraw_backend *pdraw; - struct pdraw_demuxer *demuxer; - int open_resp; - int open_resp_status; - int ready_to_play_changed; - int ready_to_play; - int play_resp; - int play_resp_status; - int close_resp; - int close_resp_status; - int stop_resp; - int stop_resp_status; -}; - - -static void -stop_resp_cb(struct pdraw_backend *pdraw, int status, void *userdata) -{ - struct pdraw_backend_app *self = userdata; - ULOGI("%s status=%d(%s)", __func__, status, strerror(-status)); - pthread_mutex_lock(&self->mutex); - self->stop_resp = 1; - self->stop_resp_status = status; - pthread_mutex_unlock(&self->mutex); - pthread_cond_signal(&self->cond); -} - - -static void media_added_cb(struct pdraw_backend *pdraw, - const struct pdraw_media_info *info, - void *userdata) -{ - ULOGI("%s id=%d", __func__, info->id); -} - - -static void media_removed_cb(struct pdraw_backend *pdraw, - const struct pdraw_media_info *info, - void *userdata) -{ - ULOGI("%s id=%d", __func__, info->id); -} - - -static void -socket_created_cb(struct pdraw_backend *pdraw, int fd, void *userdata) -{ - ULOGI("%s fd=%d", __func__, fd); -} - - -static struct pdraw_backend_cbs be_cbs = { - .stop_resp = &stop_resp_cb, - .media_added = &media_added_cb, - .media_removed = &media_removed_cb, - .socket_created = &socket_created_cb, -}; - - -static void open_resp_cb(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - int status, - void *userdata) -{ - struct pdraw_backend_app *self = userdata; - ULOGI("%s status=%d(%s)", __func__, status, strerror(-status)); - pthread_mutex_lock(&self->mutex); - self->open_resp = 1; - self->open_resp_status = status; - pthread_mutex_unlock(&self->mutex); - pthread_cond_signal(&self->cond); -} - - -static void close_resp_cb(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - int status, - void *userdata) -{ - struct pdraw_backend_app *self = userdata; - ULOGI("%s status=%d(%s)", __func__, status, strerror(-status)); - pthread_mutex_lock(&self->mutex); - self->close_resp = 1; - self->close_resp_status = status; - pthread_mutex_unlock(&self->mutex); - pthread_cond_signal(&self->cond); -} - - -static void unrecoverable_error_cb(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - void *userdata) -{ - ULOGI("%s", __func__); -} - - -static void ready_to_play_cb(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - int ready, - void *userdata) -{ - struct pdraw_backend_app *self = userdata; - ULOGI("%s ready=%d", __func__, ready); - pthread_mutex_lock(&self->mutex); - self->ready_to_play_changed = 1; - self->ready_to_play = ready; - pthread_mutex_unlock(&self->mutex); - pthread_cond_signal(&self->cond); -} - - -static void end_of_range_cb(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - uint64_t timestamp, - void *userdata) -{ - ULOGI("%s timestamp=%" PRIu64, __func__, timestamp); -} - - -static void play_resp_cb(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - int status, - uint64_t timestamp, - float speed, - void *userdata) -{ - struct pdraw_backend_app *self = userdata; - ULOGI("%s status=%d(%s) timestamp=%" PRIu64 " speed=%f", - __func__, - status, - strerror(-status), - timestamp, - speed); - pthread_mutex_lock(&self->mutex); - self->play_resp = 1; - self->play_resp_status = status; - pthread_mutex_unlock(&self->mutex); - pthread_cond_signal(&self->cond); -} - - -static void pause_resp_cb(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - int status, - uint64_t timestamp, - void *userdata) -{ - ULOGI("%s status=%d(%s) timestamp=%" PRIu64, - __func__, - status, - strerror(-status), - timestamp); -} - - -static void seek_resp_cb(struct pdraw_backend *pdraw, - struct pdraw_demuxer *demuxer, - int status, - uint64_t timestamp, - float speed, - void *userdata) -{ - ULOGI("%s status=%d(%s) timestamp=%" PRIu64 " speed=%f", - __func__, - status, - strerror(-status), - timestamp, - speed); -} - - -static struct pdraw_backend_demuxer_cbs demuxer_cbs = { - .open_resp = &open_resp_cb, - .close_resp = &close_resp_cb, - .unrecoverable_error = &unrecoverable_error_cb, - .ready_to_play = &ready_to_play_cb, - .end_of_range = &end_of_range_cb, - .play_resp = &play_resp_cb, - .pause_resp = &pause_resp_cb, - .seek_resp = &seek_resp_cb, -}; - - -static void welcome(char *prog_name) -{ - printf("%s - Parrot Drones Awesome Video Viewer " - "back-end library test program\n\n", - prog_name); -} - - -static void usage(char *prog_name) -{ - printf("Usage: %s \n\n", prog_name); -} - - -int main(int argc, char **argv) -{ - int res, status = EXIT_SUCCESS; - struct pdraw_backend_app *self = NULL; - - welcome(argv[0]); - - if (argc < 2) { - usage(argv[0]); - status = EXIT_FAILURE; - goto out; - } - - self = calloc(1, sizeof(*self)); - if (self == NULL) { - ULOG_ERRNO("calloc", ENOMEM); - status = EXIT_FAILURE; - goto out; - } - - res = pthread_mutex_init(&self->mutex, NULL); - if (res != 0) { - ULOG_ERRNO("pthread_mutex_init", res); - status = EXIT_FAILURE; - goto out; - } - self->mutex_created = 1; - - res = pthread_cond_init(&self->cond, NULL); - if (res != 0) { - ULOG_ERRNO("pthread_cond_init", res); - status = EXIT_FAILURE; - goto out; - } - self->cond_created = 1; - - /* Create PDrAW instance */ - res = pdraw_be_new(&be_cbs, self, &self->pdraw); - if (res < 0) { - ULOG_ERRNO("pdraw_be_new", -res); - status = EXIT_FAILURE; - goto out; - } - ULOGI("created"); - - /* Open URL or file */ - res = pdraw_be_demuxer_new_from_url( - self->pdraw, argv[1], &demuxer_cbs, self, &self->demuxer); - if (res < 0) { - ULOG_ERRNO("pdraw_be_demuxer_new_from_url", -res); - status = EXIT_FAILURE; - goto out; - } - pthread_mutex_lock(&self->mutex); - while (!self->open_resp) - pthread_cond_wait(&self->cond, &self->mutex); - res = self->open_resp_status; - self->open_resp_status = 0; - self->open_resp = 0; - pthread_mutex_unlock(&self->mutex); - if (res < 0) { - ULOG_ERRNO("open_resp", -res); - status = EXIT_FAILURE; - goto out; - } - ULOGI("demuxer opened"); - - /* Wait for 'ready to play' event and then play */ - pthread_mutex_lock(&self->mutex); - while (!self->ready_to_play_changed) - pthread_cond_wait(&self->cond, &self->mutex); - res = self->ready_to_play; - self->ready_to_play_changed = 0; - pthread_mutex_unlock(&self->mutex); - if (res) { - res = pdraw_be_demuxer_play(self->pdraw, self->demuxer); - if (res < 0) { - ULOG_ERRNO("pdraw_be_demuxer_play", -res); - status = EXIT_FAILURE; - goto out; - } - pthread_mutex_lock(&self->mutex); - while (!self->play_resp) - pthread_cond_wait(&self->cond, &self->mutex); - res = self->play_resp_status; - self->play_resp_status = 0; - self->play_resp = 0; - pthread_mutex_unlock(&self->mutex); - if (res < 0) { - ULOG_ERRNO("play_resp", -res); - status = EXIT_FAILURE; - goto out; - } - ULOGI("playing"); - } - - /* Play for 10s */ - sleep(10); - - /* Close the demuxer */ - res = pdraw_be_demuxer_close(self->pdraw, self->demuxer); - if (res < 0) { - ULOG_ERRNO("pdraw_be_demuxer_close", -res); - status = EXIT_FAILURE; - goto out; - } - pthread_mutex_lock(&self->mutex); - while (!self->close_resp) - pthread_cond_wait(&self->cond, &self->mutex); - res = self->close_resp_status; - self->close_resp_status = 0; - self->close_resp = 0; - pthread_mutex_unlock(&self->mutex); - if (res < 0) { - ULOG_ERRNO("close_resp", -res); - status = EXIT_FAILURE; - goto out; - } - ULOGI("demuxer closed"); - res = pdraw_be_demuxer_destroy(self->pdraw, self->demuxer); - if (res < 0) - ULOG_ERRNO("pdraw_be_demuxer_destroy", -res); - self->demuxer = NULL; - - /* Stop PDrAW */ - res = pdraw_be_stop(self->pdraw); - if (res < 0) { - ULOG_ERRNO("pdraw_be_stop", -res); - status = EXIT_FAILURE; - goto out; - } - pthread_mutex_lock(&self->mutex); - while (!self->stop_resp) - pthread_cond_wait(&self->cond, &self->mutex); - res = self->stop_resp_status; - self->stop_resp_status = 0; - self->stop_resp = 0; - pthread_mutex_unlock(&self->mutex); - if (res < 0) { - ULOG_ERRNO("stop_resp", -res); - status = EXIT_FAILURE; - goto out; - } - ULOGI("stopped"); - -out: - if (self != NULL) { - if (self->pdraw != NULL) { - if (self->demuxer != NULL) { - res = pdraw_be_demuxer_destroy(self->pdraw, - self->demuxer); - if (res < 0) { - ULOG_ERRNO("pdraw_be_demuxer_destroy", - -res); - } - } - res = pdraw_be_destroy(self->pdraw); - if (res < 0) - ULOG_ERRNO("pdraw_be_destroy", -res); - } - if (self->cond_created) { - res = pthread_cond_destroy(&self->cond); - if (res != 0) - ULOG_ERRNO("pthread_cond_destroy", res); - } - if (self->mutex_created) { - res = pthread_mutex_destroy(&self->mutex); - if (res != 0) - ULOG_ERRNO("pthread_mutex_destroy", res); - } - free(self); - } - - printf("%s\n", (status == EXIT_SUCCESS) ? "Success!" : "Failed!"); - exit(status); -} +/** + * Parrot Drones Awesome Video Viewer + * PDrAW back-end library test program + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#define ULOG_TAG pdraw_backend_test +#include +ULOG_DECLARE_TAG(pdraw_backend_test); + +#include + + +struct pdraw_backend_app { + pthread_mutex_t mutex; + int mutex_created; + pthread_cond_t cond; + int cond_created; + struct pdraw_backend *pdraw; + struct pdraw_demuxer *demuxer; + int open_resp; + int open_resp_status; + int ready_to_play_changed; + int ready_to_play; + int play_resp; + int play_resp_status; + int close_resp; + int close_resp_status; + int stop_resp; + int stop_resp_status; +}; + + +static void +stop_resp_cb(struct pdraw_backend *pdraw, int status, void *userdata) +{ + struct pdraw_backend_app *self = userdata; + ULOGI("%s status=%d(%s)", __func__, status, strerror(-status)); + pthread_mutex_lock(&self->mutex); + self->stop_resp = 1; + self->stop_resp_status = status; + pthread_mutex_unlock(&self->mutex); + pthread_cond_signal(&self->cond); +} + + +static void media_added_cb(struct pdraw_backend *pdraw, + const struct pdraw_media_info *info, + void *userdata) +{ + ULOGI("%s id=%d", __func__, info->id); +} + + +static void media_removed_cb(struct pdraw_backend *pdraw, + const struct pdraw_media_info *info, + void *userdata) +{ + ULOGI("%s id=%d", __func__, info->id); +} + + +static void +socket_created_cb(struct pdraw_backend *pdraw, int fd, void *userdata) +{ + ULOGI("%s fd=%d", __func__, fd); +} + + +static struct pdraw_backend_cbs be_cbs = { + .stop_resp = &stop_resp_cb, + .media_added = &media_added_cb, + .media_removed = &media_removed_cb, + .socket_created = &socket_created_cb, +}; + + +static void open_resp_cb(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + int status, + void *userdata) +{ + struct pdraw_backend_app *self = userdata; + ULOGI("%s status=%d(%s)", __func__, status, strerror(-status)); + pthread_mutex_lock(&self->mutex); + self->open_resp = 1; + self->open_resp_status = status; + pthread_mutex_unlock(&self->mutex); + pthread_cond_signal(&self->cond); +} + + +static void close_resp_cb(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + int status, + void *userdata) +{ + struct pdraw_backend_app *self = userdata; + ULOGI("%s status=%d(%s)", __func__, status, strerror(-status)); + pthread_mutex_lock(&self->mutex); + self->close_resp = 1; + self->close_resp_status = status; + pthread_mutex_unlock(&self->mutex); + pthread_cond_signal(&self->cond); +} + + +static void unrecoverable_error_cb(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + void *userdata) +{ + ULOGI("%s", __func__); +} + + +static void ready_to_play_cb(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + int ready, + void *userdata) +{ + struct pdraw_backend_app *self = userdata; + ULOGI("%s ready=%d", __func__, ready); + pthread_mutex_lock(&self->mutex); + self->ready_to_play_changed = 1; + self->ready_to_play = ready; + pthread_mutex_unlock(&self->mutex); + pthread_cond_signal(&self->cond); +} + + +static void end_of_range_cb(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + uint64_t timestamp, + void *userdata) +{ + ULOGI("%s timestamp=%" PRIu64, __func__, timestamp); +} + + +static void play_resp_cb(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + int status, + uint64_t timestamp, + float speed, + void *userdata) +{ + struct pdraw_backend_app *self = userdata; + ULOGI("%s status=%d(%s) timestamp=%" PRIu64 " speed=%f", + __func__, + status, + strerror(-status), + timestamp, + speed); + pthread_mutex_lock(&self->mutex); + self->play_resp = 1; + self->play_resp_status = status; + pthread_mutex_unlock(&self->mutex); + pthread_cond_signal(&self->cond); +} + + +static void pause_resp_cb(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + int status, + uint64_t timestamp, + void *userdata) +{ + ULOGI("%s status=%d(%s) timestamp=%" PRIu64, + __func__, + status, + strerror(-status), + timestamp); +} + + +static void seek_resp_cb(struct pdraw_backend *pdraw, + struct pdraw_demuxer *demuxer, + int status, + uint64_t timestamp, + float speed, + void *userdata) +{ + ULOGI("%s status=%d(%s) timestamp=%" PRIu64 " speed=%f", + __func__, + status, + strerror(-status), + timestamp, + speed); +} + + +static struct pdraw_backend_demuxer_cbs demuxer_cbs = { + .open_resp = &open_resp_cb, + .close_resp = &close_resp_cb, + .unrecoverable_error = &unrecoverable_error_cb, + .ready_to_play = &ready_to_play_cb, + .end_of_range = &end_of_range_cb, + .play_resp = &play_resp_cb, + .pause_resp = &pause_resp_cb, + .seek_resp = &seek_resp_cb, +}; + + +static void welcome(char *prog_name) +{ + printf("%s - Parrot Drones Awesome Video Viewer " + "back-end library test program\n\n", + prog_name); +} + + +static void usage(char *prog_name) +{ + printf("Usage: %s \n\n", prog_name); +} + + +int main(int argc, char **argv) +{ + int res, status = EXIT_SUCCESS; + struct pdraw_backend_app *self = NULL; + + welcome(argv[0]); + + if (argc < 2) { + usage(argv[0]); + status = EXIT_FAILURE; + goto out; + } + + self = calloc(1, sizeof(*self)); + if (self == NULL) { + ULOG_ERRNO("calloc", ENOMEM); + status = EXIT_FAILURE; + goto out; + } + + res = pthread_mutex_init(&self->mutex, NULL); + if (res != 0) { + ULOG_ERRNO("pthread_mutex_init", res); + status = EXIT_FAILURE; + goto out; + } + self->mutex_created = 1; + + res = pthread_cond_init(&self->cond, NULL); + if (res != 0) { + ULOG_ERRNO("pthread_cond_init", res); + status = EXIT_FAILURE; + goto out; + } + self->cond_created = 1; + + /* Create PDrAW instance */ + res = pdraw_be_new(&be_cbs, self, &self->pdraw); + if (res < 0) { + ULOG_ERRNO("pdraw_be_new", -res); + status = EXIT_FAILURE; + goto out; + } + ULOGI("created"); + + /* Open URL or file */ + res = pdraw_be_demuxer_new_from_url( + self->pdraw, argv[1], &demuxer_cbs, self, &self->demuxer); + if (res < 0) { + ULOG_ERRNO("pdraw_be_demuxer_new_from_url", -res); + status = EXIT_FAILURE; + goto out; + } + pthread_mutex_lock(&self->mutex); + while (!self->open_resp) + pthread_cond_wait(&self->cond, &self->mutex); + res = self->open_resp_status; + self->open_resp_status = 0; + self->open_resp = 0; + pthread_mutex_unlock(&self->mutex); + if (res < 0) { + ULOG_ERRNO("open_resp", -res); + status = EXIT_FAILURE; + goto out; + } + ULOGI("demuxer opened"); + + /* Wait for 'ready to play' event and then play */ + pthread_mutex_lock(&self->mutex); + while (!self->ready_to_play_changed) + pthread_cond_wait(&self->cond, &self->mutex); + res = self->ready_to_play; + self->ready_to_play_changed = 0; + pthread_mutex_unlock(&self->mutex); + if (res) { + res = pdraw_be_demuxer_play(self->pdraw, self->demuxer); + if (res < 0) { + ULOG_ERRNO("pdraw_be_demuxer_play", -res); + status = EXIT_FAILURE; + goto out; + } + pthread_mutex_lock(&self->mutex); + while (!self->play_resp) + pthread_cond_wait(&self->cond, &self->mutex); + res = self->play_resp_status; + self->play_resp_status = 0; + self->play_resp = 0; + pthread_mutex_unlock(&self->mutex); + if (res < 0) { + ULOG_ERRNO("play_resp", -res); + status = EXIT_FAILURE; + goto out; + } + ULOGI("playing"); + } + + /* Play for 10s */ + sleep(10); + + /* Close the demuxer */ + res = pdraw_be_demuxer_close(self->pdraw, self->demuxer); + if (res < 0) { + ULOG_ERRNO("pdraw_be_demuxer_close", -res); + status = EXIT_FAILURE; + goto out; + } + pthread_mutex_lock(&self->mutex); + while (!self->close_resp) + pthread_cond_wait(&self->cond, &self->mutex); + res = self->close_resp_status; + self->close_resp_status = 0; + self->close_resp = 0; + pthread_mutex_unlock(&self->mutex); + if (res < 0) { + ULOG_ERRNO("close_resp", -res); + status = EXIT_FAILURE; + goto out; + } + ULOGI("demuxer closed"); + res = pdraw_be_demuxer_destroy(self->pdraw, self->demuxer); + if (res < 0) + ULOG_ERRNO("pdraw_be_demuxer_destroy", -res); + self->demuxer = NULL; + + /* Stop PDrAW */ + res = pdraw_be_stop(self->pdraw); + if (res < 0) { + ULOG_ERRNO("pdraw_be_stop", -res); + status = EXIT_FAILURE; + goto out; + } + pthread_mutex_lock(&self->mutex); + while (!self->stop_resp) + pthread_cond_wait(&self->cond, &self->mutex); + res = self->stop_resp_status; + self->stop_resp_status = 0; + self->stop_resp = 0; + pthread_mutex_unlock(&self->mutex); + if (res < 0) { + ULOG_ERRNO("stop_resp", -res); + status = EXIT_FAILURE; + goto out; + } + ULOGI("stopped"); + +out: + if (self != NULL) { + if (self->pdraw != NULL) { + if (self->demuxer != NULL) { + res = pdraw_be_demuxer_destroy(self->pdraw, + self->demuxer); + if (res < 0) { + ULOG_ERRNO("pdraw_be_demuxer_destroy", + -res); + } + } + res = pdraw_be_destroy(self->pdraw); + if (res < 0) + ULOG_ERRNO("pdraw_be_destroy", -res); + } + if (self->cond_created) { + res = pthread_cond_destroy(&self->cond); + if (res != 0) + ULOG_ERRNO("pthread_cond_destroy", res); + } + if (self->mutex_created) { + res = pthread_mutex_destroy(&self->mutex); + if (res != 0) + ULOG_ERRNO("pthread_mutex_destroy", res); + } + free(self); + } + + printf("%s\n", (status == EXIT_SUCCESS) ? "Success!" : "Failed!"); + exit(status); +} diff --git a/libpdraw-gles2hud/atom.mk b/libpdraw-gles2hud/atom.mk index 9c55d36..5f640a3 100644 --- a/libpdraw-gles2hud/atom.mk +++ b/libpdraw-gles2hud/atom.mk @@ -1,55 +1,55 @@ - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE := libpdraw-gles2hud -LOCAL_CATEGORY_PATH := libs -LOCAL_DESCRIPTION := Parrot Drones Awesome Video Viewer OpenGL ES 2.0 HUD rendering library -LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include -LOCAL_CFLAGS := \ - -DPDRAW_GLES2HUD_API_EXPORTS \ - -fvisibility=hidden \ - -std=gnu99 \ - -D_USE_MATH_DEFINES -LOCAL_CXXFLAGS := -std=c++11 -LOCAL_SRC_FILES := \ - src/pdraw_gles2hud.cpp \ - src/pdraw_gles2hud_icons.cpp \ - src/pdraw_gles2hud_icons_data.cpp \ - src/pdraw_gles2hud_instruments.cpp \ - src/pdraw_gles2hud_shaders.cpp \ - src/pdraw_gles2hud_shapes.cpp \ - src/pdraw_gles2hud_text.cpp \ - src/pdraw_gles2hud_text_profont36.cpp -LOCAL_LIBRARIES := \ - eigen \ - libpdraw \ - libulog \ - libvideo-metadata - -ifeq ($(TARGET_CPU),$(filter %$(TARGET_CPU),s905d3 s905x3)) - LOCAL_LDLIBS += -lGLESv2 - LOCAL_LIBRARIES += \ - am-gpu -else ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","linux-native") - LOCAL_CFLAGS += -DUSE_GLFW3 - LOCAL_LIBRARIES += \ - glfw3 \ - opengl -else ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","linux-android") - LOCAL_LDLIBS += -lGLESv2 -else ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","darwin-native") - LOCAL_CFLAGS += -DUSE_GLFW3 - LOCAL_LDLIBS += \ - -framework OpenGL - LOCAL_LIBRARIES += \ - glfw3 -else ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","darwin-iphoneos") - LOCAL_LDLIBS += \ - -framework OpenGLES -else ifeq ("$(TARGET_OS)","windows") - LOCAL_CFLAGS += -D_WIN32_WINNT=0x0600 -DEPOXY_SHARED - LOCAL_LDLIBS += -lepoxy -endif - -include $(BUILD_LIBRARY) + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := libpdraw-gles2hud +LOCAL_CATEGORY_PATH := libs +LOCAL_DESCRIPTION := Parrot Drones Awesome Video Viewer OpenGL ES 2.0 HUD rendering library +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_CFLAGS := \ + -DPDRAW_GLES2HUD_API_EXPORTS \ + -fvisibility=hidden \ + -std=gnu99 \ + -D_USE_MATH_DEFINES +LOCAL_CXXFLAGS := -std=c++11 +LOCAL_SRC_FILES := \ + src/pdraw_gles2hud.cpp \ + src/pdraw_gles2hud_icons.cpp \ + src/pdraw_gles2hud_icons_data.cpp \ + src/pdraw_gles2hud_instruments.cpp \ + src/pdraw_gles2hud_shaders.cpp \ + src/pdraw_gles2hud_shapes.cpp \ + src/pdraw_gles2hud_text.cpp \ + src/pdraw_gles2hud_text_profont36.cpp +LOCAL_LIBRARIES := \ + eigen \ + libpdraw \ + libulog \ + libvideo-metadata + +ifeq ($(TARGET_CPU),$(filter %$(TARGET_CPU),s905d3 s905x3)) + LOCAL_LDLIBS += -lGLESv2 + LOCAL_LIBRARIES += \ + am-gpu +else ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","linux-native") + LOCAL_CFLAGS += -DUSE_GLFW3 + LOCAL_LIBRARIES += \ + glfw3 \ + opengl +else ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","linux-android") + LOCAL_LDLIBS += -lGLESv2 +else ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","darwin-native") + LOCAL_CFLAGS += -DUSE_GLFW3 + LOCAL_LDLIBS += \ + -framework OpenGL + LOCAL_LIBRARIES += \ + glfw3 +else ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","darwin-iphoneos") + LOCAL_LDLIBS += \ + -framework OpenGLES +else ifeq ("$(TARGET_OS)","windows") + LOCAL_CFLAGS += -D_WIN32_WINNT=0x0600 -DEPOXY_SHARED + LOCAL_LDLIBS += -lepoxy +endif + +include $(BUILD_LIBRARY) diff --git a/libpdraw-gles2hud/include/pdraw/pdraw_gles2hud.h b/libpdraw-gles2hud/include/pdraw/pdraw_gles2hud.h index 3477f84..a824859 100644 --- a/libpdraw-gles2hud/include/pdraw/pdraw_gles2hud.h +++ b/libpdraw-gles2hud/include/pdraw/pdraw_gles2hud.h @@ -1,227 +1,227 @@ -/** - * Parrot Drones Awesome Video Viewer - * OpenGL ES 2.0 HUD rendering library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_GLES2HUD_H_ -#define _PDRAW_GLES2HUD_H_ - -#include - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* To be used for all public API */ -#ifdef PDRAW_GLES2HUD_API_EXPORTS -# ifdef _WIN32 -# define PDRAW_GLES2HUD_API __declspec(dllexport) -# else /* !_WIN32 */ -# define PDRAW_GLES2HUD_API \ - __attribute__((visibility("default"))) -# endif /* !_WIN32 */ -#else /* !PDRAW_GLES2HUD_API_EXPORTS */ -# define PDRAW_GLES2HUD_API -#endif /* !PDRAW_GLES2HUD_API_EXPORTS */ - - -/* HUD configuration default values */ -#define PDRAW_GLES2HUD_DEFAULT_SCALE (1.00f) -#define PDRAW_GLES2HUD_DEFAULT_TEXT_SIZE (0.15f) -#define PDRAW_GLES2HUD_DEFAULT_SMALL_ICON_SIZE (0.040f) -#define PDRAW_GLES2HUD_DEFAULT_MEDIUM_ICON_SIZE (0.050f) -#define PDRAW_GLES2HUD_DEFAULT_CENTRAL_ZONE_SIZE (0.25f) -#define PDRAW_GLES2HUD_DEFAULT_HEADING_ZONE_V_OFFSET (-0.80f) -#define PDRAW_GLES2HUD_DEFAULT_ROLL_ZONE_V_OFFSET (0.50f) -#define PDRAW_GLES2HUD_DEFAULT_VU_METER_ZONE_H_OFFSET (-0.60f) -#define PDRAW_GLES2HUD_DEFAULT_VU_METER_V_INTERVAL (-0.30f) -#define PDRAW_GLES2HUD_DEFAULT_RIGHT_ZONE_H_OFFSET (0.65f) -#define PDRAW_GLES2HUD_DEFAULT_RADAR_ZONE_H_OFFSET (0.45f) -#define PDRAW_GLES2HUD_DEFAULT_RADAR_ZONE_V_OFFSET (-0.65f) - - -/* Forward declarations */ -struct pdraw_gles2hud; - - -/* HUD type */ -enum pdraw_gles2hud_type { - /* Piloting HUD with instruments for navigation */ - PDRAW_GLES2HUD_TYPE_PILOTING = 0, - - /* Imaging HUD for shooting videos and taking pictures */ - PDRAW_GLES2HUD_TYPE_IMAGING, -}; - - -/* HUD configuration */ -struct pdraw_gles2hud_config { - /* Global HUD scale */ - float scale; - - /* HUD text size */ - float text_size; - - /* Small icons size */ - float small_icon_size; - - /* Medium icons size */ - float medium_icon_size; - - /* Placement parameters */ - float central_zone_size; - float heading_zone_v_offset; - float roll_zone_v_offset; - float vu_meter_zone_h_offset; - float vu_meter_v_interval; - float right_zone_h_offset; - float radar_zone_h_offset; - float radar_zone_v_offset; -}; - - -/* clang-format off */ -/* Controller metadata */ -struct pdraw_gles2hud_controller_meta { - /* Controller location */ - struct vmeta_location location; - - /* Controller orientation is valid */ - uint32_t has_quat:1; - - /* Controller orientation */ - struct vmeta_quaternion quat; - - /* Controller wifi radar angle (deg) */ - float radar_angle; - - /* Controller battery level (0..100; - * > 100 means charging; 255 means not available) */ - uint8_t battery_percentage; -}; -/* clang-format on */ - - -/* Drone metadata */ -struct pdraw_gles2hud_drone_meta { - /* Time since video recording started - * (microseconds; null means not recording) */ - uint64_t recording_duration; -}; - - -/** - * Create a HUD instance. - * The configuration structure must be filled; null values will be replaced - * by the default values. - * The instance handle is returned through the hud parameter. - * When no longer needed, the instance must be freed using the - * pdraw_gles2hud_destroy() function. - * This function must be called on a thread with a current GLES2 context. - * @param config: HUD configuration - * @param hud: HUD instance handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_GLES2HUD_API int -pdraw_gles2hud_new(const struct pdraw_gles2hud_config *config, - struct pdraw_gles2hud **hud); - - -/** - * Free a HUD instance. - * This function frees all resources associated with a HUD instance. - * This function must be called on a thread with a current GLES2 context. - * @param self: HUD instance handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_GLES2HUD_API int pdraw_gles2hud_destroy(struct pdraw_gles2hud *self); - - -/** - * Get the HUD configuration. - * This function returns the HUD configuration by filling the provided - * configuration structure. - * This function must be called on a thread with a current GLES2 context. - * @param self: HUD instance handle - * @param config: HUD configuration (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_GLES2HUD_API int -pdraw_gles2hud_get_config(struct pdraw_gles2hud *self, - struct pdraw_gles2hud_config *config); - - -/** - * Set the HUD configuration. - * This function sets the HUD configuration; null values will be replaced - * by the default values. - * This function must be called on a thread with a current GLES2 context. - * @param self: HUD instance handle - * @param config: HUD configuration - * @return 0 on success, negative errno value in case of error - */ -PDRAW_GLES2HUD_API int -pdraw_gles2hud_set_config(struct pdraw_gles2hud *self, - const struct pdraw_gles2hud_config *config); - - -/** - * Render the HUD of the given type. - * This function must be called on a thread with a current GLES2 context. - * @param self: HUD instance handle - * @param type: HUD type to render - * @param content_pos: content position (video rectangle) - * @param view_proj_mat: view * projection matrix - * @param media_info: media information - * @param frame_meta: frame metadata - * @param frame_extra: frame extra metadata - * @param ctrl_meta: controller metadata - * @param drone_meta: drone metadata - * @return 0 on success, negative errno value in case of error - */ -PDRAW_GLES2HUD_API int -pdraw_gles2hud_render(struct pdraw_gles2hud *self, - enum pdraw_gles2hud_type type, - const struct pdraw_rect *render_pos, - const struct pdraw_rect *content_pos, - const float view_proj_mat[16], - const struct pdraw_media_info *media_info, - struct vmeta_frame *frame_meta, - const struct pdraw_video_frame_extra *frame_extra, - const struct pdraw_gles2hud_controller_meta *ctrl_meta, - const struct pdraw_gles2hud_drone_meta *drone_meta); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* !_PDRAW_GLES2HUD_H_ */ +/** + * Parrot Drones Awesome Video Viewer + * OpenGL ES 2.0 HUD rendering library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_GLES2HUD_H_ +#define _PDRAW_GLES2HUD_H_ + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* To be used for all public API */ +#ifdef PDRAW_GLES2HUD_API_EXPORTS +# ifdef _WIN32 +# define PDRAW_GLES2HUD_API __declspec(dllexport) +# else /* !_WIN32 */ +# define PDRAW_GLES2HUD_API \ + __attribute__((visibility("default"))) +# endif /* !_WIN32 */ +#else /* !PDRAW_GLES2HUD_API_EXPORTS */ +# define PDRAW_GLES2HUD_API +#endif /* !PDRAW_GLES2HUD_API_EXPORTS */ + + +/* HUD configuration default values */ +#define PDRAW_GLES2HUD_DEFAULT_SCALE (1.00f) +#define PDRAW_GLES2HUD_DEFAULT_TEXT_SIZE (0.15f) +#define PDRAW_GLES2HUD_DEFAULT_SMALL_ICON_SIZE (0.040f) +#define PDRAW_GLES2HUD_DEFAULT_MEDIUM_ICON_SIZE (0.050f) +#define PDRAW_GLES2HUD_DEFAULT_CENTRAL_ZONE_SIZE (0.25f) +#define PDRAW_GLES2HUD_DEFAULT_HEADING_ZONE_V_OFFSET (-0.80f) +#define PDRAW_GLES2HUD_DEFAULT_ROLL_ZONE_V_OFFSET (0.50f) +#define PDRAW_GLES2HUD_DEFAULT_VU_METER_ZONE_H_OFFSET (-0.60f) +#define PDRAW_GLES2HUD_DEFAULT_VU_METER_V_INTERVAL (-0.30f) +#define PDRAW_GLES2HUD_DEFAULT_RIGHT_ZONE_H_OFFSET (0.65f) +#define PDRAW_GLES2HUD_DEFAULT_RADAR_ZONE_H_OFFSET (0.45f) +#define PDRAW_GLES2HUD_DEFAULT_RADAR_ZONE_V_OFFSET (-0.65f) + + +/* Forward declarations */ +struct pdraw_gles2hud; + + +/* HUD type */ +enum pdraw_gles2hud_type { + /* Piloting HUD with instruments for navigation */ + PDRAW_GLES2HUD_TYPE_PILOTING = 0, + + /* Imaging HUD for shooting videos and taking pictures */ + PDRAW_GLES2HUD_TYPE_IMAGING, +}; + + +/* HUD configuration */ +struct pdraw_gles2hud_config { + /* Global HUD scale */ + float scale; + + /* HUD text size */ + float text_size; + + /* Small icons size */ + float small_icon_size; + + /* Medium icons size */ + float medium_icon_size; + + /* Placement parameters */ + float central_zone_size; + float heading_zone_v_offset; + float roll_zone_v_offset; + float vu_meter_zone_h_offset; + float vu_meter_v_interval; + float right_zone_h_offset; + float radar_zone_h_offset; + float radar_zone_v_offset; +}; + + +/* clang-format off */ +/* Controller metadata */ +struct pdraw_gles2hud_controller_meta { + /* Controller location */ + struct vmeta_location location; + + /* Controller orientation is valid */ + uint32_t has_quat:1; + + /* Controller orientation */ + struct vmeta_quaternion quat; + + /* Controller wifi radar angle (deg) */ + float radar_angle; + + /* Controller battery level (0..100; + * > 100 means charging; 255 means not available) */ + uint8_t battery_percentage; +}; +/* clang-format on */ + + +/* Drone metadata */ +struct pdraw_gles2hud_drone_meta { + /* Time since video recording started + * (microseconds; null means not recording) */ + uint64_t recording_duration; +}; + + +/** + * Create a HUD instance. + * The configuration structure must be filled; null values will be replaced + * by the default values. + * The instance handle is returned through the hud parameter. + * When no longer needed, the instance must be freed using the + * pdraw_gles2hud_destroy() function. + * This function must be called on a thread with a current GLES2 context. + * @param config: HUD configuration + * @param hud: HUD instance handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_GLES2HUD_API int +pdraw_gles2hud_new(const struct pdraw_gles2hud_config *config, + struct pdraw_gles2hud **hud); + + +/** + * Free a HUD instance. + * This function frees all resources associated with a HUD instance. + * This function must be called on a thread with a current GLES2 context. + * @param self: HUD instance handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_GLES2HUD_API int pdraw_gles2hud_destroy(struct pdraw_gles2hud *self); + + +/** + * Get the HUD configuration. + * This function returns the HUD configuration by filling the provided + * configuration structure. + * This function must be called on a thread with a current GLES2 context. + * @param self: HUD instance handle + * @param config: HUD configuration (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_GLES2HUD_API int +pdraw_gles2hud_get_config(struct pdraw_gles2hud *self, + struct pdraw_gles2hud_config *config); + + +/** + * Set the HUD configuration. + * This function sets the HUD configuration; null values will be replaced + * by the default values. + * This function must be called on a thread with a current GLES2 context. + * @param self: HUD instance handle + * @param config: HUD configuration + * @return 0 on success, negative errno value in case of error + */ +PDRAW_GLES2HUD_API int +pdraw_gles2hud_set_config(struct pdraw_gles2hud *self, + const struct pdraw_gles2hud_config *config); + + +/** + * Render the HUD of the given type. + * This function must be called on a thread with a current GLES2 context. + * @param self: HUD instance handle + * @param type: HUD type to render + * @param content_pos: content position (video rectangle) + * @param view_proj_mat: view * projection matrix + * @param media_info: media information + * @param frame_meta: frame metadata + * @param frame_extra: frame extra metadata + * @param ctrl_meta: controller metadata + * @param drone_meta: drone metadata + * @return 0 on success, negative errno value in case of error + */ +PDRAW_GLES2HUD_API int +pdraw_gles2hud_render(struct pdraw_gles2hud *self, + enum pdraw_gles2hud_type type, + const struct pdraw_rect *render_pos, + const struct pdraw_rect *content_pos, + const float view_proj_mat[16], + const struct pdraw_media_info *media_info, + struct vmeta_frame *frame_meta, + const struct pdraw_video_frame_extra *frame_extra, + const struct pdraw_gles2hud_controller_meta *ctrl_meta, + const struct pdraw_gles2hud_drone_meta *drone_meta); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* !_PDRAW_GLES2HUD_H_ */ diff --git a/libpdraw-gles2hud/src/pdraw_gles2hud.cpp b/libpdraw-gles2hud/src/pdraw_gles2hud.cpp index 1b9a447..4734ba9 100644 --- a/libpdraw-gles2hud/src/pdraw_gles2hud.cpp +++ b/libpdraw-gles2hud/src/pdraw_gles2hud.cpp @@ -1,1432 +1,1432 @@ -/** - * Parrot Drones Awesome Video Viewer - * OpenGL ES 2.0 HUD rendering library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "pdraw_gles2hud_priv.h" -ULOG_DECLARE_TAG(pdraw_gles2hud); - -/* used to test the radar on records */ -#if 0 -# define DEBUG_RADAR -#endif - - -enum drone_model { - /* Unknown drone model */ - DRONE_MODEL_UNKNOWN = 0, - - /* Parrot Bebop */ - DRONE_MODEL_BEBOP, - - /* Parrot Bebop 2 */ - DRONE_MODEL_BEBOP2, - - /* Parrot Disco */ - DRONE_MODEL_DISCO, - - /* Parrot Bluegrass */ - DRONE_MODEL_BLUEGRASS, - - /* Parrot Anafi */ - DRONE_MODEL_ANAFI, - - /* Parrot Anafi Thermal */ - DRONE_MODEL_ANAFI_THERMAL, -}; - - -static const int drone_model_icon_index[] = { - 2, - 0, - 2, - 1, - 2, - 2, - 2, -}; - -static const char *heading_str[] = { - ".N.", - "NW", - "W", - "SW", - "S", - "SE", - "E", - "NE", -}; - -static const char *flying_state_str[] = { - "LANDED", - "TAKING OFF", - "HOVERING", - "FLYING", - "LANDING", - "EMERGENCY", -}; - -static const char *piloting_mode_str[] = { - "MANUAL", - "RETURN HOME", - "FLIGHT PLAN", - "FOLLOW ME", -}; - -static const float color_green[4] = {0.0f, 0.9f, 0.0f, 1.0f}; - -static const float color_dark_green[4] = {0.0f, 0.5f, 0.0f, 1.0f}; - -static const float color_black_alpha[4] = {0.0f, 0.0f, 0.0f, 0.66f}; - - -static enum drone_model -get_drone_model(const struct vmeta_session *session_meta) -{ - if (strcmp(session_meta->model_id, "0901") == 0) - return DRONE_MODEL_BEBOP; - else if (strcmp(session_meta->model_id, "090c") == 0) - return DRONE_MODEL_BEBOP2; - else if (strcmp(session_meta->model_id, "090e") == 0) - return DRONE_MODEL_DISCO; - else if (strcmp(session_meta->model_id, "0916") == 0) - return DRONE_MODEL_BLUEGRASS; - else if (strcmp(session_meta->model_id, "0914") == 0) - return DRONE_MODEL_ANAFI; - else if (strcmp(session_meta->model_id, "0919") == 0) - return DRONE_MODEL_ANAFI_THERMAL; - else if (strcmp(session_meta->model, "Bebop") == 0) - return DRONE_MODEL_BEBOP; - else if (strcmp(session_meta->model, "Bebop 2") == 0) - return DRONE_MODEL_BEBOP2; - else if (strcmp(session_meta->model, "Disco") == 0) - return DRONE_MODEL_DISCO; - else if (strcmp(session_meta->model, "Bluegrass") == 0) - return DRONE_MODEL_BLUEGRASS; - else if (strcmp(session_meta->model, "ANAFI") == 0) - return DRONE_MODEL_ANAFI; - else if (strcmp(session_meta->model, "Anafi") == 0) - return DRONE_MODEL_ANAFI; - else if (strcmp(session_meta->model, "AnafiThermal") == 0) - return DRONE_MODEL_ANAFI_THERMAL; - else if (strcmp(session_meta->friendly_name, "Parrot Bebop") == 0) - return DRONE_MODEL_BEBOP; - else if (strcmp(session_meta->friendly_name, "Parrot Bebop 2") == 0) - return DRONE_MODEL_BEBOP2; - else if (strcmp(session_meta->friendly_name, "Parrot Disco") == 0) - return DRONE_MODEL_DISCO; - else - return DRONE_MODEL_UNKNOWN; -} - - -int pdraw_gles2hud_new(const struct pdraw_gles2hud_config *config, - struct pdraw_gles2hud **hud) -{ - int res, err; - struct pdraw_gles2hud *self; - - if (config == nullptr) - return -EINVAL; - if (hud == nullptr) - return -EINVAL; - - self = (struct pdraw_gles2hud *)calloc(1, sizeof(*self)); - if (self == nullptr) - return -ENOMEM; - - self->config = *config; - if (self->config.central_zone_size <= 0.) { - self->config.central_zone_size = - PDRAW_GLES2HUD_DEFAULT_CENTRAL_ZONE_SIZE; - } - if (self->config.heading_zone_v_offset <= 0.) { - self->config.heading_zone_v_offset = - PDRAW_GLES2HUD_DEFAULT_HEADING_ZONE_V_OFFSET; - } - if (self->config.roll_zone_v_offset <= 0.) { - self->config.roll_zone_v_offset = - PDRAW_GLES2HUD_DEFAULT_ROLL_ZONE_V_OFFSET; - } - if (self->config.vu_meter_zone_h_offset <= 0.) { - self->config.vu_meter_zone_h_offset = - PDRAW_GLES2HUD_DEFAULT_VU_METER_ZONE_H_OFFSET; - } - if (self->config.vu_meter_v_interval <= 0.) { - self->config.vu_meter_v_interval = - PDRAW_GLES2HUD_DEFAULT_VU_METER_V_INTERVAL; - } - if (self->config.right_zone_h_offset <= 0.) { - self->config.right_zone_h_offset = - PDRAW_GLES2HUD_DEFAULT_RIGHT_ZONE_H_OFFSET; - } - if (self->config.radar_zone_h_offset <= 0.) { - self->config.radar_zone_h_offset = - PDRAW_GLES2HUD_DEFAULT_RADAR_ZONE_H_OFFSET; - } - if (self->config.radar_zone_v_offset <= 0.) { - self->config.radar_zone_v_offset = - PDRAW_GLES2HUD_DEFAULT_RADAR_ZONE_V_OFFSET; - } - if (self->config.text_size <= 0.) { - self->config.text_size = PDRAW_GLES2HUD_DEFAULT_TEXT_SIZE; - } - if (self->config.small_icon_size <= 0.) { - self->config.small_icon_size = - PDRAW_GLES2HUD_DEFAULT_SMALL_ICON_SIZE; - } - if (self->config.medium_icon_size <= 0.) { - self->config.medium_icon_size = - PDRAW_GLES2HUD_DEFAULT_MEDIUM_ICON_SIZE; - } - if (self->config.scale <= 0.) { - self->config.scale = PDRAW_GLES2HUD_DEFAULT_SCALE; - } - - self->ratio_w = 1.; - self->ratio_h = 1.; - self->aspect_ratio = 1.; - self->h_fov = 0.; - self->v_fov = 0.; - - res = pdraw_gles2hud_create_programs(self); - if (res < 0) { - ULOG_ERRNO("pdraw_gles2hud_create_programs", -res); - goto error; - } - - *hud = self; - return 0; - -error: - err = pdraw_gles2hud_destroy(self); - if (err < 0) - ULOG_ERRNO("pdraw_gles2hud_destroy", -err); - return res; -} - - -int pdraw_gles2hud_destroy(struct pdraw_gles2hud *self) -{ - if (self == nullptr) - return 0; - - if (self->program > 0) { - glDeleteProgram(self->program); - self->program = 0; - } - if (self->tex_program > 0) { - glDeleteProgram(self->tex_program); - self->tex_program = 0; - } - if (self->icons_texture > 0) { - glDeleteTextures(1, &self->icons_texture); - self->icons_texture = 0; - } - if (self->text_texture > 0) { - glDeleteTextures(1, &self->text_texture); - self->text_texture = 0; - } - - free(self); - - return 0; -} - - -int pdraw_gles2hud_get_config(struct pdraw_gles2hud *self, - struct pdraw_gles2hud_config *config) -{ - if (self == nullptr) - return -EINVAL; - if (config == nullptr) - return -EINVAL; - - *config = self->config; - - return 0; -} - - -int pdraw_gles2hud_set_config(struct pdraw_gles2hud *self, - const struct pdraw_gles2hud_config *config) -{ - if (self == nullptr) - return -EINVAL; - if (config == nullptr) - return -EINVAL; - - self->config = *config; - if (self->config.central_zone_size <= 0.) { - self->config.central_zone_size = - PDRAW_GLES2HUD_DEFAULT_CENTRAL_ZONE_SIZE; - } - if (self->config.heading_zone_v_offset <= 0.) { - self->config.heading_zone_v_offset = - PDRAW_GLES2HUD_DEFAULT_HEADING_ZONE_V_OFFSET; - } - if (self->config.roll_zone_v_offset <= 0.) { - self->config.roll_zone_v_offset = - PDRAW_GLES2HUD_DEFAULT_ROLL_ZONE_V_OFFSET; - } - if (self->config.vu_meter_zone_h_offset <= 0.) { - self->config.vu_meter_zone_h_offset = - PDRAW_GLES2HUD_DEFAULT_VU_METER_ZONE_H_OFFSET; - } - if (self->config.vu_meter_v_interval <= 0.) { - self->config.vu_meter_v_interval = - PDRAW_GLES2HUD_DEFAULT_VU_METER_V_INTERVAL; - } - if (self->config.right_zone_h_offset <= 0.) { - self->config.right_zone_h_offset = - PDRAW_GLES2HUD_DEFAULT_RIGHT_ZONE_H_OFFSET; - } - if (self->config.radar_zone_h_offset <= 0.) { - self->config.radar_zone_h_offset = - PDRAW_GLES2HUD_DEFAULT_RADAR_ZONE_H_OFFSET; - } - if (self->config.radar_zone_v_offset <= 0.) { - self->config.radar_zone_v_offset = - PDRAW_GLES2HUD_DEFAULT_RADAR_ZONE_V_OFFSET; - } - if (self->config.text_size <= 0.) { - self->config.text_size = PDRAW_GLES2HUD_DEFAULT_TEXT_SIZE; - } - if (self->config.small_icon_size <= 0.) { - self->config.small_icon_size = - PDRAW_GLES2HUD_DEFAULT_SMALL_ICON_SIZE; - } - if (self->config.medium_icon_size <= 0.) { - self->config.medium_icon_size = - PDRAW_GLES2HUD_DEFAULT_MEDIUM_ICON_SIZE; - } - if (self->config.scale <= 0.) { - self->config.scale = PDRAW_GLES2HUD_DEFAULT_SCALE; - } - - return 0; -} - - -static void pdraw_gles2hud_get_fov(struct pdraw_gles2hud *self, - const struct vmeta_session *session_meta, - struct vmeta_frame *frame_meta) -{ - if ((frame_meta) && (frame_meta->type == VMETA_FRAME_TYPE_V3)) { - self->h_fov = frame_meta->v3.base.picture_hfov; - self->v_fov = frame_meta->v3.base.picture_vfov; - } else if ((session_meta) && (session_meta->picture_fov.has_horz) && - (session_meta->picture_fov.has_horz)) { - self->h_fov = session_meta->picture_fov.horz; - self->v_fov = session_meta->picture_fov.vert; - } else { - self->h_fov = PDRAW_GLES2HUD_DEFAULT_HFOV; - self->v_fov = PDRAW_GLES2HUD_DEFAULT_VFOV; - } - self->h_fov *= M_PI / 180.; - self->v_fov *= M_PI / 180.; -} - - -static int pdraw_gles2hud_render_piloting( - struct pdraw_gles2hud *self, - const struct pdraw_rect *render_pos, - const struct pdraw_rect *content_pos, - const float view_proj_mat[16], - const struct pdraw_media_info *media_info, - struct vmeta_frame *frame_meta, - const struct pdraw_video_frame_extra *frame_extra, - const struct pdraw_gles2hud_controller_meta *ctrl_meta, - const struct pdraw_gles2hud_drone_meta *drone_meta) -{ - int i, j, steps, angle_deg; - float cy, delta_x, delta_y, angle; - Eigen::Matrix4f xform_mat; - Eigen::Matrix4f model_mat; - Eigen::Matrix4f view_proj; - for (i = 0; i < 4; i++) { - for (j = 0; j < 4; j++) - view_proj(i, j) = view_proj_mat[i * 4 + j]; - } - - if (self == nullptr) - return -EINVAL; - if ((render_pos == nullptr) || (render_pos->width == 0) || - (render_pos->height == 0)) - return -EINVAL; - if ((content_pos == nullptr) || (content_pos->width == 0) || - (content_pos->height == 0)) - return -EINVAL; - if (media_info == nullptr) - return -EINVAL; - if (frame_meta == nullptr) - return -EINVAL; - if (frame_extra == nullptr) - return -EINVAL; - if (ctrl_meta == nullptr) - return -EINVAL; - if (drone_meta == nullptr) - return -EINVAL; - - uint8_t battery_percentage; - vmeta_frame_get_battery_percentage(frame_meta, &battery_percentage); - int8_t wifi_rssi; - vmeta_frame_get_wifi_rssi(frame_meta, &wifi_rssi); - enum vmeta_flying_state flying_state; - vmeta_frame_get_flying_state(frame_meta, &flying_state); - enum vmeta_piloting_mode piloting_mode; - vmeta_frame_get_piloting_mode(frame_meta, &piloting_mode); - enum drone_model drone_model = - get_drone_model(media_info->session_meta); - int drone_model_icon = drone_model_icon_index[drone_model]; - - /* Drone location and ground distance */ - struct vmeta_location location; - vmeta_frame_get_location(frame_meta, &location); - double ground_distance; - vmeta_frame_get_ground_distance(frame_meta, &ground_distance); - if ((drone_model == DRONE_MODEL_DISCO) && (location.valid) && - (media_info->session_meta->takeoff_loc.valid)) { - ground_distance = location.altitude_egm96amsl - - media_info->session_meta->takeoff_loc - .altitude_egm96amsl; - } - - /* Drone speeds */ - struct vmeta_ned speed; - vmeta_frame_get_speed_ned(frame_meta, &speed); - float air_speed; - vmeta_frame_get_air_speed(frame_meta, &air_speed); - float horizontal_speed = - sqrtf(speed.north * speed.north + speed.east * speed.east); - float speed_psi = atan2f(speed.east, speed.north); - /* UNUSED - * float speed_rho = sqrtf(speed.north * speed.north + - * speed.east * speed.east + - * speed.down * speed.down); - * float speed_theta = M_PI / 2 - acosf(speed.down / speed_rho); */ - - /* Drone attitude and frame orientation */ - struct vmeta_euler drone_attitude; - vmeta_frame_get_drone_euler(frame_meta, &drone_attitude); - struct vmeta_euler frame_orientation; - vmeta_frame_get_frame_euler(frame_meta, &frame_orientation); - int heading_int = ((int)(drone_attitude.psi * RAD_TO_DEG) + 360) % 360; - - /* Picture field of view */ - pdraw_gles2hud_get_fov(self, media_info->session_meta, frame_meta); - - /* Distace to take-off */ - double takeoff_distance = 0.; - double takeoff_bearing = 0.; - double takeoff_elevation = 0.; - if ((location.valid) && (media_info->session_meta->takeoff_loc.valid)) { - pdraw_gles2hud_coords_distance_and_bearing( - location.latitude, - location.longitude, - media_info->session_meta->takeoff_loc.latitude, - media_info->session_meta->takeoff_loc.longitude, - &takeoff_distance, - &takeoff_bearing); - double alt_diff = 0.; - if (!std::isnan(media_info->session_meta->takeoff_loc - .altitude_wgs84ellipsoid) && - !std::isnan(location.altitude_wgs84ellipsoid != NAN)) { - alt_diff = media_info->session_meta->takeoff_loc - .altitude_wgs84ellipsoid - - location.altitude_wgs84ellipsoid; - } else if (!std::isnan(media_info->session_meta->takeoff_loc - .altitude_egm96amsl) && - !std::isnan(location.altitude_egm96amsl)) { - alt_diff = media_info->session_meta->takeoff_loc - .altitude_egm96amsl - - location.altitude_egm96amsl; - } - takeoff_elevation = atan2(alt_diff, takeoff_distance); - } - - /* Distace to pilot */ - double self_distance = 0.; - double self_bearing = 0.; - double self_elevation = 0.; - if ((location.valid) && (ctrl_meta->location.valid)) { - pdraw_gles2hud_coords_distance_and_bearing( - location.latitude, - location.longitude, - ctrl_meta->location.latitude, - ctrl_meta->location.longitude, - &self_distance, - &self_bearing); - double alt_diff = 0.; - if (!std::isnan(ctrl_meta->location.altitude_wgs84ellipsoid) && - !std::isnan(location.altitude_wgs84ellipsoid)) { - alt_diff = ctrl_meta->location.altitude_wgs84ellipsoid - - location.altitude_wgs84ellipsoid; - } else if (!std::isnan( - ctrl_meta->location.altitude_egm96amsl) && - !std::isnan(location.altitude_egm96amsl)) { - alt_diff = ctrl_meta->location.altitude_egm96amsl - - location.altitude_egm96amsl; - } - self_elevation = atan2(alt_diff, self_distance); - } - - /* Controller orientation */ - struct vmeta_euler ctrl_orientation; - memset(&ctrl_orientation, 0, sizeof(ctrl_orientation)); -#ifdef DEBUG_RADAR /* used to test the radar on records */ - int ctrl_orientation_valid = 1; -#else /* DEBUG_RADAR */ - int ctrl_orientation_valid = ctrl_meta->has_quat; - if (ctrl_orientation_valid) { - vmeta_quat_to_euler(&ctrl_meta->quat, &ctrl_orientation); - } -#endif /* DEBUG_RADAR */ - - self->ratio_w = (float)content_pos->width / render_pos->width * - self->config.scale; - self->ratio_h = (float)content_pos->height / render_pos->height * - self->config.scale; - self->aspect_ratio = (float)render_pos->width / render_pos->height; - - GLCHK(glUseProgram(self->program)); - - GLCHK(glEnableVertexAttribArray(self->position_handle)); - GLCHK(glEnableVertexAttribArray(self->color_handle)); - - GLCHK(glUniformMatrix4fv( - self->transform_matrix_handle, 1, false, view_proj_mat)); - -#if 0 - if (horizontal_speed >= 0.2) - pdraw_gles2hud_draw_flight_path_vector(self, &frame_orientation, - speed_theta, speed_psi, - color_green); -#endif - - /* Draw instruments */ - pdraw_gles2hud_draw_artificial_horizon( - self, &drone_attitude, &frame_orientation, color_green); - pdraw_gles2hud_draw_roll(self, drone_attitude.phi, color_green); - pdraw_gles2hud_draw_heading(self, - drone_attitude.psi, - horizontal_speed, - speed_psi, - color_green); - double altitude = 0.; - if (!std::isnan(location.altitude_wgs84ellipsoid)) - altitude = location.altitude_wgs84ellipsoid; - else if (!std::isnan(location.altitude_egm96amsl)) - altitude = location.altitude_egm96amsl; - pdraw_gles2hud_draw_altitude( - self, altitude, ground_distance, speed.down, color_green); - pdraw_gles2hud_draw_speed(self, horizontal_speed, color_green); -#ifdef DEBUG_RADAR /* used to test the radar on records */ - if ((location.valid) && (session_meta->takeoff_loc.valid) && - (ctrl_orientation_valid) && (ctrl_meta->radar_angle > 0.)) { - pdraw_gles2hud_draw_controller_radar(self, - takeoff_distance, - takeoff_bearing, - ctrl_orientation.psi, - drone_attitude.psi, - ctrl_meta->radar_angle * - M_PI / 180., - color_green); - } -#else /* DEBUG_RADAR */ - if ((location.valid) && (ctrl_meta->location.valid) && - (ctrl_orientation_valid) && (ctrl_meta->radar_angle > 0.)) { - pdraw_gles2hud_draw_controller_radar(self, - self_distance, - self_bearing, - ctrl_orientation.psi, - drone_attitude.psi, - ctrl_meta->radar_angle * - M_PI / 180., - color_green); - } -#endif /* DEBUG_RADAR */ - if ((media_info->duration > 0) && - (media_info->duration != (uint64_t)-1)) { - pdraw_gles2hud_draw_record_timeline(self, - frame_extra->play_timestamp, - media_info->duration, - color_green); - } else if (media_info->playback_type == PDRAW_PLAYBACK_TYPE_LIVE) { - pdraw_gles2hud_draw_recording_status( - self, drone_meta->recording_duration, color_green); - } - pdraw_gles2hud_draw_vumeter(self, - self->config.vu_meter_zone_h_offset, - -self->config.vu_meter_v_interval, - 0.05, - battery_percentage, - 0., - 100., - 0., - 20., - color_green, - color_dark_green); - pdraw_gles2hud_draw_vumeter(self, - self->config.vu_meter_zone_h_offset, - 0.0, - 0.05, - wifi_rssi, - -90., - -20., - -90., - -70., - color_green, - color_dark_green); - pdraw_gles2hud_draw_vumeter(self, - self->config.vu_meter_zone_h_offset, - self->config.vu_meter_v_interval, - 0.05, - location.sv_count, - 0., - 30., - 0., - 5., - color_green, - color_dark_green); - - GLCHK(glDisableVertexAttribArray(self->position_handle)); - GLCHK(glDisableVertexAttribArray(self->color_handle)); - - GLCHK(glEnable(GL_BLEND)); - GLCHK(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - - GLCHK(glUseProgram(self->tex_program)); - GLCHK(glUniformMatrix4fv( - self->tex_transform_matrix_handle, 1, false, view_proj_mat)); - - GLCHK(glActiveTexture(GL_TEXTURE0 + self->icons_texunit)); - GLCHK(glBindTexture(GL_TEXTURE_2D, self->icons_texture)); - GLCHK(glUniform1i(self->tex_uniform_sampler, self->icons_texunit)); - GLCHK(glEnableVertexAttribArray(self->tex_position_handle)); - GLCHK(glEnableVertexAttribArray(self->tex_texcoord_handle)); - GLCHK(glEnableVertexAttribArray(self->tex_color_handle)); - - pdraw_gles2hud_draw_icon( - self, - 3, - self->config.vu_meter_zone_h_offset * self->ratio_w, - (-self->config.vu_meter_v_interval - 0.01) * self->ratio_h, - self->config.small_icon_size, - self->ratio_w, - self->ratio_w * self->aspect_ratio, - color_green); - pdraw_gles2hud_draw_icon(self, - 4, - self->config.vu_meter_zone_h_offset * - self->ratio_w, - (0.0 - 0.01) * self->ratio_h, - self->config.small_icon_size, - self->ratio_w, - self->ratio_w * self->aspect_ratio, - color_green); - pdraw_gles2hud_draw_icon( - self, - 5, - self->config.vu_meter_zone_h_offset * self->ratio_w, - (self->config.vu_meter_v_interval - 0.01) * self->ratio_h, - self->config.small_icon_size, - self->ratio_w, - self->ratio_w * self->aspect_ratio, - color_green); - if (drone_model != DRONE_MODEL_UNKNOWN) { - pdraw_gles2hud_draw_icon(self, - drone_model_icon, - 0.0, - self->config.heading_zone_v_offset * - self->ratio_h, - self->config.small_icon_size, - self->ratio_w, - self->ratio_w * self->aspect_ratio, - color_green); - } - float friendly_name_x_offset = - (self->config.vu_meter_zone_h_offset - 0.05) * self->ratio_w; - if ((strlen(media_info->session_meta->friendly_name) > 0) && - (drone_model != DRONE_MODEL_UNKNOWN)) { - pdraw_gles2hud_draw_icon( - self, - drone_model_icon, - friendly_name_x_offset + 0.025 * self->ratio_w, - self->config.roll_zone_v_offset * self->ratio_h + - 0.12 * self->ratio_w * self->aspect_ratio, - self->config.medium_icon_size, - self->ratio_w, - self->ratio_w * self->aspect_ratio, - color_green); - friendly_name_x_offset += 0.06 * self->ratio_w; - } - -#ifdef DEBUG_RADAR /* used to test the radar on records */ - if ((location.valid) && (session_meta->takeoff_loc.valid) && - (ctrl_orientation_valid)) { -#else /* DEBUG_RADAR */ - if ((location.valid) && (ctrl_meta->location.valid) && - (ctrl_orientation_valid)) { -#endif /* DEBUG_RADAR */ - float x = self->config.radar_zone_h_offset * self->ratio_w; - float y = self->config.radar_zone_v_offset * self->ratio_h; - pdraw_gles2hud_draw_icon(self, - 8, - x, - y, - self->config.small_icon_size, - self->ratio_w, - self->ratio_w * self->aspect_ratio, - color_green); - if ((drone_model != DRONE_MODEL_UNKNOWN) && - (takeoff_distance > 50.)) { -#ifdef DEBUG_RADAR /* used to test the radar on records */ - angle = M_PI / 2. + ctrl_orientation.psi - - (takeoff_bearing + M_PI); -#else /* DEBUG_RADAR */ - angle = M_PI / 2. + ctrl_orientation.psi - - (self_bearing + M_PI); -#endif /* DEBUG_RADAR */ - delta_x = x + 0.06 * cosf(angle) * self->ratio_w; - delta_y = y + 0.06 * sinf(angle) * self->ratio_w * - self->aspect_ratio; - angle = ctrl_orientation.psi - drone_attitude.psi; - model_mat << cosf(angle), -sinf(angle), 0., delta_x, - sinf(angle) * self->aspect_ratio, - cosf(angle) * self->aspect_ratio, 0., delta_y, - 0., 0., 1., 0., 0., 0., 0., 1.; - xform_mat = model_mat * view_proj; - GLCHK(glUniformMatrix4fv( - self->tex_transform_matrix_handle, - 1, - false, - xform_mat.data())); - pdraw_gles2hud_draw_icon(self, - drone_model_icon, - 0., - 0., - self->config.small_icon_size, - self->ratio_w, - self->ratio_w, - color_green); - } - } - - /* Heading controller icon */ - if (takeoff_distance > 50.) { - /* TODO: pilot */ - cy = 0.15 * self->ratio_w; - delta_x = 0.; - delta_y = self->config.heading_zone_v_offset * self->ratio_h; - angle = drone_attitude.psi - takeoff_bearing; - int angle_deg = ((int)(angle * 180. / M_PI + 70. + 360.)) % 360; - if (angle_deg <= 140) { - model_mat << cosf(angle), -sinf(angle), 0., delta_x, - sinf(angle) * self->aspect_ratio, - cosf(angle) * self->aspect_ratio, 0., delta_y, - 0., 0., 1., 0., 0., 0., 0., 1.; - xform_mat = model_mat * view_proj; - GLCHK(glUniformMatrix4fv( - self->tex_transform_matrix_handle, - 1, - false, - xform_mat.data())); - pdraw_gles2hud_draw_icon(self, - 8, - 0., - cy, - self->config.small_icon_size, - self->ratio_w, - self->ratio_w, - color_green); - } - } - - GLCHK(glActiveTexture(GL_TEXTURE0 + self->text_texunit)); - GLCHK(glBindTexture(GL_TEXTURE_2D, self->text_texture)); - GLCHK(glUniform1i(self->tex_uniform_sampler, self->text_texunit)); - - GLCHK(glUniformMatrix4fv( - self->tex_transform_matrix_handle, 1, false, view_proj_mat)); - - char str[20]; - snprintf(str, sizeof(str), "%d%%", battery_percentage); - pdraw_gles2hud_draw_text( - self, - str, - self->config.vu_meter_zone_h_offset * self->ratio_w, - (-self->config.vu_meter_v_interval - 0.07) * self->ratio_h, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, - PDRAW_GLES2HUD_TEXT_ALIGN_TOP, - color_green); - snprintf(str, sizeof(str), "%ddBm", wifi_rssi); - pdraw_gles2hud_draw_text(self, - str, - self->config.vu_meter_zone_h_offset * - self->ratio_w, - (0.0 - 0.07) * self->ratio_h, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, - PDRAW_GLES2HUD_TEXT_ALIGN_TOP, - color_green); - snprintf(str, sizeof(str), "%d", location.sv_count); - pdraw_gles2hud_draw_text( - self, - str, - self->config.vu_meter_zone_h_offset * self->ratio_w, - (self->config.vu_meter_v_interval - 0.07) * self->ratio_h, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, - PDRAW_GLES2HUD_TEXT_ALIGN_TOP, - color_green); - snprintf(str, sizeof(str), "ALT"); - pdraw_gles2hud_draw_text(self, - str, - self->config.central_zone_size * self->ratio_w, - (self->config.central_zone_size - 0.01) * - self->ratio_h, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_LEFT, - PDRAW_GLES2HUD_TEXT_ALIGN_BOTTOM, - color_green); - snprintf(str, sizeof(str), "%.1fm", altitude); - pdraw_gles2hud_draw_text(self, - str, - (self->config.central_zone_size + 0.04) * - self->ratio_w, - 0.0 * self->ratio_h, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_LEFT, - PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, - color_green); - if (drone_model == DRONE_MODEL_DISCO) { - if ((location.valid) && - (media_info->session_meta->takeoff_loc.valid)) { - double alt_diff = 0.; - if (!std::isnan(media_info->session_meta->takeoff_loc - .altitude_wgs84ellipsoid) && - !std::isnan(location.altitude_wgs84ellipsoid)) { - alt_diff = location.altitude_wgs84ellipsoid - - media_info->session_meta->takeoff_loc - .altitude_wgs84ellipsoid; - } else if (!std::isnan( - media_info->session_meta->takeoff_loc - .altitude_egm96amsl) && - !std::isnan(location.altitude_egm96amsl)) { - alt_diff = location.altitude_egm96amsl - - media_info->session_meta->takeoff_loc - .altitude_egm96amsl; - } - snprintf(str, sizeof(str), "DELTA: %+.1fm", alt_diff); - pdraw_gles2hud_draw_text( - self, - str, - self->config.central_zone_size * self->ratio_w, - (-self->config.central_zone_size + 0.01) * - self->ratio_h, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_LEFT, - PDRAW_GLES2HUD_TEXT_ALIGN_TOP, - color_green); - } - } else { - snprintf(str, sizeof(str), "GND: %.1fm", ground_distance); - pdraw_gles2hud_draw_text( - self, - str, - self->config.central_zone_size * self->ratio_w, - (-self->config.central_zone_size + 0.01) * - self->ratio_h, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_LEFT, - PDRAW_GLES2HUD_TEXT_ALIGN_TOP, - color_green); - } - snprintf(str, sizeof(str), "SPD"); - pdraw_gles2hud_draw_text( - self, - str, - -self->config.central_zone_size * self->ratio_w, - (self->config.central_zone_size - 0.01) * self->ratio_h, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT, - PDRAW_GLES2HUD_TEXT_ALIGN_BOTTOM, - color_green); - snprintf(str, sizeof(str), "%.1fm/s", horizontal_speed); - pdraw_gles2hud_draw_text(self, - str, - -(self->config.central_zone_size + 0.04) * - self->ratio_w, - 0.0 * self->ratio_h, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT, - PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, - color_green); - if (air_speed != -1.) { - snprintf(str, sizeof(str), "AIR: %4.1fm/s", air_speed); - pdraw_gles2hud_draw_text( - self, - str, - -self->config.central_zone_size * self->ratio_w, - (-self->config.central_zone_size + 0.01) * - self->ratio_h, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT, - PDRAW_GLES2HUD_TEXT_ALIGN_TOP, - color_green); - } - if (takeoff_distance != 0.) { - /* TODO: pilot */ - snprintf(str, sizeof(str), "DIST: %.0fm", takeoff_distance); - pdraw_gles2hud_draw_text( - self, - str, - 0.0, - (-self->config.central_zone_size / 2. - 0.10) * - self->ratio_w * self->aspect_ratio, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, - PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, - color_green); - } - if ((flying_state == VMETA_FLYING_STATE_TAKINGOFF) || - (flying_state == VMETA_FLYING_STATE_LANDING) || - (flying_state == VMETA_FLYING_STATE_EMERGENCY)) { - pdraw_gles2hud_draw_text( - self, - flying_state_str[flying_state], - 0.0, - (self->config.central_zone_size / 2. + 0.10) * - self->ratio_w * self->aspect_ratio, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, - PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, - color_green); - } else if ((piloting_mode == VMETA_PILOTING_MODE_RETURN_HOME) || - (piloting_mode == VMETA_PILOTING_MODE_FLIGHT_PLAN)) { - pdraw_gles2hud_draw_text( - self, - piloting_mode_str[piloting_mode], - 0.0, - (self->config.central_zone_size / 2. + 0.10) * - self->ratio_w * self->aspect_ratio, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, - PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, - color_green); - } - if (ctrl_meta->battery_percentage < 255) { - if (ctrl_meta->battery_percentage <= 100) { - snprintf(str, - sizeof(str), - "CTRL BAT: %d%%", - ctrl_meta->battery_percentage); - } else { - snprintf(str, sizeof(str), "CTRL BAT: --%%"); - } - pdraw_gles2hud_draw_text( - self, - str, - self->config.right_zone_h_offset * self->ratio_w, - 0. * self->ratio_w * self->aspect_ratio, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT, - PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, - color_green); - } - if (media_info->playback_type == PDRAW_PLAYBACK_TYPE_LIVE) { - snprintf(str, - sizeof(str), - "CTRL LOC: %s", - (ctrl_meta->location.valid) ? "OK" : "NOK"); - pdraw_gles2hud_draw_text( - self, - str, - self->config.right_zone_h_offset * self->ratio_w, - 0.05 * self->ratio_w * self->aspect_ratio, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT, - PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, - color_green); - } - if (strlen(media_info->session_meta->friendly_name) > 0) - pdraw_gles2hud_draw_text( - self, - media_info->session_meta->friendly_name, - friendly_name_x_offset, - self->config.roll_zone_v_offset * self->ratio_h + - 0.12 * self->ratio_w * self->aspect_ratio, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_LEFT, - PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, - color_green); - if ((frame_extra->play_timestamp > 0) && - (frame_extra->play_timestamp != (uint64_t)-1) && - (media_info->duration > 0) && - (media_info->duration != (uint64_t)-1)) { - uint64_t remaining_time = - media_info->duration - frame_extra->play_timestamp; - unsigned int c_hrs = 0, c_min = 0, c_sec = 0, c_msec = 0; - unsigned int r_hrs = 0, r_min = 0, r_sec = 0, r_msec = 0; - unsigned int d_hrs = 0, d_min = 0, d_sec = 0, d_msec = 0; - pdraw_gles2hud_friendly_time_from_us( - frame_extra->play_timestamp, - &c_hrs, - &c_min, - &c_sec, - &c_msec); - pdraw_gles2hud_friendly_time_from_us( - remaining_time, &r_hrs, &r_min, &r_sec, &r_msec); - pdraw_gles2hud_friendly_time_from_us( - media_info->duration, &d_hrs, &d_min, &d_sec, &d_msec); - if (d_hrs) { - snprintf(str, - sizeof(str), - "+%02d:%02d:%02d.%03d", - c_hrs, - c_min, - c_sec, - c_msec); - } else { - snprintf(str, - sizeof(str), - "+%02d:%02d.%03d", - c_min, - c_sec, - c_msec); - } - pdraw_gles2hud_draw_text( - self, - str, - (self->config.right_zone_h_offset - 0.4) * - self->ratio_w, - self->config.roll_zone_v_offset * self->ratio_h + - 0.12 * self->ratio_w * self->aspect_ratio, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_LEFT, - PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, - color_green); - if (d_hrs) { - snprintf(str, - sizeof(str), - "-%02d:%02d:%02d.%03d", - r_hrs, - r_min, - r_sec, - r_msec); - } else { - snprintf(str, - sizeof(str), - "-%02d:%02d.%03d", - r_min, - r_sec, - r_msec); - } - pdraw_gles2hud_draw_text( - self, - str, - self->config.right_zone_h_offset * self->ratio_w, - self->config.roll_zone_v_offset * self->ratio_h + - 0.12 * self->ratio_w * self->aspect_ratio, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT, - PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, - color_green); - if (d_hrs) { - snprintf(str, - sizeof(str), - "DUR: %02d:%02d:%02d", - d_hrs, - d_min, - d_sec); - } else { - snprintf(str, - sizeof(str), - "DUR: %02d:%02d", - d_min, - d_sec); - } - pdraw_gles2hud_draw_text( - self, - str, - (self->config.right_zone_h_offset - 0.2) * - self->ratio_w, - self->config.roll_zone_v_offset * self->ratio_h + - 0.10 * self->ratio_w * self->aspect_ratio, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, - PDRAW_GLES2HUD_TEXT_ALIGN_TOP, - color_green); - } else if (media_info->playback_type == PDRAW_PLAYBACK_TYPE_LIVE) { - if (drone_meta->recording_duration > 0) { - unsigned int d_hrs = 0, d_min = 0; - unsigned int d_sec = 0, d_msec = 0; - pdraw_gles2hud_friendly_time_from_us( - drone_meta->recording_duration, - &d_hrs, - &d_min, - &d_sec, - &d_msec); - if (d_hrs) { - snprintf(str, - sizeof(str), - "REC %02d:%02d:%02d", - d_hrs, - d_min, - d_sec); - } else { - snprintf(str, - sizeof(str), - "REC %02d:%02d", - d_min, - d_sec); - } - pdraw_gles2hud_draw_text( - self, - str, - self->config.right_zone_h_offset * - self->ratio_w, - self->config.roll_zone_v_offset * - self->ratio_h + - 0.12 * self->ratio_w * - self->aspect_ratio, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT, - PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, - color_green); - } else { - pdraw_gles2hud_draw_text( - self, - "REC", - self->config.right_zone_h_offset * - self->ratio_w, - self->config.roll_zone_v_offset * - self->ratio_h + - 0.12 * self->ratio_w * - self->aspect_ratio, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT, - PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, - color_green); - } - } - snprintf(str, sizeof(str), "%03d", heading_int); - pdraw_gles2hud_draw_text(self, - str, - 0. * self->ratio_w, - (self->config.heading_zone_v_offset + 0.10) * - self->ratio_h, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, - PDRAW_GLES2HUD_TEXT_ALIGN_BOTTOM, - color_green); - - float height = self->config.central_zone_size * self->ratio_w * - self->aspect_ratio; - steps = 6; - for (i = -steps; i <= steps; i++) { - if ((i != 0) && (!(i & 1))) { - snprintf(str, sizeof(str), "%+2d ", i * 10); - pdraw_gles2hud_draw_text( - self, - str, - 0. * self->ratio_w, - i * height / 2 / steps, - self->config.text_size * self->ratio_w, - 1., - self->aspect_ratio, - PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, - PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, - color_green); - } - } - - /* Roll text */ - cy = 0.10 * self->ratio_w; - delta_x = 0.; - delta_y = self->config.roll_zone_v_offset * self->ratio_h; - steps = 2; - model_mat << 1., 0., 0., delta_x, 0., 1., 0., delta_y, 0., 0., 1., 0., - 0., 0., 0., 1.; - for (i = -steps, angle = M_PI * (-30. * steps) / 180.; i <= steps; - i++, angle += M_PI * 30. / 180.) { - angle_deg = (i * 30 + 60 + 360) % 360; - if (angle_deg <= 120) { - model_mat(0, 0) = cosf(angle); - model_mat(1, 0) = sinf(angle) * self->aspect_ratio; - model_mat(0, 1) = -sinf(angle); - model_mat(1, 1) = cosf(angle) * self->aspect_ratio; - xform_mat = model_mat * view_proj; - GLCHK(glUniformMatrix4fv( - self->tex_transform_matrix_handle, - 1, - false, - xform_mat.data())); - if (i == 0) - snprintf(str, sizeof(str), "0"); - else - snprintf(str, sizeof(str), "%+2d ", -i * 30); - pdraw_gles2hud_draw_text( - self, - str, - 0., - cy, - self->config.text_size * self->ratio_w, - 1., - 1., - PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, - PDRAW_GLES2HUD_TEXT_ALIGN_TOP, - color_green); - } - } - - /* Heading text */ - cy = 0.10 * self->ratio_w; - delta_x = 0.; - delta_y = self->config.heading_zone_v_offset * self->ratio_h; - model_mat << 1., 0., 0., delta_x, 0., 1., 0., delta_y, 0., 0., 1., 0., - 0., 0., 0., 1.; - for (i = 0, angle = drone_attitude.psi; i < 8; - i++, angle += M_PI / 4.) { - angle_deg = (heading_int + i * 45 + 70 + 360) % 360; - if (angle_deg <= 140) { - model_mat(0, 0) = cosf(angle); - model_mat(1, 0) = sinf(angle) * self->aspect_ratio; - model_mat(0, 1) = -sinf(angle); - model_mat(1, 1) = cosf(angle) * self->aspect_ratio; - xform_mat = model_mat * view_proj; - GLCHK(glUniformMatrix4fv( - self->tex_transform_matrix_handle, - 1, - false, - xform_mat.data())); - pdraw_gles2hud_draw_text( - self, - heading_str[i], - 0., - cy, - self->config.text_size, - 1., - 1., - PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, - PDRAW_GLES2HUD_TEXT_ALIGN_TOP, - color_green); - } - } - - /* Radar text */ -#ifdef DEBUG_RADAR /* used to test the radar on records */ - if ((location.valid) && (session_meta->takeoff_loc.valid) && - (ctrl_orientation_valid)) { -#else /* DEBUG_RADAR */ - if ((location.valid) && (ctrl_meta->location.valid) && - (ctrl_orientation_valid)) { -#endif /* DEBUG_RADAR */ - cy = 0.09 * self->ratio_w; - delta_x = self->config.radar_zone_h_offset * self->ratio_w; - delta_y = self->config.radar_zone_v_offset * self->ratio_h; - model_mat << 1., 0., 0., delta_x, 0., 1., 0., delta_y, 0., 0., - 1., 0., 0., 0., 0., 1.; - for (i = 0, angle = ctrl_orientation.psi; i < 8; - i += 2, angle += M_PI / 2.) { - model_mat(0, 0) = cosf(angle); - model_mat(1, 0) = sinf(angle) * self->aspect_ratio; - model_mat(0, 1) = -sinf(angle); - model_mat(1, 1) = cosf(angle) * self->aspect_ratio; - xform_mat = model_mat * view_proj; - GLCHK(glUniformMatrix4fv( - self->tex_transform_matrix_handle, - 1, - false, - xform_mat.data())); - pdraw_gles2hud_draw_text( - self, - heading_str[i], - 0., - cy, - self->config.text_size, - 1., - 1., - PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, - PDRAW_GLES2HUD_TEXT_ALIGN_BOTTOM, - color_green); - } - } - - GLCHK(glDisableVertexAttribArray(self->tex_position_handle)); - GLCHK(glDisableVertexAttribArray(self->tex_texcoord_handle)); - GLCHK(glDisableVertexAttribArray(self->tex_color_handle)); - - return 0; -} - - -static int pdraw_gles2hud_render_imaging( - struct pdraw_gles2hud *self, - const struct pdraw_rect *render_pos, - const struct pdraw_rect *content_pos, - const float view_proj_mat[16], - const struct pdraw_media_info *media_info, - struct vmeta_frame *frame_meta, - const struct pdraw_video_frame_extra *frame_extra, - const struct pdraw_gles2hud_controller_meta *ctrl_meta, - const struct pdraw_gles2hud_drone_meta *drone_meta) -{ - if (self == nullptr) - return -EINVAL; - if ((render_pos == nullptr) || (render_pos->width == 0) || - (render_pos->height == 0)) - return -EINVAL; - if ((content_pos == nullptr) || (content_pos->width == 0) || - (content_pos->height == 0)) - return -EINVAL; - if (media_info == nullptr) - return -EINVAL; - if (frame_meta == nullptr) - return -EINVAL; - if (frame_extra == nullptr) - return -EINVAL; - if (ctrl_meta == nullptr) - return -EINVAL; - if (drone_meta == nullptr) - return -EINVAL; - - /* Picture field of view */ - pdraw_gles2hud_get_fov(self, media_info->session_meta, frame_meta); - - self->ratio_w = (float)content_pos->width / render_pos->width * - self->config.scale; - self->ratio_h = (float)content_pos->height / render_pos->height * - self->config.scale; - self->aspect_ratio = (float)render_pos->width / render_pos->height; - - GLCHK(glEnable(GL_BLEND)); - GLCHK(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - GLCHK(glUseProgram(self->program)); - - GLCHK(glEnableVertexAttribArray(self->position_handle)); - GLCHK(glEnableVertexAttribArray(self->color_handle)); - - GLCHK(glUniformMatrix4fv( - self->transform_matrix_handle, 1, false, view_proj_mat)); - - pdraw_gles2hud_draw_framing_grid( - self, render_pos, content_pos, color_black_alpha); - pdraw_gles2hud_draw_histograms(self, frame_extra); - - GLCHK(glDisableVertexAttribArray(self->position_handle)); - GLCHK(glDisableVertexAttribArray(self->color_handle)); - - return 0; -} - - -int pdraw_gles2hud_render( - struct pdraw_gles2hud *self, - enum pdraw_gles2hud_type type, - const struct pdraw_rect *render_pos, - const struct pdraw_rect *content_pos, - const float view_proj_mat[16], - const struct pdraw_media_info *media_info, - struct vmeta_frame *frame_meta, - const struct pdraw_video_frame_extra *frame_extra, - const struct pdraw_gles2hud_controller_meta *ctrl_meta, - const struct pdraw_gles2hud_drone_meta *drone_meta) -{ - switch (type) { - case PDRAW_GLES2HUD_TYPE_PILOTING: - return pdraw_gles2hud_render_piloting(self, - render_pos, - content_pos, - view_proj_mat, - media_info, - frame_meta, - frame_extra, - ctrl_meta, - drone_meta); - case PDRAW_GLES2HUD_TYPE_IMAGING: - return pdraw_gles2hud_render_imaging(self, - render_pos, - content_pos, - view_proj_mat, - media_info, - frame_meta, - frame_extra, - ctrl_meta, - drone_meta); - default: - ULOGE("unsupported HUD type: %d", type); - return -ENOSYS; - } -} +/** + * Parrot Drones Awesome Video Viewer + * OpenGL ES 2.0 HUD rendering library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 "pdraw_gles2hud_priv.h" +ULOG_DECLARE_TAG(pdraw_gles2hud); + +/* used to test the radar on records */ +#if 0 +# define DEBUG_RADAR +#endif + + +enum drone_model { + /* Unknown drone model */ + DRONE_MODEL_UNKNOWN = 0, + + /* Parrot Bebop */ + DRONE_MODEL_BEBOP, + + /* Parrot Bebop 2 */ + DRONE_MODEL_BEBOP2, + + /* Parrot Disco */ + DRONE_MODEL_DISCO, + + /* Parrot Bluegrass */ + DRONE_MODEL_BLUEGRASS, + + /* Parrot Anafi */ + DRONE_MODEL_ANAFI, + + /* Parrot Anafi Thermal */ + DRONE_MODEL_ANAFI_THERMAL, +}; + + +static const int drone_model_icon_index[] = { + 2, + 0, + 2, + 1, + 2, + 2, + 2, +}; + +static const char *heading_str[] = { + ".N.", + "NW", + "W", + "SW", + "S", + "SE", + "E", + "NE", +}; + +static const char *flying_state_str[] = { + "LANDED", + "TAKING OFF", + "HOVERING", + "FLYING", + "LANDING", + "EMERGENCY", +}; + +static const char *piloting_mode_str[] = { + "MANUAL", + "RETURN HOME", + "FLIGHT PLAN", + "FOLLOW ME", +}; + +static const float color_green[4] = {0.0f, 0.9f, 0.0f, 1.0f}; + +static const float color_dark_green[4] = {0.0f, 0.5f, 0.0f, 1.0f}; + +static const float color_black_alpha[4] = {0.0f, 0.0f, 0.0f, 0.66f}; + + +static enum drone_model +get_drone_model(const struct vmeta_session *session_meta) +{ + if (strcmp(session_meta->model_id, "0901") == 0) + return DRONE_MODEL_BEBOP; + else if (strcmp(session_meta->model_id, "090c") == 0) + return DRONE_MODEL_BEBOP2; + else if (strcmp(session_meta->model_id, "090e") == 0) + return DRONE_MODEL_DISCO; + else if (strcmp(session_meta->model_id, "0916") == 0) + return DRONE_MODEL_BLUEGRASS; + else if (strcmp(session_meta->model_id, "0914") == 0) + return DRONE_MODEL_ANAFI; + else if (strcmp(session_meta->model_id, "0919") == 0) + return DRONE_MODEL_ANAFI_THERMAL; + else if (strcmp(session_meta->model, "Bebop") == 0) + return DRONE_MODEL_BEBOP; + else if (strcmp(session_meta->model, "Bebop 2") == 0) + return DRONE_MODEL_BEBOP2; + else if (strcmp(session_meta->model, "Disco") == 0) + return DRONE_MODEL_DISCO; + else if (strcmp(session_meta->model, "Bluegrass") == 0) + return DRONE_MODEL_BLUEGRASS; + else if (strcmp(session_meta->model, "ANAFI") == 0) + return DRONE_MODEL_ANAFI; + else if (strcmp(session_meta->model, "Anafi") == 0) + return DRONE_MODEL_ANAFI; + else if (strcmp(session_meta->model, "AnafiThermal") == 0) + return DRONE_MODEL_ANAFI_THERMAL; + else if (strcmp(session_meta->friendly_name, "Parrot Bebop") == 0) + return DRONE_MODEL_BEBOP; + else if (strcmp(session_meta->friendly_name, "Parrot Bebop 2") == 0) + return DRONE_MODEL_BEBOP2; + else if (strcmp(session_meta->friendly_name, "Parrot Disco") == 0) + return DRONE_MODEL_DISCO; + else + return DRONE_MODEL_UNKNOWN; +} + + +int pdraw_gles2hud_new(const struct pdraw_gles2hud_config *config, + struct pdraw_gles2hud **hud) +{ + int res, err; + struct pdraw_gles2hud *self; + + if (config == nullptr) + return -EINVAL; + if (hud == nullptr) + return -EINVAL; + + self = (struct pdraw_gles2hud *)calloc(1, sizeof(*self)); + if (self == nullptr) + return -ENOMEM; + + self->config = *config; + if (self->config.central_zone_size <= 0.) { + self->config.central_zone_size = + PDRAW_GLES2HUD_DEFAULT_CENTRAL_ZONE_SIZE; + } + if (self->config.heading_zone_v_offset <= 0.) { + self->config.heading_zone_v_offset = + PDRAW_GLES2HUD_DEFAULT_HEADING_ZONE_V_OFFSET; + } + if (self->config.roll_zone_v_offset <= 0.) { + self->config.roll_zone_v_offset = + PDRAW_GLES2HUD_DEFAULT_ROLL_ZONE_V_OFFSET; + } + if (self->config.vu_meter_zone_h_offset <= 0.) { + self->config.vu_meter_zone_h_offset = + PDRAW_GLES2HUD_DEFAULT_VU_METER_ZONE_H_OFFSET; + } + if (self->config.vu_meter_v_interval <= 0.) { + self->config.vu_meter_v_interval = + PDRAW_GLES2HUD_DEFAULT_VU_METER_V_INTERVAL; + } + if (self->config.right_zone_h_offset <= 0.) { + self->config.right_zone_h_offset = + PDRAW_GLES2HUD_DEFAULT_RIGHT_ZONE_H_OFFSET; + } + if (self->config.radar_zone_h_offset <= 0.) { + self->config.radar_zone_h_offset = + PDRAW_GLES2HUD_DEFAULT_RADAR_ZONE_H_OFFSET; + } + if (self->config.radar_zone_v_offset <= 0.) { + self->config.radar_zone_v_offset = + PDRAW_GLES2HUD_DEFAULT_RADAR_ZONE_V_OFFSET; + } + if (self->config.text_size <= 0.) { + self->config.text_size = PDRAW_GLES2HUD_DEFAULT_TEXT_SIZE; + } + if (self->config.small_icon_size <= 0.) { + self->config.small_icon_size = + PDRAW_GLES2HUD_DEFAULT_SMALL_ICON_SIZE; + } + if (self->config.medium_icon_size <= 0.) { + self->config.medium_icon_size = + PDRAW_GLES2HUD_DEFAULT_MEDIUM_ICON_SIZE; + } + if (self->config.scale <= 0.) { + self->config.scale = PDRAW_GLES2HUD_DEFAULT_SCALE; + } + + self->ratio_w = 1.; + self->ratio_h = 1.; + self->aspect_ratio = 1.; + self->h_fov = 0.; + self->v_fov = 0.; + + res = pdraw_gles2hud_create_programs(self); + if (res < 0) { + ULOG_ERRNO("pdraw_gles2hud_create_programs", -res); + goto error; + } + + *hud = self; + return 0; + +error: + err = pdraw_gles2hud_destroy(self); + if (err < 0) + ULOG_ERRNO("pdraw_gles2hud_destroy", -err); + return res; +} + + +int pdraw_gles2hud_destroy(struct pdraw_gles2hud *self) +{ + if (self == nullptr) + return 0; + + if (self->program > 0) { + glDeleteProgram(self->program); + self->program = 0; + } + if (self->tex_program > 0) { + glDeleteProgram(self->tex_program); + self->tex_program = 0; + } + if (self->icons_texture > 0) { + glDeleteTextures(1, &self->icons_texture); + self->icons_texture = 0; + } + if (self->text_texture > 0) { + glDeleteTextures(1, &self->text_texture); + self->text_texture = 0; + } + + free(self); + + return 0; +} + + +int pdraw_gles2hud_get_config(struct pdraw_gles2hud *self, + struct pdraw_gles2hud_config *config) +{ + if (self == nullptr) + return -EINVAL; + if (config == nullptr) + return -EINVAL; + + *config = self->config; + + return 0; +} + + +int pdraw_gles2hud_set_config(struct pdraw_gles2hud *self, + const struct pdraw_gles2hud_config *config) +{ + if (self == nullptr) + return -EINVAL; + if (config == nullptr) + return -EINVAL; + + self->config = *config; + if (self->config.central_zone_size <= 0.) { + self->config.central_zone_size = + PDRAW_GLES2HUD_DEFAULT_CENTRAL_ZONE_SIZE; + } + if (self->config.heading_zone_v_offset <= 0.) { + self->config.heading_zone_v_offset = + PDRAW_GLES2HUD_DEFAULT_HEADING_ZONE_V_OFFSET; + } + if (self->config.roll_zone_v_offset <= 0.) { + self->config.roll_zone_v_offset = + PDRAW_GLES2HUD_DEFAULT_ROLL_ZONE_V_OFFSET; + } + if (self->config.vu_meter_zone_h_offset <= 0.) { + self->config.vu_meter_zone_h_offset = + PDRAW_GLES2HUD_DEFAULT_VU_METER_ZONE_H_OFFSET; + } + if (self->config.vu_meter_v_interval <= 0.) { + self->config.vu_meter_v_interval = + PDRAW_GLES2HUD_DEFAULT_VU_METER_V_INTERVAL; + } + if (self->config.right_zone_h_offset <= 0.) { + self->config.right_zone_h_offset = + PDRAW_GLES2HUD_DEFAULT_RIGHT_ZONE_H_OFFSET; + } + if (self->config.radar_zone_h_offset <= 0.) { + self->config.radar_zone_h_offset = + PDRAW_GLES2HUD_DEFAULT_RADAR_ZONE_H_OFFSET; + } + if (self->config.radar_zone_v_offset <= 0.) { + self->config.radar_zone_v_offset = + PDRAW_GLES2HUD_DEFAULT_RADAR_ZONE_V_OFFSET; + } + if (self->config.text_size <= 0.) { + self->config.text_size = PDRAW_GLES2HUD_DEFAULT_TEXT_SIZE; + } + if (self->config.small_icon_size <= 0.) { + self->config.small_icon_size = + PDRAW_GLES2HUD_DEFAULT_SMALL_ICON_SIZE; + } + if (self->config.medium_icon_size <= 0.) { + self->config.medium_icon_size = + PDRAW_GLES2HUD_DEFAULT_MEDIUM_ICON_SIZE; + } + if (self->config.scale <= 0.) { + self->config.scale = PDRAW_GLES2HUD_DEFAULT_SCALE; + } + + return 0; +} + + +static void pdraw_gles2hud_get_fov(struct pdraw_gles2hud *self, + const struct vmeta_session *session_meta, + struct vmeta_frame *frame_meta) +{ + if ((frame_meta) && (frame_meta->type == VMETA_FRAME_TYPE_V3)) { + self->h_fov = frame_meta->v3.base.picture_hfov; + self->v_fov = frame_meta->v3.base.picture_vfov; + } else if ((session_meta) && (session_meta->picture_fov.has_horz) && + (session_meta->picture_fov.has_horz)) { + self->h_fov = session_meta->picture_fov.horz; + self->v_fov = session_meta->picture_fov.vert; + } else { + self->h_fov = PDRAW_GLES2HUD_DEFAULT_HFOV; + self->v_fov = PDRAW_GLES2HUD_DEFAULT_VFOV; + } + self->h_fov *= M_PI / 180.; + self->v_fov *= M_PI / 180.; +} + + +static int pdraw_gles2hud_render_piloting( + struct pdraw_gles2hud *self, + const struct pdraw_rect *render_pos, + const struct pdraw_rect *content_pos, + const float view_proj_mat[16], + const struct pdraw_media_info *media_info, + struct vmeta_frame *frame_meta, + const struct pdraw_video_frame_extra *frame_extra, + const struct pdraw_gles2hud_controller_meta *ctrl_meta, + const struct pdraw_gles2hud_drone_meta *drone_meta) +{ + int i, j, steps, angle_deg; + float cy, delta_x, delta_y, angle; + Eigen::Matrix4f xform_mat; + Eigen::Matrix4f model_mat; + Eigen::Matrix4f view_proj; + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) + view_proj(i, j) = view_proj_mat[i * 4 + j]; + } + + if (self == nullptr) + return -EINVAL; + if ((render_pos == nullptr) || (render_pos->width == 0) || + (render_pos->height == 0)) + return -EINVAL; + if ((content_pos == nullptr) || (content_pos->width == 0) || + (content_pos->height == 0)) + return -EINVAL; + if (media_info == nullptr) + return -EINVAL; + if (frame_meta == nullptr) + return -EINVAL; + if (frame_extra == nullptr) + return -EINVAL; + if (ctrl_meta == nullptr) + return -EINVAL; + if (drone_meta == nullptr) + return -EINVAL; + + uint8_t battery_percentage; + vmeta_frame_get_battery_percentage(frame_meta, &battery_percentage); + int8_t wifi_rssi; + vmeta_frame_get_wifi_rssi(frame_meta, &wifi_rssi); + enum vmeta_flying_state flying_state; + vmeta_frame_get_flying_state(frame_meta, &flying_state); + enum vmeta_piloting_mode piloting_mode; + vmeta_frame_get_piloting_mode(frame_meta, &piloting_mode); + enum drone_model drone_model = + get_drone_model(media_info->session_meta); + int drone_model_icon = drone_model_icon_index[drone_model]; + + /* Drone location and ground distance */ + struct vmeta_location location; + vmeta_frame_get_location(frame_meta, &location); + double ground_distance; + vmeta_frame_get_ground_distance(frame_meta, &ground_distance); + if ((drone_model == DRONE_MODEL_DISCO) && (location.valid) && + (media_info->session_meta->takeoff_loc.valid)) { + ground_distance = location.altitude_egm96amsl - + media_info->session_meta->takeoff_loc + .altitude_egm96amsl; + } + + /* Drone speeds */ + struct vmeta_ned speed; + vmeta_frame_get_speed_ned(frame_meta, &speed); + float air_speed; + vmeta_frame_get_air_speed(frame_meta, &air_speed); + float horizontal_speed = + sqrtf(speed.north * speed.north + speed.east * speed.east); + float speed_psi = atan2f(speed.east, speed.north); + /* UNUSED + * float speed_rho = sqrtf(speed.north * speed.north + + * speed.east * speed.east + + * speed.down * speed.down); + * float speed_theta = M_PI / 2 - acosf(speed.down / speed_rho); */ + + /* Drone attitude and frame orientation */ + struct vmeta_euler drone_attitude; + vmeta_frame_get_drone_euler(frame_meta, &drone_attitude); + struct vmeta_euler frame_orientation; + vmeta_frame_get_frame_euler(frame_meta, &frame_orientation); + int heading_int = ((int)(drone_attitude.psi * RAD_TO_DEG) + 360) % 360; + + /* Picture field of view */ + pdraw_gles2hud_get_fov(self, media_info->session_meta, frame_meta); + + /* Distace to take-off */ + double takeoff_distance = 0.; + double takeoff_bearing = 0.; + double takeoff_elevation = 0.; + if ((location.valid) && (media_info->session_meta->takeoff_loc.valid)) { + pdraw_gles2hud_coords_distance_and_bearing( + location.latitude, + location.longitude, + media_info->session_meta->takeoff_loc.latitude, + media_info->session_meta->takeoff_loc.longitude, + &takeoff_distance, + &takeoff_bearing); + double alt_diff = 0.; + if (!std::isnan(media_info->session_meta->takeoff_loc + .altitude_wgs84ellipsoid) && + !std::isnan(location.altitude_wgs84ellipsoid != NAN)) { + alt_diff = media_info->session_meta->takeoff_loc + .altitude_wgs84ellipsoid - + location.altitude_wgs84ellipsoid; + } else if (!std::isnan(media_info->session_meta->takeoff_loc + .altitude_egm96amsl) && + !std::isnan(location.altitude_egm96amsl)) { + alt_diff = media_info->session_meta->takeoff_loc + .altitude_egm96amsl - + location.altitude_egm96amsl; + } + takeoff_elevation = atan2(alt_diff, takeoff_distance); + } + + /* Distace to pilot */ + double self_distance = 0.; + double self_bearing = 0.; + double self_elevation = 0.; + if ((location.valid) && (ctrl_meta->location.valid)) { + pdraw_gles2hud_coords_distance_and_bearing( + location.latitude, + location.longitude, + ctrl_meta->location.latitude, + ctrl_meta->location.longitude, + &self_distance, + &self_bearing); + double alt_diff = 0.; + if (!std::isnan(ctrl_meta->location.altitude_wgs84ellipsoid) && + !std::isnan(location.altitude_wgs84ellipsoid)) { + alt_diff = ctrl_meta->location.altitude_wgs84ellipsoid - + location.altitude_wgs84ellipsoid; + } else if (!std::isnan( + ctrl_meta->location.altitude_egm96amsl) && + !std::isnan(location.altitude_egm96amsl)) { + alt_diff = ctrl_meta->location.altitude_egm96amsl - + location.altitude_egm96amsl; + } + self_elevation = atan2(alt_diff, self_distance); + } + + /* Controller orientation */ + struct vmeta_euler ctrl_orientation; + memset(&ctrl_orientation, 0, sizeof(ctrl_orientation)); +#ifdef DEBUG_RADAR /* used to test the radar on records */ + int ctrl_orientation_valid = 1; +#else /* DEBUG_RADAR */ + int ctrl_orientation_valid = ctrl_meta->has_quat; + if (ctrl_orientation_valid) { + vmeta_quat_to_euler(&ctrl_meta->quat, &ctrl_orientation); + } +#endif /* DEBUG_RADAR */ + + self->ratio_w = (float)content_pos->width / render_pos->width * + self->config.scale; + self->ratio_h = (float)content_pos->height / render_pos->height * + self->config.scale; + self->aspect_ratio = (float)render_pos->width / render_pos->height; + + GLCHK(glUseProgram(self->program)); + + GLCHK(glEnableVertexAttribArray(self->position_handle)); + GLCHK(glEnableVertexAttribArray(self->color_handle)); + + GLCHK(glUniformMatrix4fv( + self->transform_matrix_handle, 1, false, view_proj_mat)); + +#if 0 + if (horizontal_speed >= 0.2) + pdraw_gles2hud_draw_flight_path_vector(self, &frame_orientation, + speed_theta, speed_psi, + color_green); +#endif + + /* Draw instruments */ + pdraw_gles2hud_draw_artificial_horizon( + self, &drone_attitude, &frame_orientation, color_green); + pdraw_gles2hud_draw_roll(self, drone_attitude.phi, color_green); + pdraw_gles2hud_draw_heading(self, + drone_attitude.psi, + horizontal_speed, + speed_psi, + color_green); + double altitude = 0.; + if (!std::isnan(location.altitude_wgs84ellipsoid)) + altitude = location.altitude_wgs84ellipsoid; + else if (!std::isnan(location.altitude_egm96amsl)) + altitude = location.altitude_egm96amsl; + pdraw_gles2hud_draw_altitude( + self, altitude, ground_distance, speed.down, color_green); + pdraw_gles2hud_draw_speed(self, horizontal_speed, color_green); +#ifdef DEBUG_RADAR /* used to test the radar on records */ + if ((location.valid) && (session_meta->takeoff_loc.valid) && + (ctrl_orientation_valid) && (ctrl_meta->radar_angle > 0.)) { + pdraw_gles2hud_draw_controller_radar(self, + takeoff_distance, + takeoff_bearing, + ctrl_orientation.psi, + drone_attitude.psi, + ctrl_meta->radar_angle * + M_PI / 180., + color_green); + } +#else /* DEBUG_RADAR */ + if ((location.valid) && (ctrl_meta->location.valid) && + (ctrl_orientation_valid) && (ctrl_meta->radar_angle > 0.)) { + pdraw_gles2hud_draw_controller_radar(self, + self_distance, + self_bearing, + ctrl_orientation.psi, + drone_attitude.psi, + ctrl_meta->radar_angle * + M_PI / 180., + color_green); + } +#endif /* DEBUG_RADAR */ + if ((media_info->duration > 0) && + (media_info->duration != (uint64_t)-1)) { + pdraw_gles2hud_draw_record_timeline(self, + frame_extra->play_timestamp, + media_info->duration, + color_green); + } else if (media_info->playback_type == PDRAW_PLAYBACK_TYPE_LIVE) { + pdraw_gles2hud_draw_recording_status( + self, drone_meta->recording_duration, color_green); + } + pdraw_gles2hud_draw_vumeter(self, + self->config.vu_meter_zone_h_offset, + -self->config.vu_meter_v_interval, + 0.05, + battery_percentage, + 0., + 100., + 0., + 20., + color_green, + color_dark_green); + pdraw_gles2hud_draw_vumeter(self, + self->config.vu_meter_zone_h_offset, + 0.0, + 0.05, + wifi_rssi, + -90., + -20., + -90., + -70., + color_green, + color_dark_green); + pdraw_gles2hud_draw_vumeter(self, + self->config.vu_meter_zone_h_offset, + self->config.vu_meter_v_interval, + 0.05, + location.sv_count, + 0., + 30., + 0., + 5., + color_green, + color_dark_green); + + GLCHK(glDisableVertexAttribArray(self->position_handle)); + GLCHK(glDisableVertexAttribArray(self->color_handle)); + + GLCHK(glEnable(GL_BLEND)); + GLCHK(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + + GLCHK(glUseProgram(self->tex_program)); + GLCHK(glUniformMatrix4fv( + self->tex_transform_matrix_handle, 1, false, view_proj_mat)); + + GLCHK(glActiveTexture(GL_TEXTURE0 + self->icons_texunit)); + GLCHK(glBindTexture(GL_TEXTURE_2D, self->icons_texture)); + GLCHK(glUniform1i(self->tex_uniform_sampler, self->icons_texunit)); + GLCHK(glEnableVertexAttribArray(self->tex_position_handle)); + GLCHK(glEnableVertexAttribArray(self->tex_texcoord_handle)); + GLCHK(glEnableVertexAttribArray(self->tex_color_handle)); + + pdraw_gles2hud_draw_icon( + self, + 3, + self->config.vu_meter_zone_h_offset * self->ratio_w, + (-self->config.vu_meter_v_interval - 0.01) * self->ratio_h, + self->config.small_icon_size, + self->ratio_w, + self->ratio_w * self->aspect_ratio, + color_green); + pdraw_gles2hud_draw_icon(self, + 4, + self->config.vu_meter_zone_h_offset * + self->ratio_w, + (0.0 - 0.01) * self->ratio_h, + self->config.small_icon_size, + self->ratio_w, + self->ratio_w * self->aspect_ratio, + color_green); + pdraw_gles2hud_draw_icon( + self, + 5, + self->config.vu_meter_zone_h_offset * self->ratio_w, + (self->config.vu_meter_v_interval - 0.01) * self->ratio_h, + self->config.small_icon_size, + self->ratio_w, + self->ratio_w * self->aspect_ratio, + color_green); + if (drone_model != DRONE_MODEL_UNKNOWN) { + pdraw_gles2hud_draw_icon(self, + drone_model_icon, + 0.0, + self->config.heading_zone_v_offset * + self->ratio_h, + self->config.small_icon_size, + self->ratio_w, + self->ratio_w * self->aspect_ratio, + color_green); + } + float friendly_name_x_offset = + (self->config.vu_meter_zone_h_offset - 0.05) * self->ratio_w; + if ((strlen(media_info->session_meta->friendly_name) > 0) && + (drone_model != DRONE_MODEL_UNKNOWN)) { + pdraw_gles2hud_draw_icon( + self, + drone_model_icon, + friendly_name_x_offset + 0.025 * self->ratio_w, + self->config.roll_zone_v_offset * self->ratio_h + + 0.12 * self->ratio_w * self->aspect_ratio, + self->config.medium_icon_size, + self->ratio_w, + self->ratio_w * self->aspect_ratio, + color_green); + friendly_name_x_offset += 0.06 * self->ratio_w; + } + +#ifdef DEBUG_RADAR /* used to test the radar on records */ + if ((location.valid) && (session_meta->takeoff_loc.valid) && + (ctrl_orientation_valid)) { +#else /* DEBUG_RADAR */ + if ((location.valid) && (ctrl_meta->location.valid) && + (ctrl_orientation_valid)) { +#endif /* DEBUG_RADAR */ + float x = self->config.radar_zone_h_offset * self->ratio_w; + float y = self->config.radar_zone_v_offset * self->ratio_h; + pdraw_gles2hud_draw_icon(self, + 8, + x, + y, + self->config.small_icon_size, + self->ratio_w, + self->ratio_w * self->aspect_ratio, + color_green); + if ((drone_model != DRONE_MODEL_UNKNOWN) && + (takeoff_distance > 50.)) { +#ifdef DEBUG_RADAR /* used to test the radar on records */ + angle = M_PI / 2. + ctrl_orientation.psi - + (takeoff_bearing + M_PI); +#else /* DEBUG_RADAR */ + angle = M_PI / 2. + ctrl_orientation.psi - + (self_bearing + M_PI); +#endif /* DEBUG_RADAR */ + delta_x = x + 0.06 * cosf(angle) * self->ratio_w; + delta_y = y + 0.06 * sinf(angle) * self->ratio_w * + self->aspect_ratio; + angle = ctrl_orientation.psi - drone_attitude.psi; + model_mat << cosf(angle), -sinf(angle), 0., delta_x, + sinf(angle) * self->aspect_ratio, + cosf(angle) * self->aspect_ratio, 0., delta_y, + 0., 0., 1., 0., 0., 0., 0., 1.; + xform_mat = model_mat * view_proj; + GLCHK(glUniformMatrix4fv( + self->tex_transform_matrix_handle, + 1, + false, + xform_mat.data())); + pdraw_gles2hud_draw_icon(self, + drone_model_icon, + 0., + 0., + self->config.small_icon_size, + self->ratio_w, + self->ratio_w, + color_green); + } + } + + /* Heading controller icon */ + if (takeoff_distance > 50.) { + /* TODO: pilot */ + cy = 0.15 * self->ratio_w; + delta_x = 0.; + delta_y = self->config.heading_zone_v_offset * self->ratio_h; + angle = drone_attitude.psi - takeoff_bearing; + int angle_deg = ((int)(angle * 180. / M_PI + 70. + 360.)) % 360; + if (angle_deg <= 140) { + model_mat << cosf(angle), -sinf(angle), 0., delta_x, + sinf(angle) * self->aspect_ratio, + cosf(angle) * self->aspect_ratio, 0., delta_y, + 0., 0., 1., 0., 0., 0., 0., 1.; + xform_mat = model_mat * view_proj; + GLCHK(glUniformMatrix4fv( + self->tex_transform_matrix_handle, + 1, + false, + xform_mat.data())); + pdraw_gles2hud_draw_icon(self, + 8, + 0., + cy, + self->config.small_icon_size, + self->ratio_w, + self->ratio_w, + color_green); + } + } + + GLCHK(glActiveTexture(GL_TEXTURE0 + self->text_texunit)); + GLCHK(glBindTexture(GL_TEXTURE_2D, self->text_texture)); + GLCHK(glUniform1i(self->tex_uniform_sampler, self->text_texunit)); + + GLCHK(glUniformMatrix4fv( + self->tex_transform_matrix_handle, 1, false, view_proj_mat)); + + char str[20]; + snprintf(str, sizeof(str), "%d%%", battery_percentage); + pdraw_gles2hud_draw_text( + self, + str, + self->config.vu_meter_zone_h_offset * self->ratio_w, + (-self->config.vu_meter_v_interval - 0.07) * self->ratio_h, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, + PDRAW_GLES2HUD_TEXT_ALIGN_TOP, + color_green); + snprintf(str, sizeof(str), "%ddBm", wifi_rssi); + pdraw_gles2hud_draw_text(self, + str, + self->config.vu_meter_zone_h_offset * + self->ratio_w, + (0.0 - 0.07) * self->ratio_h, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, + PDRAW_GLES2HUD_TEXT_ALIGN_TOP, + color_green); + snprintf(str, sizeof(str), "%d", location.sv_count); + pdraw_gles2hud_draw_text( + self, + str, + self->config.vu_meter_zone_h_offset * self->ratio_w, + (self->config.vu_meter_v_interval - 0.07) * self->ratio_h, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, + PDRAW_GLES2HUD_TEXT_ALIGN_TOP, + color_green); + snprintf(str, sizeof(str), "ALT"); + pdraw_gles2hud_draw_text(self, + str, + self->config.central_zone_size * self->ratio_w, + (self->config.central_zone_size - 0.01) * + self->ratio_h, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_LEFT, + PDRAW_GLES2HUD_TEXT_ALIGN_BOTTOM, + color_green); + snprintf(str, sizeof(str), "%.1fm", altitude); + pdraw_gles2hud_draw_text(self, + str, + (self->config.central_zone_size + 0.04) * + self->ratio_w, + 0.0 * self->ratio_h, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_LEFT, + PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, + color_green); + if (drone_model == DRONE_MODEL_DISCO) { + if ((location.valid) && + (media_info->session_meta->takeoff_loc.valid)) { + double alt_diff = 0.; + if (!std::isnan(media_info->session_meta->takeoff_loc + .altitude_wgs84ellipsoid) && + !std::isnan(location.altitude_wgs84ellipsoid)) { + alt_diff = location.altitude_wgs84ellipsoid - + media_info->session_meta->takeoff_loc + .altitude_wgs84ellipsoid; + } else if (!std::isnan( + media_info->session_meta->takeoff_loc + .altitude_egm96amsl) && + !std::isnan(location.altitude_egm96amsl)) { + alt_diff = location.altitude_egm96amsl - + media_info->session_meta->takeoff_loc + .altitude_egm96amsl; + } + snprintf(str, sizeof(str), "DELTA: %+.1fm", alt_diff); + pdraw_gles2hud_draw_text( + self, + str, + self->config.central_zone_size * self->ratio_w, + (-self->config.central_zone_size + 0.01) * + self->ratio_h, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_LEFT, + PDRAW_GLES2HUD_TEXT_ALIGN_TOP, + color_green); + } + } else { + snprintf(str, sizeof(str), "GND: %.1fm", ground_distance); + pdraw_gles2hud_draw_text( + self, + str, + self->config.central_zone_size * self->ratio_w, + (-self->config.central_zone_size + 0.01) * + self->ratio_h, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_LEFT, + PDRAW_GLES2HUD_TEXT_ALIGN_TOP, + color_green); + } + snprintf(str, sizeof(str), "SPD"); + pdraw_gles2hud_draw_text( + self, + str, + -self->config.central_zone_size * self->ratio_w, + (self->config.central_zone_size - 0.01) * self->ratio_h, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT, + PDRAW_GLES2HUD_TEXT_ALIGN_BOTTOM, + color_green); + snprintf(str, sizeof(str), "%.1fm/s", horizontal_speed); + pdraw_gles2hud_draw_text(self, + str, + -(self->config.central_zone_size + 0.04) * + self->ratio_w, + 0.0 * self->ratio_h, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT, + PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, + color_green); + if (air_speed != -1.) { + snprintf(str, sizeof(str), "AIR: %4.1fm/s", air_speed); + pdraw_gles2hud_draw_text( + self, + str, + -self->config.central_zone_size * self->ratio_w, + (-self->config.central_zone_size + 0.01) * + self->ratio_h, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT, + PDRAW_GLES2HUD_TEXT_ALIGN_TOP, + color_green); + } + if (takeoff_distance != 0.) { + /* TODO: pilot */ + snprintf(str, sizeof(str), "DIST: %.0fm", takeoff_distance); + pdraw_gles2hud_draw_text( + self, + str, + 0.0, + (-self->config.central_zone_size / 2. - 0.10) * + self->ratio_w * self->aspect_ratio, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, + PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, + color_green); + } + if ((flying_state == VMETA_FLYING_STATE_TAKINGOFF) || + (flying_state == VMETA_FLYING_STATE_LANDING) || + (flying_state == VMETA_FLYING_STATE_EMERGENCY)) { + pdraw_gles2hud_draw_text( + self, + flying_state_str[flying_state], + 0.0, + (self->config.central_zone_size / 2. + 0.10) * + self->ratio_w * self->aspect_ratio, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, + PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, + color_green); + } else if ((piloting_mode == VMETA_PILOTING_MODE_RETURN_HOME) || + (piloting_mode == VMETA_PILOTING_MODE_FLIGHT_PLAN)) { + pdraw_gles2hud_draw_text( + self, + piloting_mode_str[piloting_mode], + 0.0, + (self->config.central_zone_size / 2. + 0.10) * + self->ratio_w * self->aspect_ratio, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, + PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, + color_green); + } + if (ctrl_meta->battery_percentage < 255) { + if (ctrl_meta->battery_percentage <= 100) { + snprintf(str, + sizeof(str), + "CTRL BAT: %d%%", + ctrl_meta->battery_percentage); + } else { + snprintf(str, sizeof(str), "CTRL BAT: --%%"); + } + pdraw_gles2hud_draw_text( + self, + str, + self->config.right_zone_h_offset * self->ratio_w, + 0. * self->ratio_w * self->aspect_ratio, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT, + PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, + color_green); + } + if (media_info->playback_type == PDRAW_PLAYBACK_TYPE_LIVE) { + snprintf(str, + sizeof(str), + "CTRL LOC: %s", + (ctrl_meta->location.valid) ? "OK" : "NOK"); + pdraw_gles2hud_draw_text( + self, + str, + self->config.right_zone_h_offset * self->ratio_w, + 0.05 * self->ratio_w * self->aspect_ratio, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT, + PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, + color_green); + } + if (strlen(media_info->session_meta->friendly_name) > 0) + pdraw_gles2hud_draw_text( + self, + media_info->session_meta->friendly_name, + friendly_name_x_offset, + self->config.roll_zone_v_offset * self->ratio_h + + 0.12 * self->ratio_w * self->aspect_ratio, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_LEFT, + PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, + color_green); + if ((frame_extra->play_timestamp > 0) && + (frame_extra->play_timestamp != (uint64_t)-1) && + (media_info->duration > 0) && + (media_info->duration != (uint64_t)-1)) { + uint64_t remaining_time = + media_info->duration - frame_extra->play_timestamp; + unsigned int c_hrs = 0, c_min = 0, c_sec = 0, c_msec = 0; + unsigned int r_hrs = 0, r_min = 0, r_sec = 0, r_msec = 0; + unsigned int d_hrs = 0, d_min = 0, d_sec = 0, d_msec = 0; + pdraw_gles2hud_friendly_time_from_us( + frame_extra->play_timestamp, + &c_hrs, + &c_min, + &c_sec, + &c_msec); + pdraw_gles2hud_friendly_time_from_us( + remaining_time, &r_hrs, &r_min, &r_sec, &r_msec); + pdraw_gles2hud_friendly_time_from_us( + media_info->duration, &d_hrs, &d_min, &d_sec, &d_msec); + if (d_hrs) { + snprintf(str, + sizeof(str), + "+%02d:%02d:%02d.%03d", + c_hrs, + c_min, + c_sec, + c_msec); + } else { + snprintf(str, + sizeof(str), + "+%02d:%02d.%03d", + c_min, + c_sec, + c_msec); + } + pdraw_gles2hud_draw_text( + self, + str, + (self->config.right_zone_h_offset - 0.4) * + self->ratio_w, + self->config.roll_zone_v_offset * self->ratio_h + + 0.12 * self->ratio_w * self->aspect_ratio, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_LEFT, + PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, + color_green); + if (d_hrs) { + snprintf(str, + sizeof(str), + "-%02d:%02d:%02d.%03d", + r_hrs, + r_min, + r_sec, + r_msec); + } else { + snprintf(str, + sizeof(str), + "-%02d:%02d.%03d", + r_min, + r_sec, + r_msec); + } + pdraw_gles2hud_draw_text( + self, + str, + self->config.right_zone_h_offset * self->ratio_w, + self->config.roll_zone_v_offset * self->ratio_h + + 0.12 * self->ratio_w * self->aspect_ratio, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT, + PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, + color_green); + if (d_hrs) { + snprintf(str, + sizeof(str), + "DUR: %02d:%02d:%02d", + d_hrs, + d_min, + d_sec); + } else { + snprintf(str, + sizeof(str), + "DUR: %02d:%02d", + d_min, + d_sec); + } + pdraw_gles2hud_draw_text( + self, + str, + (self->config.right_zone_h_offset - 0.2) * + self->ratio_w, + self->config.roll_zone_v_offset * self->ratio_h + + 0.10 * self->ratio_w * self->aspect_ratio, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, + PDRAW_GLES2HUD_TEXT_ALIGN_TOP, + color_green); + } else if (media_info->playback_type == PDRAW_PLAYBACK_TYPE_LIVE) { + if (drone_meta->recording_duration > 0) { + unsigned int d_hrs = 0, d_min = 0; + unsigned int d_sec = 0, d_msec = 0; + pdraw_gles2hud_friendly_time_from_us( + drone_meta->recording_duration, + &d_hrs, + &d_min, + &d_sec, + &d_msec); + if (d_hrs) { + snprintf(str, + sizeof(str), + "REC %02d:%02d:%02d", + d_hrs, + d_min, + d_sec); + } else { + snprintf(str, + sizeof(str), + "REC %02d:%02d", + d_min, + d_sec); + } + pdraw_gles2hud_draw_text( + self, + str, + self->config.right_zone_h_offset * + self->ratio_w, + self->config.roll_zone_v_offset * + self->ratio_h + + 0.12 * self->ratio_w * + self->aspect_ratio, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT, + PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, + color_green); + } else { + pdraw_gles2hud_draw_text( + self, + "REC", + self->config.right_zone_h_offset * + self->ratio_w, + self->config.roll_zone_v_offset * + self->ratio_h + + 0.12 * self->ratio_w * + self->aspect_ratio, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT, + PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, + color_green); + } + } + snprintf(str, sizeof(str), "%03d", heading_int); + pdraw_gles2hud_draw_text(self, + str, + 0. * self->ratio_w, + (self->config.heading_zone_v_offset + 0.10) * + self->ratio_h, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, + PDRAW_GLES2HUD_TEXT_ALIGN_BOTTOM, + color_green); + + float height = self->config.central_zone_size * self->ratio_w * + self->aspect_ratio; + steps = 6; + for (i = -steps; i <= steps; i++) { + if ((i != 0) && (!(i & 1))) { + snprintf(str, sizeof(str), "%+2d ", i * 10); + pdraw_gles2hud_draw_text( + self, + str, + 0. * self->ratio_w, + i * height / 2 / steps, + self->config.text_size * self->ratio_w, + 1., + self->aspect_ratio, + PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, + PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE, + color_green); + } + } + + /* Roll text */ + cy = 0.10 * self->ratio_w; + delta_x = 0.; + delta_y = self->config.roll_zone_v_offset * self->ratio_h; + steps = 2; + model_mat << 1., 0., 0., delta_x, 0., 1., 0., delta_y, 0., 0., 1., 0., + 0., 0., 0., 1.; + for (i = -steps, angle = M_PI * (-30. * steps) / 180.; i <= steps; + i++, angle += M_PI * 30. / 180.) { + angle_deg = (i * 30 + 60 + 360) % 360; + if (angle_deg <= 120) { + model_mat(0, 0) = cosf(angle); + model_mat(1, 0) = sinf(angle) * self->aspect_ratio; + model_mat(0, 1) = -sinf(angle); + model_mat(1, 1) = cosf(angle) * self->aspect_ratio; + xform_mat = model_mat * view_proj; + GLCHK(glUniformMatrix4fv( + self->tex_transform_matrix_handle, + 1, + false, + xform_mat.data())); + if (i == 0) + snprintf(str, sizeof(str), "0"); + else + snprintf(str, sizeof(str), "%+2d ", -i * 30); + pdraw_gles2hud_draw_text( + self, + str, + 0., + cy, + self->config.text_size * self->ratio_w, + 1., + 1., + PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, + PDRAW_GLES2HUD_TEXT_ALIGN_TOP, + color_green); + } + } + + /* Heading text */ + cy = 0.10 * self->ratio_w; + delta_x = 0.; + delta_y = self->config.heading_zone_v_offset * self->ratio_h; + model_mat << 1., 0., 0., delta_x, 0., 1., 0., delta_y, 0., 0., 1., 0., + 0., 0., 0., 1.; + for (i = 0, angle = drone_attitude.psi; i < 8; + i++, angle += M_PI / 4.) { + angle_deg = (heading_int + i * 45 + 70 + 360) % 360; + if (angle_deg <= 140) { + model_mat(0, 0) = cosf(angle); + model_mat(1, 0) = sinf(angle) * self->aspect_ratio; + model_mat(0, 1) = -sinf(angle); + model_mat(1, 1) = cosf(angle) * self->aspect_ratio; + xform_mat = model_mat * view_proj; + GLCHK(glUniformMatrix4fv( + self->tex_transform_matrix_handle, + 1, + false, + xform_mat.data())); + pdraw_gles2hud_draw_text( + self, + heading_str[i], + 0., + cy, + self->config.text_size, + 1., + 1., + PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, + PDRAW_GLES2HUD_TEXT_ALIGN_TOP, + color_green); + } + } + + /* Radar text */ +#ifdef DEBUG_RADAR /* used to test the radar on records */ + if ((location.valid) && (session_meta->takeoff_loc.valid) && + (ctrl_orientation_valid)) { +#else /* DEBUG_RADAR */ + if ((location.valid) && (ctrl_meta->location.valid) && + (ctrl_orientation_valid)) { +#endif /* DEBUG_RADAR */ + cy = 0.09 * self->ratio_w; + delta_x = self->config.radar_zone_h_offset * self->ratio_w; + delta_y = self->config.radar_zone_v_offset * self->ratio_h; + model_mat << 1., 0., 0., delta_x, 0., 1., 0., delta_y, 0., 0., + 1., 0., 0., 0., 0., 1.; + for (i = 0, angle = ctrl_orientation.psi; i < 8; + i += 2, angle += M_PI / 2.) { + model_mat(0, 0) = cosf(angle); + model_mat(1, 0) = sinf(angle) * self->aspect_ratio; + model_mat(0, 1) = -sinf(angle); + model_mat(1, 1) = cosf(angle) * self->aspect_ratio; + xform_mat = model_mat * view_proj; + GLCHK(glUniformMatrix4fv( + self->tex_transform_matrix_handle, + 1, + false, + xform_mat.data())); + pdraw_gles2hud_draw_text( + self, + heading_str[i], + 0., + cy, + self->config.text_size, + 1., + 1., + PDRAW_GLES2HUD_TEXT_ALIGN_CENTER, + PDRAW_GLES2HUD_TEXT_ALIGN_BOTTOM, + color_green); + } + } + + GLCHK(glDisableVertexAttribArray(self->tex_position_handle)); + GLCHK(glDisableVertexAttribArray(self->tex_texcoord_handle)); + GLCHK(glDisableVertexAttribArray(self->tex_color_handle)); + + return 0; +} + + +static int pdraw_gles2hud_render_imaging( + struct pdraw_gles2hud *self, + const struct pdraw_rect *render_pos, + const struct pdraw_rect *content_pos, + const float view_proj_mat[16], + const struct pdraw_media_info *media_info, + struct vmeta_frame *frame_meta, + const struct pdraw_video_frame_extra *frame_extra, + const struct pdraw_gles2hud_controller_meta *ctrl_meta, + const struct pdraw_gles2hud_drone_meta *drone_meta) +{ + if (self == nullptr) + return -EINVAL; + if ((render_pos == nullptr) || (render_pos->width == 0) || + (render_pos->height == 0)) + return -EINVAL; + if ((content_pos == nullptr) || (content_pos->width == 0) || + (content_pos->height == 0)) + return -EINVAL; + if (media_info == nullptr) + return -EINVAL; + if (frame_meta == nullptr) + return -EINVAL; + if (frame_extra == nullptr) + return -EINVAL; + if (ctrl_meta == nullptr) + return -EINVAL; + if (drone_meta == nullptr) + return -EINVAL; + + /* Picture field of view */ + pdraw_gles2hud_get_fov(self, media_info->session_meta, frame_meta); + + self->ratio_w = (float)content_pos->width / render_pos->width * + self->config.scale; + self->ratio_h = (float)content_pos->height / render_pos->height * + self->config.scale; + self->aspect_ratio = (float)render_pos->width / render_pos->height; + + GLCHK(glEnable(GL_BLEND)); + GLCHK(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + GLCHK(glUseProgram(self->program)); + + GLCHK(glEnableVertexAttribArray(self->position_handle)); + GLCHK(glEnableVertexAttribArray(self->color_handle)); + + GLCHK(glUniformMatrix4fv( + self->transform_matrix_handle, 1, false, view_proj_mat)); + + pdraw_gles2hud_draw_framing_grid( + self, render_pos, content_pos, color_black_alpha); + pdraw_gles2hud_draw_histograms(self, frame_extra); + + GLCHK(glDisableVertexAttribArray(self->position_handle)); + GLCHK(glDisableVertexAttribArray(self->color_handle)); + + return 0; +} + + +int pdraw_gles2hud_render( + struct pdraw_gles2hud *self, + enum pdraw_gles2hud_type type, + const struct pdraw_rect *render_pos, + const struct pdraw_rect *content_pos, + const float view_proj_mat[16], + const struct pdraw_media_info *media_info, + struct vmeta_frame *frame_meta, + const struct pdraw_video_frame_extra *frame_extra, + const struct pdraw_gles2hud_controller_meta *ctrl_meta, + const struct pdraw_gles2hud_drone_meta *drone_meta) +{ + switch (type) { + case PDRAW_GLES2HUD_TYPE_PILOTING: + return pdraw_gles2hud_render_piloting(self, + render_pos, + content_pos, + view_proj_mat, + media_info, + frame_meta, + frame_extra, + ctrl_meta, + drone_meta); + case PDRAW_GLES2HUD_TYPE_IMAGING: + return pdraw_gles2hud_render_imaging(self, + render_pos, + content_pos, + view_proj_mat, + media_info, + frame_meta, + frame_extra, + ctrl_meta, + drone_meta); + default: + ULOGE("unsupported HUD type: %d", type); + return -ENOSYS; + } +} diff --git a/libpdraw-gles2hud/src/pdraw_gles2hud_icons.cpp b/libpdraw-gles2hud/src/pdraw_gles2hud_icons.cpp index a4213aa..4eb9aa0 100644 --- a/libpdraw-gles2hud/src/pdraw_gles2hud_icons.cpp +++ b/libpdraw-gles2hud/src/pdraw_gles2hud_icons.cpp @@ -1,76 +1,76 @@ -/** - * Parrot Drones Awesome Video Viewer - * OpenGL ES 2.0 HUD rendering library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "pdraw_gles2hud_priv.h" - - -void pdraw_gles2hud_draw_icon(struct pdraw_gles2hud *self, - int index, - float x, - float y, - float size, - float scalew, - float scaleh, - const float color[4]) -{ - float vertices[8]; - float texcoords[8]; - - vertices[0] = x - size * scalew / 2.; - vertices[1] = y - size * scaleh / 2.; - vertices[2] = x + size * scalew / 2.; - vertices[3] = y - size * scaleh / 2.; - vertices[4] = x - size * scalew / 2.; - vertices[5] = y + size * scaleh / 2.; - vertices[6] = x + size * scalew / 2.; - vertices[7] = y + size * scaleh / 2.; - - GLCHK(glVertexAttribPointer( - self->tex_position_handle, 2, GL_FLOAT, false, 0, vertices)); - - int ix = index % 3; - int iy = index / 3; - - texcoords[0] = ((float)ix + 0.) / 3.; - texcoords[1] = ((float)iy + 0.99) / 3.; - texcoords[2] = ((float)ix + 0.99) / 3.; - texcoords[3] = ((float)iy + 0.99) / 3.; - texcoords[4] = ((float)ix + 0.) / 3.; - texcoords[5] = ((float)iy + 0.) / 3.; - texcoords[6] = ((float)ix + 0.99) / 3.; - texcoords[7] = ((float)iy + 0.) / 3.; - - GLCHK(glVertexAttribPointer( - self->tex_texcoord_handle, 2, GL_FLOAT, false, 0, texcoords)); - - GLCHK(glUniform4fv(self->tex_color_handle, 1, color)); - - GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); -} +/** + * Parrot Drones Awesome Video Viewer + * OpenGL ES 2.0 HUD rendering library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 "pdraw_gles2hud_priv.h" + + +void pdraw_gles2hud_draw_icon(struct pdraw_gles2hud *self, + int index, + float x, + float y, + float size, + float scalew, + float scaleh, + const float color[4]) +{ + float vertices[8]; + float texcoords[8]; + + vertices[0] = x - size * scalew / 2.; + vertices[1] = y - size * scaleh / 2.; + vertices[2] = x + size * scalew / 2.; + vertices[3] = y - size * scaleh / 2.; + vertices[4] = x - size * scalew / 2.; + vertices[5] = y + size * scaleh / 2.; + vertices[6] = x + size * scalew / 2.; + vertices[7] = y + size * scaleh / 2.; + + GLCHK(glVertexAttribPointer( + self->tex_position_handle, 2, GL_FLOAT, false, 0, vertices)); + + int ix = index % 3; + int iy = index / 3; + + texcoords[0] = ((float)ix + 0.) / 3.; + texcoords[1] = ((float)iy + 0.99) / 3.; + texcoords[2] = ((float)ix + 0.99) / 3.; + texcoords[3] = ((float)iy + 0.99) / 3.; + texcoords[4] = ((float)ix + 0.) / 3.; + texcoords[5] = ((float)iy + 0.) / 3.; + texcoords[6] = ((float)ix + 0.99) / 3.; + texcoords[7] = ((float)iy + 0.) / 3.; + + GLCHK(glVertexAttribPointer( + self->tex_texcoord_handle, 2, GL_FLOAT, false, 0, texcoords)); + + GLCHK(glUniform4fv(self->tex_color_handle, 1, color)); + + GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); +} diff --git a/libpdraw-gles2hud/src/pdraw_gles2hud_icons_data.cpp b/libpdraw-gles2hud/src/pdraw_gles2hud_icons_data.cpp index 996391c..f3aa05c 100644 --- a/libpdraw-gles2hud/src/pdraw_gles2hud_icons_data.cpp +++ b/libpdraw-gles2hud/src/pdraw_gles2hud_icons_data.cpp @@ -1,3218 +1,3218 @@ -/** - * Parrot Drones Awesome Video Viewer - * OpenGL ES 2.0 HUD rendering library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "pdraw_gles2hud_priv.h" - - -const int hud_icons_width = 212; -const int hud_icons_height = 210; - -const uint8_t hud_icons[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 29, 117, 184, 229, 251, 253, 237, 197, 135, 50, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 50, 136, 197, 237, 253, 251, 229, 184, 117, 29, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 27, 162, 251, 255, 254, 227, 199, 195, 217, 251, 255, 255, - 194, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 55, 194, 255, 255, 251, 217, 195, 198, 227, 254, - 255, 251, 163, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 85, 241, 255, 214, 106, 25, 0, 0, 0, 0, 13, - 84, 186, 255, 253, 133, 2, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 133, 253, 255, 186, 84, 13, 0, 0, 0, - 0, 25, 106, 214, 255, 241, 85, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 115, 234, 245, 152, 11, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 11, 152, 245, 234, 116, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 253, 245, 108, 2, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 68, 225, 255, 168, 3, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3, 168, 255, 225, 68, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2, 108, 245, 253, 111, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 81, 251, 255, 255, 254, 130, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 130, 254, 255, 255, 251, 81, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 86, 254, 232, 50, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 20, 200, 255, 148, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 148, 255, 200, 20, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 232, 254, 86, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2, 206, 254, 255, 255, 255, 189, 8, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 8, 189, 255, 255, 255, 254, - 205, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 111, 233, 233, 111, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 29, 241, 245, 49, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 216, 255, 79, - 0, 0, 0, 0, 0, 0, 0, 0, 79, 255, 216, 17, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 245, - 241, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 74, 252, 255, 255, 255, 255, 158, - 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 158, 255, 255, - 255, 255, 251, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 175, 255, 255, 255, 255, 175, 9, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 166, 255, 106, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, - 251, 223, 5, 0, 0, 0, 0, 0, 0, 5, 223, 251, 48, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 106, 255, 166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 254, 255, 255, 255, - 250, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, - 250, 255, 255, 255, 254, 180, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 5, 184, 255, 255, 255, 255, 255, 255, 184, 5, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 252, 213, 2, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 153, 255, 92, 0, 0, 0, 0, 0, 0, 92, 255, 153, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 2, 213, 252, 32, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 249, 255, 255, - 255, 255, 178, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 5, 177, 255, 255, 255, 255, 249, 34, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 151, 255, 255, 255, 255, 255, 255, 255, 255, - 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 255, 105, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 42, 255, 183, 0, 0, 0, 0, 0, 0, 183, - 255, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 105, 255, 120, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 254, - 255, 255, 255, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 73, 255, 255, 255, 255, 254, 120, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 84, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 187, - 254, 25, 0, 0, 0, 0, 0, 0, 0, 37, 134, 143, 67, 0, - 0, 0, 0, 0, 0, 0, 0, 217, 245, 4, 0, 0, 0, 0, - 4, 245, 217, 0, 0, 0, 0, 0, 0, 0, 0, 67, 143, 134, - 37, 0, 0, 0, 0, 0, 0, 0, 25, 254, 187, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 206, 255, 255, 255, 255, 214, 11, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 12, 214, 255, 255, 255, 255, 205, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 61, 246, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 246, 61, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 231, 227, 0, 0, 0, 0, 0, 0, 0, 33, 242, 255, 255, - 254, 104, 0, 0, 0, 0, 0, 0, 0, 165, 255, 38, 0, 0, - 0, 0, 38, 255, 164, 0, 0, 0, 0, 0, 0, 0, 104, 254, - 255, 255, 242, 33, 0, 0, 0, 0, 0, 0, 0, 227, 231, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 26, 251, 255, 255, 255, 255, 134, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 134, 255, 255, 255, 255, - 251, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 84, 247, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 247, 84, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 251, 201, 0, 0, 0, 0, 0, 0, 0, 123, 255, - 255, 255, 255, 254, 104, 0, 0, 0, 0, 0, 0, 138, 255, 59, - 0, 0, 0, 0, 59, 255, 138, 0, 0, 0, 0, 0, 0, 104, - 254, 255, 255, 255, 255, 123, 0, 0, 0, 0, 0, 0, 0, 201, - 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 93, 254, 255, 255, 255, 255, 54, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 255, 255, - 255, 255, 254, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 253, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 112, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 252, 199, 0, 0, 0, 0, 0, 0, 0, - 125, 255, 255, 255, 255, 255, 254, 111, 0, 0, 0, 0, 0, 79, - 219, 30, 0, 0, 0, 0, 30, 219, 79, 0, 0, 0, 0, 0, - 111, 254, 255, 255, 255, 255, 255, 125, 0, 0, 0, 0, 0, 0, - 0, 199, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 149, 254, 255, 255, 255, 225, 11, 0, 0, - 0, 0, 0, 9, 112, 164, 94, 5, 0, 0, 0, 0, 0, 0, - 0, 0, 33, 82, 108, 118, 108, 82, 33, 0, 0, 0, 0, 0, - 0, 0, 0, 5, 94, 164, 112, 9, 0, 0, 0, 0, 0, 11, - 226, 255, 255, 255, 254, 149, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 142, 255, 201, 200, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 200, 201, - 255, 142, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 234, 224, 0, 0, 0, 0, 0, - 0, 0, 43, 250, 255, 255, 255, 255, 255, 254, 112, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 112, 254, 255, 255, 255, 255, 255, 250, 43, 0, 0, 0, 0, - 0, 0, 0, 224, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 194, 255, 255, 255, 255, 173, 4, - 0, 0, 0, 0, 0, 114, 250, 255, 248, 80, 0, 0, 0, 0, - 0, 0, 16, 147, 233, 249, 254, 255, 254, 249, 233, 147, 16, 0, - 0, 0, 0, 0, 0, 80, 248, 255, 250, 114, 0, 0, 0, 0, - 0, 4, 176, 255, 255, 255, 255, 194, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 170, 255, 178, - 10, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 191, 10, 178, 255, 170, 7, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 254, 20, 0, 0, - 0, 0, 0, 0, 0, 69, 250, 255, 255, 255, 255, 255, 254, 112, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 112, 254, 255, 255, 255, 255, 255, 250, 69, 0, 0, 0, - 0, 0, 0, 0, 20, 254, 192, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 227, 255, 255, 255, 255, - 132, 0, 0, 0, 0, 0, 0, 166, 255, 255, 254, 130, 0, 0, - 0, 0, 0, 0, 71, 251, 255, 255, 255, 255, 255, 255, 255, 251, - 71, 0, 0, 0, 0, 0, 0, 130, 254, 255, 255, 166, 0, 0, - 0, 0, 0, 0, 133, 255, 255, 255, 255, 227, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 194, 255, - 151, 3, 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 191, 0, 3, 151, 255, 194, 17, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 96, - 0, 0, 0, 0, 0, 0, 0, 0, 69, 250, 255, 255, 255, 255, - 255, 254, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 112, 254, 255, 255, 255, 255, 255, 250, 69, 0, 0, - 0, 0, 0, 0, 0, 0, 96, 255, 127, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 255, - 255, 255, 104, 0, 0, 0, 0, 0, 0, 93, 247, 254, 240, 63, - 0, 0, 0, 0, 0, 0, 86, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 85, 0, 0, 0, 0, 0, 0, 63, 240, 254, 247, 93, - 0, 0, 0, 0, 0, 0, 103, 255, 255, 255, 255, 248, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 214, - 254, 120, 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 191, 0, 0, 0, 120, 254, 214, 31, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, - 254, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 250, 255, - 255, 255, 255, 255, 254, 112, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 112, 254, 255, 255, 255, 255, 255, 250, 69, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 203, 254, 39, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, - 255, 255, 255, 255, 94, 0, 0, 0, 0, 0, 0, 5, 74, 123, - 60, 2, 0, 0, 0, 0, 0, 0, 100, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 99, 0, 0, 0, 0, 0, 0, 2, 60, 123, - 74, 5, 0, 0, 0, 0, 0, 0, 94, 255, 255, 255, 255, 254, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, - 230, 249, 92, 0, 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, 0, 92, 249, 230, - 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 178, 255, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 66, 249, 255, 255, 255, 255, 255, 254, 120, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 120, 254, 255, 255, 255, 255, 255, 249, 66, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 255, 178, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 246, 255, 255, 255, 255, 101, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 111, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 255, 255, 255, - 255, 246, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 71, 242, 240, 67, 0, 0, 0, 0, 0, 191, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, - 67, 240, 242, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 39, 247, 238, 36, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 64, 249, 255, 255, 255, 255, 255, 255, 121, 0, 0, - 0, 0, 0, 0, 0, 0, 121, 255, 255, 255, 255, 255, 255, 249, - 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 238, 247, 38, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 232, 255, 255, 255, 255, 121, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 119, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 255, - 255, 255, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 97, 250, 227, 46, 0, 0, 0, 0, 0, 0, 191, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, - 0, 0, 0, 46, 227, 250, 97, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 104, 255, 222, 35, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 64, 249, 255, 255, 255, 255, 255, 255, - 121, 0, 0, 0, 0, 0, 0, 121, 255, 255, 255, 255, 255, 255, - 249, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 222, 255, - 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 204, 255, 255, 255, 255, 151, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 128, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 152, 255, 255, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 126, 254, 211, 28, 0, 0, 0, 0, 0, 0, 0, 191, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, - 0, 0, 0, 0, 0, 0, 28, 211, 254, 126, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, 255, 236, 84, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 249, 255, 255, 255, - 255, 255, 243, 0, 0, 0, 0, 0, 0, 243, 255, 255, 255, 255, - 255, 249, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 236, - 255, 133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 171, 255, 255, 255, 255, 192, 6, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 136, 255, 255, 255, 255, 255, 255, 255, 255, 255, 136, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 6, 192, 255, 255, 255, 255, 171, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 4, 156, 255, 190, 15, 0, 0, 0, 0, 0, 0, 0, - 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 191, 0, 0, 0, 0, 0, 0, 0, 0, 15, 190, 255, 156, 4, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, - 248, 255, 193, 82, 9, 0, 0, 0, 0, 0, 0, 0, 64, 249, - 255, 255, 255, 255, 243, 0, 0, 0, 0, 0, 0, 243, 255, 255, - 255, 255, 249, 64, 0, 0, 0, 0, 0, 0, 0, 9, 83, 193, - 255, 248, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 254, 255, 255, 255, - 237, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 139, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 14, 237, 255, 255, 255, 254, 128, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 12, 182, 255, 165, 6, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 165, - 255, 182, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 43, 186, 255, 255, 248, 203, 175, 124, 0, 0, 0, 0, - 0, 129, 255, 255, 255, 255, 243, 0, 0, 0, 0, 0, 0, 243, - 255, 255, 255, 255, 129, 0, 0, 0, 0, 0, 125, 175, 203, 248, - 255, 255, 186, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, 254, 255, - 255, 255, 255, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 145, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 56, 255, 255, 255, 255, 254, 79, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 24, 204, 255, 136, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 136, 255, 204, 24, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 49, 141, 208, 249, 255, 228, 0, 0, - 0, 0, 0, 125, 255, 255, 255, 255, 243, 0, 0, 0, 0, 0, - 0, 243, 255, 255, 255, 255, 125, 0, 0, 0, 0, 0, 228, 255, - 249, 208, 140, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, - 251, 255, 255, 255, 255, 116, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 255, 255, 251, 25, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 39, 222, 252, 106, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 106, 252, 222, 39, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 19, 9, - 0, 0, 0, 0, 0, 125, 255, 255, 255, 255, 243, 0, 0, 0, - 0, 0, 0, 243, 255, 255, 255, 255, 125, 0, 0, 0, 0, 0, - 9, 19, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 217, 255, 255, 255, 255, 169, 3, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 3, 169, 255, 255, 255, 255, - 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 59, 236, 245, 79, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 79, 245, 236, 59, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 125, 255, 255, 255, 255, 243, 0, - 0, 0, 0, 0, 0, 243, 255, 255, 255, 255, 125, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 157, 254, 255, 255, 255, 213, 9, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 148, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 213, 255, 255, - 255, 254, 157, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 83, 246, 234, 56, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 234, 246, - 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 255, 255, 255, 255, - 243, 0, 0, 0, 0, 0, 0, 243, 255, 255, 255, 255, 125, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 101, 254, 255, 255, 255, 246, 16, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 147, 255, 255, 255, 255, 255, 255, 255, 255, 255, 146, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 246, - 255, 255, 255, 254, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 111, 252, 220, 37, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 37, 220, 252, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 255, 255, - 255, 255, 243, 0, 0, 0, 0, 0, 0, 243, 255, 255, 255, 255, - 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 64, 252, 255, 255, 255, 255, - 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 141, 255, 255, 255, 255, 255, 255, 255, 255, 255, 141, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 33, 255, 255, 255, 255, 252, 63, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 141, 255, 201, 21, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 21, 201, 255, 141, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, - 255, 255, 255, 255, 243, 0, 0, 0, 0, 0, 0, 243, 255, 255, - 255, 255, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 252, 255, 255, - 255, 255, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 139, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 45, 255, 255, 255, 255, 252, 52, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 169, 255, 178, 10, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 10, 178, 255, 169, 7, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 3, 19, 9, 0, 0, 0, 0, - 0, 125, 255, 255, 255, 255, 243, 0, 0, 0, 0, 0, 0, 243, - 255, 255, 255, 255, 125, 0, 0, 0, 0, 0, 116, 150, 128, 84, - 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 253, - 255, 255, 255, 255, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 134, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 34, 255, 255, 255, 255, 253, 69, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 193, 255, 151, - 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 151, 255, 193, 17, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 49, 141, 208, 249, 255, 228, 0, 0, - 0, 0, 0, 125, 255, 255, 255, 255, 243, 0, 0, 0, 0, 0, - 0, 243, 255, 255, 255, 255, 125, 0, 0, 0, 0, 0, 231, 255, - 255, 255, 251, 180, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 114, 254, 255, 255, 255, 245, 16, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 124, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 16, 245, 255, 255, 255, 254, 113, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 213, 254, - 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, - 254, 213, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 43, 186, 255, 255, 248, 203, 175, 125, - 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 243, 0, 0, 0, - 0, 0, 0, 243, 255, 255, 255, 255, 127, 0, 0, 0, 0, 0, - 15, 44, 72, 125, 206, 255, 255, 168, 17, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 175, 255, 255, 255, 255, 207, 9, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 114, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 9, 207, 255, 255, 255, 255, - 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 229, - 249, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 92, 249, 229, 48, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 107, 248, 255, 193, 82, 9, 0, - 0, 0, 0, 0, 0, 0, 58, 247, 255, 255, 255, 255, 243, 0, - 0, 0, 0, 0, 0, 243, 255, 255, 255, 255, 247, 58, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 62, 204, 255, 221, 36, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 4, 235, 255, 255, 255, 255, 154, 2, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 102, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 154, 255, 255, - 255, 255, 235, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, - 242, 240, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 67, 240, 242, 70, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 133, 255, 236, 84, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 64, 247, 255, 255, 255, 255, 255, - 243, 0, 0, 0, 0, 0, 0, 243, 255, 255, 255, 255, 255, 247, - 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 140, 255, 227, - 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 46, 253, 255, 255, 255, 255, 90, 0, 0, - 0, 0, 0, 0, 0, 12, 57, 51, 7, 0, 0, 0, 0, 0, - 91, 255, 255, 255, 255, 255, 255, 255, 255, 255, 90, 0, 0, 0, - 0, 0, 7, 51, 57, 13, 0, 0, 0, 0, 0, 0, 0, 90, - 255, 255, 255, 255, 253, 46, 0, 0, 0, 0, 0, 0, 0, 0, - 96, 250, 227, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 46, 228, 250, 96, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 222, 35, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 64, 249, 255, 255, 255, 255, - 255, 255, 121, 0, 0, 0, 0, 0, 0, 121, 255, 255, 255, 255, - 255, 255, 249, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 127, 255, 199, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 93, 254, 255, 255, 255, 252, 30, - 0, 0, 0, 0, 0, 0, 5, 162, 251, 246, 126, 3, 0, 0, - 0, 0, 76, 254, 255, 255, 255, 255, 255, 255, 255, 254, 75, 0, - 0, 0, 0, 3, 126, 246, 251, 162, 5, 0, 0, 0, 0, 0, - 0, 30, 252, 255, 255, 255, 254, 93, 0, 0, 0, 0, 0, 0, - 0, 91, 254, 211, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 212, 254, - 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 247, 238, 36, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 249, 255, 255, 255, - 255, 255, 255, 121, 0, 0, 0, 0, 0, 0, 0, 0, 121, 255, - 255, 255, 255, 255, 255, 249, 64, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 174, 255, 110, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 254, 255, 255, 255, - 225, 10, 0, 0, 0, 0, 0, 0, 44, 237, 255, 255, 216, 23, - 0, 0, 0, 0, 65, 248, 255, 255, 255, 255, 255, 255, 255, 248, - 65, 0, 0, 0, 0, 23, 217, 255, 255, 237, 44, 0, 0, 0, - 0, 0, 0, 10, 225, 255, 255, 255, 254, 125, 0, 0, 0, 0, - 0, 0, 0, 219, 220, 16, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 16, 220, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 178, 255, - 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 249, 255, 255, - 255, 255, 255, 255, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 121, 255, 255, 255, 255, 255, 255, 249, 64, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 25, 243, 233, 8, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 255, 255, - 255, 255, 202, 7, 0, 0, 0, 0, 0, 0, 26, 223, 254, 254, - 193, 15, 0, 0, 0, 0, 46, 225, 254, 255, 255, 255, 255, 255, - 254, 225, 46, 0, 0, 0, 0, 15, 193, 254, 254, 223, 26, 0, - 0, 0, 0, 0, 0, 7, 203, 255, 255, 255, 255, 141, 0, 0, - 0, 0, 0, 0, 0, 254, 153, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 15, 116, 240, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 240, 116, 15, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 153, 254, 0, 0, 0, 0, 0, 0, 0, 0, 39, - 254, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 249, 255, - 255, 255, 255, 255, 255, 121, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 121, 255, 255, 255, 255, 255, 255, 249, 64, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 145, 255, 87, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, - 255, 255, 255, 255, 198, 6, 0, 0, 0, 0, 0, 0, 0, 77, - 192, 181, 54, 0, 0, 0, 0, 0, 1, 51, 114, 152, 170, 176, - 170, 152, 114, 51, 1, 0, 0, 0, 0, 0, 54, 181, 192, 77, - 0, 0, 0, 0, 0, 0, 0, 6, 198, 255, 255, 255, 255, 146, - 0, 0, 0, 0, 0, 0, 0, 255, 151, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 33, 143, 242, 255, 232, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 232, 255, 242, 143, 33, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 151, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 127, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 69, 249, - 255, 255, 255, 255, 255, 254, 116, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 116, 254, 255, 255, 255, 255, 255, - 249, 69, 0, 0, 0, 0, 0, 0, 0, 0, 52, 255, 163, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 134, 254, 255, 255, 255, 213, 8, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 213, 255, 255, 255, - 254, 134, 0, 0, 0, 0, 0, 0, 0, 255, 151, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 57, 171, 252, 255, 192, 78, 2, 159, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 159, 2, 78, 192, 255, 252, 171, - 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 151, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 192, 254, 20, 0, 0, 0, 0, 0, 0, 0, 69, - 250, 255, 255, 255, 255, 255, 254, 112, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 254, 255, 255, - 255, 255, 255, 250, 69, 0, 0, 0, 0, 0, 0, 0, 2, 242, - 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 106, 254, 255, 255, 255, 246, 22, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 246, 255, - 255, 255, 254, 106, 0, 0, 0, 0, 0, 0, 0, 255, 151, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 3, 85, 198, 255, 250, 164, 50, 0, 0, 0, 1, 121, - 248, 255, 255, 255, 255, 255, 255, 248, 122, 1, 0, 0, 0, 50, - 164, 250, 255, 198, 85, 3, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 234, 224, 0, 0, 0, 0, 0, 0, 0, - 43, 249, 255, 255, 255, 255, 255, 254, 112, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3, 95, 21, 0, 0, 0, 0, 112, - 254, 255, 255, 255, 255, 255, 249, 43, 0, 0, 0, 0, 0, 0, - 0, 208, 247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 58, 253, 255, 255, 255, 255, 101, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, - 255, 255, 255, 255, 253, 58, 0, 0, 0, 0, 0, 0, 0, 255, - 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 13, 113, 222, 255, 238, 136, 28, 0, 0, 0, 0, 0, - 0, 0, 36, 163, 250, 255, 255, 250, 163, 36, 0, 0, 0, 0, - 0, 0, 0, 28, 136, 238, 255, 222, 113, 13, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 252, 199, 0, 0, 0, 0, 0, - 0, 0, 125, 255, 255, 255, 255, 255, 254, 112, 0, 0, 0, 0, - 0, 79, 219, 30, 0, 0, 0, 0, 59, 255, 128, 0, 0, 0, - 0, 0, 112, 254, 255, 255, 255, 255, 255, 125, 0, 0, 0, 0, - 0, 0, 0, 197, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 7, 235, 255, 255, 255, 255, 211, - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 16, 211, 255, 255, 255, 255, 234, 7, 0, 0, 0, 0, 0, 0, - 0, 255, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 30, 140, 240, 255, 220, 109, 12, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 26, 119, 119, 26, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 12, 109, 220, 255, 240, 140, 30, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 251, 201, 0, 0, 0, - 0, 0, 0, 0, 124, 255, 255, 255, 255, 254, 112, 0, 0, 0, - 0, 0, 0, 138, 255, 59, 0, 0, 0, 0, 51, 255, 148, 0, - 0, 0, 0, 0, 0, 112, 254, 255, 255, 255, 255, 124, 0, 0, - 0, 0, 0, 0, 0, 211, 244, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 254, 255, 255, - 255, 255, 138, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 5, 138, 255, 255, 255, 255, 254, 149, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 54, 167, 251, 255, 195, 81, 2, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 81, 195, - 255, 251, 167, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 151, 255, 0, 0, 0, 0, 0, 0, 0, 0, 231, 227, 0, - 0, 0, 0, 0, 0, 0, 35, 243, 255, 255, 254, 107, 0, 0, - 0, 0, 0, 0, 0, 164, 255, 38, 0, 0, 0, 0, 18, 255, - 188, 0, 0, 0, 0, 0, 0, 0, 107, 254, 255, 255, 243, 35, - 0, 0, 0, 0, 0, 0, 5, 246, 211, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 246, - 255, 255, 255, 255, 251, 109, 5, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 5, 109, 251, 255, 255, 255, 255, 246, 41, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 151, 0, 0, 0, 0, 0, 0, 0, - 0, 2, 81, 195, 255, 251, 167, 54, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 54, 167, 251, 255, 195, 81, 2, 0, 0, 0, 0, 0, - 0, 0, 0, 151, 255, 0, 0, 0, 0, 0, 0, 0, 0, 187, - 254, 25, 0, 0, 0, 0, 0, 0, 0, 39, 137, 147, 69, 0, - 0, 0, 0, 0, 0, 0, 0, 217, 245, 4, 0, 0, 0, 0, - 0, 218, 245, 10, 0, 0, 0, 0, 0, 0, 0, 69, 147, 137, - 39, 0, 0, 0, 0, 0, 0, 0, 62, 255, 155, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 147, 254, 255, 255, 255, 255, 250, 134, 15, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 15, 134, 250, 255, 255, 255, 255, 254, 147, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 151, 0, 0, 0, 0, 0, - 0, 11, 109, 220, 255, 240, 140, 30, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 6, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 6, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 30, 140, 240, 255, 220, 109, 11, 0, - 0, 0, 0, 0, 0, 151, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 120, 255, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 42, 255, 183, 0, 0, 0, - 0, 0, 0, 139, 255, 95, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 158, 255, 76, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 17, 215, 254, 255, 255, 255, 255, 255, 204, 82, 13, - 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 13, 82, 204, 255, 255, 255, 255, 255, 254, 214, 17, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 151, 0, 0, 0, - 0, 28, 136, 238, 255, 222, 113, 13, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 100, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 100, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 113, 222, 255, - 238, 136, 28, 0, 0, 0, 0, 151, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 32, 252, 213, 2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 255, 92, 0, - 0, 0, 0, 0, 0, 37, 252, 217, 6, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 249, 222, - 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 49, 235, 254, 255, 255, 255, 255, 255, - 255, 220, 156, 110, 85, 83, 90, 46, 3, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 46, - 90, 83, 85, 110, 156, 220, 255, 255, 255, 255, 255, 255, 254, 235, - 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 185, 0, - 0, 50, 164, 250, 255, 198, 85, 3, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 141, 143, 143, - 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 141, 40, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 3, 85, 198, 255, 250, 164, 50, 0, 0, 185, 240, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 166, 255, 106, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 251, 223, - 5, 0, 0, 0, 0, 0, 0, 0, 155, 255, 134, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 193, - 255, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 236, 254, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 249, 120, 3, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, - 120, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, - 235, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, - 255, 179, 193, 255, 252, 171, 57, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 57, 171, 252, 255, 193, 179, 255, 148, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 29, 241, 245, 49, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 216, - 255, 79, 0, 0, 0, 0, 0, 0, 0, 0, 18, 225, 253, 95, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 155, 255, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 215, - 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 236, 35, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 35, 236, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 254, 215, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 9, 156, 246, 235, 143, 33, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 143, 235, 246, 156, - 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 254, 232, - 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, - 200, 255, 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, - 241, 253, 121, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 13, 169, 255, 211, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 16, 148, 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 253, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 49, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 254, 247, 148, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 111, 253, 245, 108, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 68, 225, 255, 168, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 53, 232, 255, 197, 62, 0, 0, 0, 0, 0, 0, 0, - 4, 93, 226, 255, 201, 21, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 45, 159, 243, 254, 254, 255, 255, 255, 255, - 255, 254, 208, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 20, 208, 254, 255, 255, 255, 255, 255, 254, - 254, 243, 159, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 85, 241, 255, 214, 106, 25, 0, 0, 0, 0, 13, - 84, 186, 255, 253, 133, 2, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 25, 177, 255, 255, 214, 141, 94, 72, 75, - 103, 156, 233, 255, 250, 137, 7, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 89, 152, 200, 234, - 250, 253, 242, 188, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 47, 188, 242, 253, 250, 234, - 200, 152, 88, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 27, 163, 251, 255, 254, 227, 198, 195, - 217, 251, 255, 255, 194, 55, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 174, 248, 255, 255, - 255, 255, 255, 255, 237, 149, 36, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 117, 184, 229, - 251, 253, 237, 197, 136, 50, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, - 66, 105, 123, 120, 97, 53, 3, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 21, 177, 252, 255, 255, 230, 193, - 155, 100, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 255, 255, 255, 255, - 255, 255, 255, 255, 253, 214, 125, 33, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 248, 164, 51, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 249, - 149, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 19, 153, 242, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 230, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 22, 51, 92, 147, 211, 255, 255, - 255, 255, 255, 255, 255, 255, 250, 142, 3, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 47, 143, 238, 255, 255, 255, 255, 255, 255, 253, 167, 10, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 19, 140, 246, 255, 255, 255, 255, 255, 254, 180, - 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 46, 200, 255, 255, 255, 255, - 255, 253, 158, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 40, - 56, 31, 21, 0, 0, 0, 0, 0, 0, 0, 0, 13, 147, 255, - 255, 255, 255, 255, 252, 117, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, - 210, 255, 255, 255, 255, 240, 196, 139, 63, 2, 0, 0, 0, 0, - 5, 128, 255, 255, 255, 255, 255, 245, 58, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 6, 54, 102, 146, 180, 209, 229, 244, 251, 250, 244, 229, 208, 179, - 145, 101, 53, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 163, 255, 255, 255, 255, 255, 255, 255, 255, 255, 213, 91, 2, - 0, 0, 0, 4, 130, 255, 255, 255, 255, 255, 210, 7, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, - 117, 190, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 245, 188, 115, 38, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 70, - 104, 126, 107, 83, 37, 2, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 253, 183, 29, 0, 0, 0, 6, 167, 255, 255, 255, 255, 253, 120, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 239, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 239, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 115, - 209, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, 112, 19, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 144, - 242, 255, 255, 255, 255, 255, 255, 233, 165, 78, 6, 0, 0, 0, - 0, 0, 0, 0, 0, 116, 252, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 220, 41, 0, 0, 0, 21, 217, 255, 255, 255, - 255, 232, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 239, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 239, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 160, - 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 247, 157, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, - 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 130, - 22, 0, 0, 0, 0, 0, 0, 11, 119, 204, 223, 229, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 207, 21, 0, 0, 0, 74, 255, - 255, 255, 255, 254, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 82, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 239, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 82, 3, 3, 3, 3, 3, - 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 173, - 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 253, 169, 40, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 14, 204, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 240, 131, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 6, 48, 111, 210, 255, 255, 255, 255, 255, 254, 151, 0, 0, 0, - 5, 175, 255, 255, 255, 255, 222, 3, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 182, 182, 182, 182, 182, 182, - 205, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 250, 182, - 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 250, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 205, 182, 182, 182, - 182, 182, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 153, - 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 149, 18, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 169, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 227, 91, 5, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 5, 96, 238, 255, 255, 255, 255, 240, 50, - 0, 0, 0, 64, 255, 255, 255, 255, 253, 69, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, - 237, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 247, 239, 239, 247, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 234, - 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 89, 252, 255, 255, 255, 255, 255, 221, 113, 59, 53, - 79, 127, 205, 252, 255, 255, 255, 255, 255, 255, 152, 3, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 246, 255, 255, 255, - 255, 144, 0, 0, 0, 8, 198, 255, 255, 255, 255, 152, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, - 183, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 246, 193, 139, 95, 58, 28, 5, 0, 0, 0, 0, 6, 29, 59, - 97, 141, 194, 247, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 179, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 15, 228, 255, 255, 255, 255, 255, 225, 28, 0, - 0, 0, 0, 0, 0, 45, 154, 246, 255, 255, 252, 180, 36, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 149, 255, - 255, 255, 255, 222, 6, 0, 0, 0, 114, 255, 255, 255, 255, 220, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 251, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, - 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, - 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, - 237, 237, 237, 237, 237, 251, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 72, 234, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 228, - 144, 66, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 8, 69, 147, 230, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 231, 67, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 122, 254, 255, 255, 255, 255, 255, 109, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 114, 111, 41, 0, - 0, 0, 0, 3, 49, 89, 68, 17, 0, 0, 0, 0, 0, 0, - 45, 246, 255, 255, 255, 243, 57, 0, 0, 0, 37, 255, 255, 255, - 255, 253, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 212, 25, 25, 25, 25, 25, 25, 25, 25, - 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, - 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, - 25, 25, 25, 25, 25, 25, 25, 205, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 126, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 173, - 69, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 73, 177, 253, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 119, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 7, 234, 255, 255, 255, 255, 255, - 255, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 60, 211, 255, 255, 255, 244, 135, 9, 0, 0, - 0, 0, 15, 200, 255, 255, 255, 252, 93, 0, 0, 0, 9, 219, - 255, 255, 255, 254, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 160, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 155, - 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 38, 162, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 155, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 254, 255, 255, 255, - 255, 255, 255, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 62, 235, 255, 255, 255, 255, 255, 255, 160, - 4, 0, 0, 0, 1, 162, 255, 255, 255, 255, 130, 0, 0, 0, - 5, 183, 255, 255, 255, 254, 110, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 87, 253, 255, 255, 255, 255, 255, 255, 255, 179, - 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 48, 186, 255, 255, 255, 255, 255, 255, 255, - 254, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 187, 255, 255, - 255, 255, 255, 255, 255, 73, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 10, 197, 255, 255, 255, 255, 255, 255, - 255, 247, 78, 0, 0, 0, 0, 131, 255, 255, 255, 255, 159, 0, - 0, 0, 0, 147, 255, 255, 255, 255, 126, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 113, 255, 255, 255, 255, 255, 229, - 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 232, 255, 255, 255, - 255, 255, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 252, - 255, 255, 255, 255, 255, 255, 255, 133, 0, 0, 0, 0, 0, 0, - 0, 10, 63, 95, 123, 152, 180, 208, 235, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 155, 0, 0, 0, 0, 98, 255, 255, 255, 255, - 167, 0, 0, 0, 0, 140, 255, 255, 255, 255, 158, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 255, 255, 255, - 174, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 17, 51, 65, 78, 81, 71, 52, 22, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 179, - 255, 255, 255, 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 78, 254, 255, 255, 255, 255, 255, 255, 255, 198, 11, 0, 0, 0, - 0, 0, 35, 219, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 183, 0, 0, 0, 0, 98, 255, 255, - 255, 255, 167, 0, 0, 0, 0, 112, 255, 255, 255, 255, 158, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 167, - 252, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 59, 130, 189, 235, 255, 255, 255, 255, 255, 255, 255, 255, 237, - 187, 128, 58, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 121, 253, 173, 2, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 134, 255, 255, 255, 255, 255, 255, 255, 255, 251, 50, 0, - 0, 0, 0, 0, 116, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 167, 0, 0, 0, 0, 52, - 249, 255, 255, 254, 131, 0, 0, 0, 0, 63, 255, 255, 255, 254, - 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 7, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 40, 140, 226, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 229, 140, 38, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 64, 9, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 174, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 160, 3, 0, 0, 0, 0, 110, 253, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 94, 0, 0, 0, - 0, 5, 120, 245, 253, 183, 19, 0, 0, 0, 0, 3, 134, 251, - 254, 173, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 53, 179, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 176, 50, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 198, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 245, 47, 0, 0, 0, 0, 16, 157, 220, 217, 191, 163, - 133, 106, 78, 124, 249, 255, 255, 255, 255, 255, 255, 191, 14, 0, - 0, 0, 0, 0, 0, 11, 23, 0, 0, 0, 0, 0, 0, 0, - 0, 12, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 171, 128, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 28, 168, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, - 164, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 222, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 171, 7, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 110, 243, 255, 255, 255, 255, 188, 30, - 0, 1, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 191, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 102, 241, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 237, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 222, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 251, 83, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 26, 217, 255, 255, 255, 111, - 4, 0, 0, 79, 210, 26, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, - 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 15, 174, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 166, 10, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 222, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 224, 28, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 231, 255, 255, - 246, 61, 0, 0, 17, 203, 255, 159, 2, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 40, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 7, 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 254, 227, 190, 169, 157, 158, 170, 192, 229, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 205, 6, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 198, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 167, - 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 245, - 255, 255, 233, 45, 0, 0, 66, 255, 255, 245, 74, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 40, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 108, 254, 255, 255, 255, 255, 255, 255, 255, - 255, 240, 160, 84, 21, 0, 0, 0, 0, 0, 0, 0, 0, 23, - 86, 166, 242, 255, 255, 255, 255, 255, 255, 255, 255, 255, 108, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 166, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 114, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 73, 255, 255, 255, 221, 29, 0, 0, 110, 255, 255, 255, 202, 11, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, 100, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 96, 0, 0, 0, 0, 0, 0, 0, - 52, 140, 140, 140, 140, 158, 255, 226, 140, 140, 140, 140, 136, 0, - 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 131, 255, 255, 255, 255, 255, - 255, 219, 104, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 12, 107, 222, 255, 255, 255, 255, 255, 255, 137, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 122, 254, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 246, 75, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 101, 255, 255, 255, 209, 13, 0, 0, 85, 252, 255, 255, - 250, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, 183, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 175, 0, 0, 0, 0, 0, - 0, 0, 96, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 247, 0, 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 255, 255, - 255, 238, 107, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 6, 112, 241, 255, 255, 255, - 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 62, 253, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 237, 54, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 129, 255, 255, 255, 195, 0, 0, 0, 13, 191, - 255, 255, 255, 208, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, - 60, 84, 84, 84, 84, 84, 84, 84, 84, 84, 58, 0, 0, 0, - 0, 0, 0, 0, 31, 84, 84, 84, 84, 111, 255, 212, 84, 84, - 84, 84, 82, 0, 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, - 185, 255, 184, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 190, - 255, 186, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 7, 240, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 227, 51, 0, 0, - 0, 0, 0, 0, 0, 0, 161, 255, 255, 255, 166, 0, 0, 0, - 0, 78, 252, 255, 255, 249, 86, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 191, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 11, 108, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3, 112, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 227, - 52, 0, 0, 0, 0, 0, 0, 0, 105, 255, 255, 253, 104, 0, - 0, 0, 0, 13, 196, 255, 255, 255, 185, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, - 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, - 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 228, 54, 0, 0, 0, 0, 0, 0, 12, 125, 187, 121, - 10, 0, 0, 0, 0, 0, 109, 255, 255, 255, 235, 38, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 40, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 16, 91, 153, 193, 210, 209, 192, 152, 91, 15, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 202, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 236, 76, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 40, 244, 255, 255, 252, 101, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 40, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 41, 164, 248, 255, 255, 255, 255, 255, 255, 255, 255, 247, - 161, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 72, 252, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 247, 115, 4, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 200, 255, 255, - 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 18, 115, 86, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 10, 144, 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 250, 138, 8, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 175, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 169, 22, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 179, - 255, 255, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 12, 211, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 206, 9, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 235, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 219, 73, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 3, 176, 255, 255, 255, 229, 15, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 123, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 123, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 251, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 252, 170, 38, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 16, 203, 255, 255, 255, 232, 24, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 151, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 151, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 242, 145, 38, 0, 0, 0, - 0, 0, 0, 0, 84, 252, 255, 255, 255, 231, 21, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 179, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 173, 2, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 6, 181, 254, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 249, 184, - 103, 35, 2, 0, 18, 110, 239, 255, 255, 255, 255, 216, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 9, 197, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 197, 9, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 20, 201, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 251, 242, 255, 255, 255, 255, 255, 255, 255, 160, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 19, 216, 255, 255, 255, 255, 255, 255, 255, 255, 212, - 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 199, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 243, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 242, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, - 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, - 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, - 186, 186, 186, 186, 186, 240, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 34, 230, 255, 255, 255, 255, 255, 255, - 228, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 182, - 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 253, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 241, 255, 255, 255, - 255, 238, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 6, 140, 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 253, 164, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 249, - 255, 255, 248, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 77, 225, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 243, 113, 2, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 93, 253, 253, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 18, 144, 247, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 250, 171, 37, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 116, 108, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 151, - 241, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 241, 162, 46, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 13, 96, 174, 238, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 232, 171, 93, 18, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 4, 42, 85, 121, 129, 153, 138, - 127, 111, 79, 34, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 67, 161, 65, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 22, 166, 252, 255, 255, 199, 91, 10, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 37, 216, 255, 255, 255, 255, 255, 255, 238, 162, 91, 26, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 34, 226, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 223, 184, 151, 129, 125, 117, 127, 133, 162, 196, 180, 33, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 6, 194, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 223, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 110, 252, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 205, 21, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 6, 221, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 137, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 249, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 82, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2, 55, 118, 168, 208, 236, 253, 255, 253, 236, 208, 168, 118, - 55, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 43, 145, 232, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 232, 145, 43, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 192, 49, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 49, 179, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 254, 180, 49, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, 236, 255, - 230, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 15, 152, 253, 255, 255, 255, 255, 255, 255, 255, 255, 239, 218, - 212, 218, 239, 255, 255, 255, 255, 255, 255, 255, 255, 253, 153, 15, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 98, 243, - 255, 255, 255, 238, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 56, 224, 255, 255, 255, 255, 255, 255, 208, 130, 67, 18, - 0, 0, 0, 0, 0, 18, 67, 130, 208, 255, 255, 255, 255, 255, - 255, 224, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 117, - 248, 255, 255, 255, 255, 255, 244, 103, 2, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 93, 249, 255, 255, 255, 255, 250, 158, 47, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 158, 250, - 255, 255, 255, 255, 249, 94, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, - 137, 252, 255, 255, 255, 255, 255, 255, 255, 249, 122, 4, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 108, 254, 255, 255, 255, 255, 183, 38, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 37, 183, 255, 255, 255, 255, 254, 108, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 12, 158, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 142, - 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 97, 254, 255, 255, 255, 246, 102, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 101, 246, 255, 255, 255, 254, 98, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 18, 177, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 163, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 62, 250, 255, 255, 255, 233, 53, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 53, 232, 255, 255, 255, 250, - 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 27, 195, 255, 255, 255, 255, 255, 255, 255, 238, 255, 255, - 255, 255, 255, 255, 255, 182, 20, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 19, 228, 255, 255, 255, 232, 41, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 232, 255, - 255, 255, 229, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 39, 211, 255, 255, 255, 255, 255, 255, 253, 153, 26, - 168, 254, 255, 255, 255, 255, 255, 255, 199, 29, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 255, 255, 255, 245, - 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 52, 245, 255, 255, 255, 163, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 53, 181, 189, 189, 189, 189, 189, 189, 189, 189, 189, - 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, - 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, - 189, 189, 189, 189, 189, 189, 189, 189, 189, 162, 27, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 53, 224, 255, 255, 255, 255, 255, 255, 250, 134, - 6, 0, 9, 149, 252, 255, 255, 255, 255, 255, 255, 214, 42, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 254, 255, 255, - 255, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 97, 255, 255, 255, 254, 60, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 231, 254, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 158, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 70, 233, 255, 255, 255, 255, 255, 255, 246, - 114, 2, 0, 0, 0, 5, 129, 249, 255, 255, 255, 255, 255, 255, - 227, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 193, 255, - 255, 255, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 179, 255, 255, 255, 193, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 7, 245, 254, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 88, 242, 255, 255, 255, 255, 255, 255, - 241, 94, 0, 0, 0, 0, 0, 0, 2, 109, 245, 255, 255, 255, - 255, 255, 255, 236, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, - 255, 255, 255, 249, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 35, 249, 255, 255, 255, 56, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 217, 225, 225, 225, - 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, - 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, - 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, - 225, 199, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3, 107, 248, 255, 255, 255, 255, 255, - 255, 235, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 240, - 255, 255, 255, 255, 255, 255, 244, 92, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 158, 255, 255, 255, 154, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 10, 53, 70, 54, 10, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 154, 255, 255, - 255, 158, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 5, 127, 252, 255, 255, 255, 255, - 255, 255, 225, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 70, 232, 255, 255, 255, 255, 255, 255, 249, 112, 3, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 6, 241, 255, 255, 255, 43, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 43, 170, 248, 255, 255, 255, 248, 171, - 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, - 255, 255, 255, 241, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 8, 149, 255, 255, 255, 255, - 255, 255, 255, 213, 42, 0, 0, 0, 0, 0, 12, 85, 8, 0, - 0, 0, 0, 0, 53, 223, 255, 255, 255, 255, 255, 255, 253, 132, - 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 67, 255, 255, 255, 206, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 105, 249, 255, 255, 255, 255, 255, - 255, 255, 250, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 206, 255, 255, 255, 67, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 171, 255, 255, 255, - 255, 255, 255, 255, 198, 30, 0, 0, 0, 0, 0, 19, 177, 254, - 164, 14, 0, 0, 0, 0, 0, 38, 210, 255, 255, 255, 255, 255, - 255, 255, 155, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 129, 255, 255, 255, 129, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 99, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 102, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 129, 255, 255, 255, 129, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 190, 255, 255, - 255, 255, 255, 255, 254, 182, 20, 0, 0, 0, 0, 0, 28, 195, - 255, 255, 255, 183, 21, 0, 0, 0, 0, 0, 27, 195, 255, 255, - 255, 255, 255, 255, 255, 176, 15, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 178, 255, 255, 255, 68, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 245, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 246, 32, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 68, 255, 255, 255, 178, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 207, 255, - 255, 255, 255, 255, 255, 253, 164, 12, 0, 0, 0, 0, 0, 39, - 210, 255, 255, 255, 255, 255, 200, 31, 0, 0, 0, 0, 0, 18, - 178, 254, 255, 255, 255, 255, 255, 255, 194, 23, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 215, 255, 255, 255, - 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 147, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 22, 255, 255, 255, 215, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 247, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 221, - 255, 255, 255, 255, 255, 255, 251, 144, 6, 0, 0, 0, 0, 0, - 54, 223, 255, 255, 255, 255, 255, 255, 255, 214, 44, 0, 0, 0, - 0, 0, 10, 159, 252, 255, 255, 255, 255, 255, 255, 211, 34, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, - 255, 246, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 227, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 255, 255, - 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 247, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, - 232, 255, 255, 255, 255, 255, 255, 248, 124, 2, 0, 0, 0, 0, - 0, 71, 233, 255, 255, 255, 255, 255, 255, 255, 255, 255, 226, 59, - 0, 0, 0, 0, 0, 5, 140, 250, 255, 255, 255, 255, 255, 255, - 224, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 10, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 71, 126, 143, 119, - 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 247, 38, 0, 0, 0, 0, 0, 0, 0, 1, - 60, 119, 143, 126, 71, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 77, 241, 255, 255, 255, 255, 255, 255, 245, 102, 0, 0, 0, 0, - 0, 1, 89, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 236, 76, 0, 0, 0, 0, 0, 1, 119, 248, 255, 255, 255, - 255, 255, 255, 234, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 225, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 225, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 95, 227, 254, 255, - 255, 254, 254, 207, 69, 0, 0, 0, 0, 0, 0, 0, 96, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 247, 38, 0, 0, 0, 0, 0, 0, - 70, 207, 254, 254, 255, 255, 254, 227, 88, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 96, 248, 255, 255, 255, 255, 255, 255, 239, 81, 0, 0, 0, - 0, 0, 3, 108, 247, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 243, 95, 1, 0, 0, 0, 0, 0, 97, 244, - 255, 255, 255, 255, 255, 255, 243, 82, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 247, 255, 255, 237, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 244, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 246, 2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 237, 255, 255, 247, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 154, 255, 254, - 255, 255, 255, 255, 255, 254, 251, 119, 0, 0, 0, 0, 0, 0, - 96, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 247, 38, 0, 0, 0, 0, - 0, 119, 253, 254, 255, 255, 255, 255, 255, 254, 255, 152, 5, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2, 118, 253, 255, 255, 255, 255, 255, 255, 231, 63, 0, 0, - 0, 0, 0, 6, 127, 251, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 248, 114, 3, 0, 0, 0, 0, - 0, 77, 238, 255, 255, 255, 255, 255, 255, 250, 101, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 225, 255, 255, 254, 8, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 180, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 184, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 8, 254, 255, 255, 226, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, 254, - 254, 255, 255, 255, 255, 255, 255, 255, 255, 254, 85, 0, 0, 0, - 0, 0, 96, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 247, 38, 0, 0, - 0, 0, 88, 254, 255, 255, 255, 255, 255, 255, 255, 255, 254, 254, - 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 3, 142, 255, 255, 255, 255, 255, 255, 255, 220, 46, 0, - 0, 0, 0, 0, 9, 148, 254, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 134, 7, 0, - 0, 0, 0, 0, 58, 229, 255, 255, 255, 255, 255, 255, 253, 124, - 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 193, 255, 255, 255, 48, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 80, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 47, 255, 255, 255, 194, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, - 247, 255, 255, 255, 254, 255, 255, 255, 254, 255, 255, 254, 227, 8, - 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 247, 38, - 0, 0, 0, 9, 230, 254, 255, 255, 254, 255, 255, 255, 254, 255, - 255, 255, 247, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 7, 165, 255, 255, 255, 255, 255, 255, 255, 207, 32, - 0, 0, 0, 0, 0, 14, 169, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, - 155, 10, 0, 0, 0, 0, 0, 42, 218, 255, 255, 255, 255, 255, - 255, 255, 148, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 255, - 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 182, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 102, 255, 255, 255, 149, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 136, 254, 255, 255, 254, 229, 100, 51, 119, 250, 254, 255, 255, - 254, 97, 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 247, 38, 0, 0, 0, 99, 254, 255, 255, 254, 243, 116, 51, 100, - 229, 254, 255, 255, 254, 136, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 14, 185, 255, 255, 255, 255, 255, 255, 254, 191, - 22, 0, 0, 0, 0, 0, 22, 188, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 175, 16, 0, 0, 0, 0, 0, 29, 204, 255, 255, - 255, 255, 255, 255, 255, 170, 8, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 255, - 255, 255, 174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 15, 201, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 16, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 255, - 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 206, 255, 255, 255, 250, 61, 0, 0, 0, 92, 255, - 255, 255, 254, 162, 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 247, 38, 0, 0, 0, 166, 254, 255, 255, 255, 92, 0, - 0, 0, 61, 250, 255, 255, 255, 203, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 23, 204, 255, 255, 255, 255, 255, 255, 253, - 174, 12, 0, 0, 0, 0, 0, 33, 205, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 194, 25, 0, 0, 0, 0, 0, 19, - 188, 254, 255, 255, 255, 255, 255, 255, 190, 16, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 22, 253, 255, 255, 245, 15, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 8, 142, 249, 255, 255, 255, 255, 255, 249, 144, 9, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 244, 255, - 255, 253, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 231, 255, 255, 255, 218, 8, 0, 0, 0, - 23, 246, 255, 255, 255, 181, 0, 0, 0, 0, 96, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 247, 38, 0, 0, 0, 188, 255, 255, 255, 245, - 22, 0, 0, 0, 8, 218, 255, 255, 255, 231, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 35, 219, 255, 255, 255, 255, 255, 255, - 252, 154, 6, 0, 0, 0, 0, 0, 46, 219, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 210, 37, 0, 0, 0, - 0, 0, 10, 170, 253, 255, 255, 255, 255, 255, 255, 208, 25, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 191, 255, 255, 255, 111, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 25, 107, 157, 174, 157, 108, 26, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, - 255, 255, 255, 194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 228, 255, 255, 255, 231, 18, 0, - 0, 0, 32, 251, 255, 255, 255, 181, 0, 0, 0, 0, 96, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 247, 38, 0, 0, 0, 183, 255, 255, - 255, 250, 32, 0, 0, 0, 19, 232, 255, 255, 255, 224, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, 255, 255, 255, - 255, 251, 133, 2, 0, 0, 0, 0, 0, 61, 230, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 223, 50, - 0, 0, 0, 0, 0, 4, 150, 252, 255, 255, 255, 255, 255, 255, - 199, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 94, 255, 255, 255, 225, 7, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 6, 223, 255, 255, 255, 97, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 254, 255, 255, 254, - 133, 6, 0, 9, 173, 254, 255, 255, 254, 134, 0, 0, 0, 0, - 96, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 247, 38, 0, 0, 0, 136, - 254, 255, 255, 254, 173, 9, 0, 6, 133, 254, 255, 255, 254, 181, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 234, 255, 255, - 255, 255, 250, 109, 0, 0, 0, 0, 0, 0, 78, 239, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 234, 66, 0, 0, 0, 0, 0, 1, 127, 251, 255, 255, 255, - 255, 240, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 7, 229, 255, 255, 255, 122, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 119, 255, 255, 255, 231, 7, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 254, 255, - 255, 254, 254, 205, 170, 219, 254, 255, 255, 255, 254, 44, 0, 0, - 0, 0, 96, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 247, 38, 0, 0, - 0, 51, 254, 255, 255, 255, 254, 219, 170, 207, 254, 254, 255, 255, - 254, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, - 242, 255, 255, 245, 87, 0, 0, 0, 0, 0, 2, 97, 246, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 242, 84, 1, 0, 0, 0, 0, 0, 104, 249, - 255, 255, 248, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 255, 255, 255, - 249, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 49, 248, 255, 255, 255, 112, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 210, 254, 255, 255, 255, 255, 255, 254, 255, 255, 255, 254, 172, 0, - 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 247, 38, - 0, 0, 0, 0, 175, 254, 255, 255, 255, 254, 255, 255, 255, 255, - 255, 254, 210, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 73, 249, 238, 67, 0, 0, 0, 0, 0, 3, 117, 251, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 248, 104, 2, 0, 0, 0, 0, - 0, 82, 244, 252, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 215, - 255, 255, 255, 217, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 10, 215, 255, 255, 255, 217, 6, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 46, 239, 254, 255, 255, 255, 255, 255, 255, 255, 254, 224, - 26, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 247, 38, 0, 0, 0, 0, 26, 224, 254, 255, 255, 255, 255, 255, - 255, 255, 254, 234, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 85, 49, 0, 0, 0, 0, 0, 5, 140, - 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 125, 4, 0, - 0, 0, 0, 0, 63, 101, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 49, 248, 255, 255, 255, 157, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 155, 255, 255, 255, 249, 51, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 54, 217, 254, 255, 255, 255, 255, 255, 254, - 198, 32, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 247, 38, 0, 0, 0, 0, 0, 31, 201, 254, 255, 255, - 255, 255, 255, 254, 217, 51, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, - 162, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 148, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 115, 255, 255, 255, 255, 86, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 84, 255, 255, 255, 255, 117, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 255, 255, 255, 255, - 255, 240, 10, 0, 0, 0, 0, 0, 0, 0, 58, 151, 151, 151, - 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, - 151, 151, 151, 151, 148, 22, 0, 0, 0, 0, 0, 0, 10, 240, - 255, 255, 255, 255, 255, 255, 37, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 16, 183, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 169, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 185, 255, 255, 255, 239, 31, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 29, 238, 255, 255, 255, 186, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, 255, 255, - 255, 255, 255, 240, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 9, 240, 255, 255, 255, 255, 255, 255, 36, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 25, 201, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 189, 19, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 233, 255, 255, 255, - 195, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 193, 255, 255, 255, - 234, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, - 255, 255, 255, 255, 255, 240, 9, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 240, 255, 255, 255, 255, 255, 255, 36, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 37, 216, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, 28, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 254, - 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 255, 255, - 255, 254, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 36, 255, 255, 255, 255, 255, 255, 240, 9, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 9, 240, 255, 255, 255, 255, 255, 255, 36, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 6, 201, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 187, 4, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 145, 255, 255, 255, 251, 59, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 251, - 255, 255, 255, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 36, 255, 255, 255, 255, 255, 255, 240, 9, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 9, 240, 255, 255, 255, 255, 255, 255, - 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 7, 212, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 197, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 7, 208, 255, 255, 255, 224, 15, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, - 223, 255, 255, 255, 210, 8, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 36, 255, 255, 255, 255, 255, 255, 240, 9, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 9, 240, 255, 255, 255, 255, - 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 7, 212, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 197, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 42, 245, 255, 255, 255, 168, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 166, 255, 255, 255, 246, 43, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 36, 255, 255, 255, 255, 255, 255, 240, - 9, 0, 0, 0, 0, 156, 254, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 243, 84, 0, 0, 0, 9, 240, 255, 255, - 255, 255, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 212, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 197, 5, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 255, 255, - 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 95, 255, 255, 255, 255, 106, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, 255, 255, 255, 255, - 255, 240, 9, 0, 0, 0, 43, 248, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 254, 201, 3, 0, 0, 9, 240, - 255, 255, 255, 255, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 212, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 197, 5, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 175, 255, - 255, 255, 243, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 36, 242, 255, 255, 255, 176, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, 255, 255, - 255, 255, 255, 240, 9, 0, 0, 0, 18, 229, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 161, 0, 0, 0, - 9, 240, 255, 255, 255, 255, 255, 255, 36, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, - 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 197, 5, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 18, 228, 255, 255, 255, 204, 5, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 5, 202, 255, 255, 255, 228, 19, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, - 255, 255, 255, 255, 255, 240, 9, 0, 0, 0, 0, 49, 137, 143, - 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, - 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 125, 18, 0, - 0, 0, 9, 240, 255, 255, 255, 255, 255, 255, 36, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 7, 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 197, 5, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 65, 253, 255, 255, 255, 138, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 136, 255, 255, 255, 253, 66, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 26, 254, 255, 255, 255, 255, 255, 235, 8, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 8, 235, 255, 255, 255, 255, 255, 254, 23, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 7, 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 197, 5, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 134, 255, 255, 255, 253, 68, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 67, 253, 255, 255, 255, 135, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 212, 254, 255, 255, 255, 254, 170, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2, 176, 254, 255, 255, 255, 254, 212, - 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 7, 212, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 197, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 4, 201, 255, 255, 255, 229, - 20, 0, 0, 0, 0, 0, 0, 0, 20, 229, 255, 255, 255, 202, - 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 61, 239, 254, 254, 255, 225, 38, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 228, 255, 254, 254, - 236, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 7, 212, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 197, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 241, 255, - 255, 255, 178, 0, 0, 0, 0, 0, 0, 0, 177, 255, 255, 255, - 242, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 31, 128, 161, 116, 17, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 116, - 161, 128, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 212, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 197, 5, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 93, 255, 255, 255, 255, 107, 0, 0, 0, 0, 0, 107, 255, 255, - 255, 255, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 212, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 197, 5, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 164, 255, 255, 255, 246, 44, 0, 0, 0, 44, 246, - 255, 255, 255, 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, - 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 197, 5, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 14, 221, 255, 255, 255, 211, 8, 0, 8, - 210, 255, 255, 255, 222, 14, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 7, 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 197, 5, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 56, 251, 255, 255, 255, 148, - 0, 148, 255, 255, 255, 251, 57, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 7, 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 197, 5, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123, 255, 255, - 255, 254, 146, 254, 255, 255, 255, 124, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 5, 192, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 176, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, - 192, 255, 255, 255, 255, 255, 255, 255, 192, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 89, 253, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 251, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 28, 237, 255, 255, 255, 255, 255, 237, 29, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 89, 213, 253, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 252, 206, 79, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 82, 255, 255, 255, 255, 255, 83, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 154, 255, 255, 255, 154, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 180, 252, 180, - 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; +/** + * Parrot Drones Awesome Video Viewer + * OpenGL ES 2.0 HUD rendering library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 "pdraw_gles2hud_priv.h" + + +const int hud_icons_width = 212; +const int hud_icons_height = 210; + +const uint8_t hud_icons[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 29, 117, 184, 229, 251, 253, 237, 197, 135, 50, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 50, 136, 197, 237, 253, 251, 229, 184, 117, 29, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 27, 162, 251, 255, 254, 227, 199, 195, 217, 251, 255, 255, + 194, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 55, 194, 255, 255, 251, 217, 195, 198, 227, 254, + 255, 251, 163, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 85, 241, 255, 214, 106, 25, 0, 0, 0, 0, 13, + 84, 186, 255, 253, 133, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 133, 253, 255, 186, 84, 13, 0, 0, 0, + 0, 25, 106, 214, 255, 241, 85, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 115, 234, 245, 152, 11, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 11, 152, 245, 234, 116, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 111, 253, 245, 108, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 68, 225, 255, 168, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3, 168, 255, 225, 68, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 108, 245, 253, 111, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 81, 251, 255, 255, 254, 130, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 130, 254, 255, 255, 251, 81, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 86, 254, 232, 50, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 20, 200, 255, 148, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 148, 255, 200, 20, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 232, 254, 86, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 206, 254, 255, 255, 255, 189, 8, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 189, 255, 255, 255, 254, + 205, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 111, 233, 233, 111, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 29, 241, 245, 49, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 216, 255, 79, + 0, 0, 0, 0, 0, 0, 0, 0, 79, 255, 216, 17, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 245, + 241, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 74, 252, 255, 255, 255, 255, 158, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 158, 255, 255, + 255, 255, 251, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 175, 255, 255, 255, 255, 175, 9, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 166, 255, 106, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, + 251, 223, 5, 0, 0, 0, 0, 0, 0, 5, 223, 251, 48, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 106, 255, 166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 254, 255, 255, 255, + 250, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, + 250, 255, 255, 255, 254, 180, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 184, 255, 255, 255, 255, 255, 255, 184, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 252, 213, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 153, 255, 92, 0, 0, 0, 0, 0, 0, 92, 255, 153, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 213, 252, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 249, 255, 255, + 255, 255, 178, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 177, 255, 255, 255, 255, 249, 34, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 151, 255, 255, 255, 255, 255, 255, 255, 255, + 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 255, 105, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 42, 255, 183, 0, 0, 0, 0, 0, 0, 183, + 255, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 105, 255, 120, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 254, + 255, 255, 255, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 73, 255, 255, 255, 255, 254, 120, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 187, + 254, 25, 0, 0, 0, 0, 0, 0, 0, 37, 134, 143, 67, 0, + 0, 0, 0, 0, 0, 0, 0, 217, 245, 4, 0, 0, 0, 0, + 4, 245, 217, 0, 0, 0, 0, 0, 0, 0, 0, 67, 143, 134, + 37, 0, 0, 0, 0, 0, 0, 0, 25, 254, 187, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 206, 255, 255, 255, 255, 214, 11, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 12, 214, 255, 255, 255, 255, 205, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 61, 246, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 246, 61, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 231, 227, 0, 0, 0, 0, 0, 0, 0, 33, 242, 255, 255, + 254, 104, 0, 0, 0, 0, 0, 0, 0, 165, 255, 38, 0, 0, + 0, 0, 38, 255, 164, 0, 0, 0, 0, 0, 0, 0, 104, 254, + 255, 255, 242, 33, 0, 0, 0, 0, 0, 0, 0, 227, 231, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 26, 251, 255, 255, 255, 255, 134, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 134, 255, 255, 255, 255, + 251, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 84, 247, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 247, 84, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 251, 201, 0, 0, 0, 0, 0, 0, 0, 123, 255, + 255, 255, 255, 254, 104, 0, 0, 0, 0, 0, 0, 138, 255, 59, + 0, 0, 0, 0, 59, 255, 138, 0, 0, 0, 0, 0, 0, 104, + 254, 255, 255, 255, 255, 123, 0, 0, 0, 0, 0, 0, 0, 201, + 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 93, 254, 255, 255, 255, 255, 54, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 255, 255, + 255, 255, 254, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 253, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 112, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 252, 199, 0, 0, 0, 0, 0, 0, 0, + 125, 255, 255, 255, 255, 255, 254, 111, 0, 0, 0, 0, 0, 79, + 219, 30, 0, 0, 0, 0, 30, 219, 79, 0, 0, 0, 0, 0, + 111, 254, 255, 255, 255, 255, 255, 125, 0, 0, 0, 0, 0, 0, + 0, 199, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 149, 254, 255, 255, 255, 225, 11, 0, 0, + 0, 0, 0, 9, 112, 164, 94, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 33, 82, 108, 118, 108, 82, 33, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 94, 164, 112, 9, 0, 0, 0, 0, 0, 11, + 226, 255, 255, 255, 254, 149, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 142, 255, 201, 200, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 200, 201, + 255, 142, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 234, 224, 0, 0, 0, 0, 0, + 0, 0, 43, 250, 255, 255, 255, 255, 255, 254, 112, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 112, 254, 255, 255, 255, 255, 255, 250, 43, 0, 0, 0, 0, + 0, 0, 0, 224, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 194, 255, 255, 255, 255, 173, 4, + 0, 0, 0, 0, 0, 114, 250, 255, 248, 80, 0, 0, 0, 0, + 0, 0, 16, 147, 233, 249, 254, 255, 254, 249, 233, 147, 16, 0, + 0, 0, 0, 0, 0, 80, 248, 255, 250, 114, 0, 0, 0, 0, + 0, 4, 176, 255, 255, 255, 255, 194, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 170, 255, 178, + 10, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 191, 10, 178, 255, 170, 7, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 254, 20, 0, 0, + 0, 0, 0, 0, 0, 69, 250, 255, 255, 255, 255, 255, 254, 112, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 112, 254, 255, 255, 255, 255, 255, 250, 69, 0, 0, 0, + 0, 0, 0, 0, 20, 254, 192, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 227, 255, 255, 255, 255, + 132, 0, 0, 0, 0, 0, 0, 166, 255, 255, 254, 130, 0, 0, + 0, 0, 0, 0, 71, 251, 255, 255, 255, 255, 255, 255, 255, 251, + 71, 0, 0, 0, 0, 0, 0, 130, 254, 255, 255, 166, 0, 0, + 0, 0, 0, 0, 133, 255, 255, 255, 255, 227, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 194, 255, + 151, 3, 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 191, 0, 3, 151, 255, 194, 17, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 96, + 0, 0, 0, 0, 0, 0, 0, 0, 69, 250, 255, 255, 255, 255, + 255, 254, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 112, 254, 255, 255, 255, 255, 255, 250, 69, 0, 0, + 0, 0, 0, 0, 0, 0, 96, 255, 127, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 255, + 255, 255, 104, 0, 0, 0, 0, 0, 0, 93, 247, 254, 240, 63, + 0, 0, 0, 0, 0, 0, 86, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 85, 0, 0, 0, 0, 0, 0, 63, 240, 254, 247, 93, + 0, 0, 0, 0, 0, 0, 103, 255, 255, 255, 255, 248, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 214, + 254, 120, 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 191, 0, 0, 0, 120, 254, 214, 31, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, + 254, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 250, 255, + 255, 255, 255, 255, 254, 112, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 112, 254, 255, 255, 255, 255, 255, 250, 69, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 203, 254, 39, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, + 255, 255, 255, 255, 94, 0, 0, 0, 0, 0, 0, 5, 74, 123, + 60, 2, 0, 0, 0, 0, 0, 0, 100, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 99, 0, 0, 0, 0, 0, 0, 2, 60, 123, + 74, 5, 0, 0, 0, 0, 0, 0, 94, 255, 255, 255, 255, 254, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, + 230, 249, 92, 0, 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, 0, 92, 249, 230, + 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 178, 255, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 66, 249, 255, 255, 255, 255, 255, 254, 120, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 120, 254, 255, 255, 255, 255, 255, 249, 66, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 255, 178, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 246, 255, 255, 255, 255, 101, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 111, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 255, 255, 255, + 255, 246, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 71, 242, 240, 67, 0, 0, 0, 0, 0, 191, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, + 67, 240, 242, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 39, 247, 238, 36, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 64, 249, 255, 255, 255, 255, 255, 255, 121, 0, 0, + 0, 0, 0, 0, 0, 0, 121, 255, 255, 255, 255, 255, 255, 249, + 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 238, 247, 38, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 232, 255, 255, 255, 255, 121, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 119, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 255, + 255, 255, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 97, 250, 227, 46, 0, 0, 0, 0, 0, 0, 191, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, + 0, 0, 0, 46, 227, 250, 97, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 104, 255, 222, 35, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 64, 249, 255, 255, 255, 255, 255, 255, + 121, 0, 0, 0, 0, 0, 0, 121, 255, 255, 255, 255, 255, 255, + 249, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 222, 255, + 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 204, 255, 255, 255, 255, 151, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 128, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 152, 255, 255, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 126, 254, 211, 28, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, + 0, 0, 0, 0, 0, 0, 28, 211, 254, 126, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, 255, 236, 84, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 249, 255, 255, 255, + 255, 255, 243, 0, 0, 0, 0, 0, 0, 243, 255, 255, 255, 255, + 255, 249, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 236, + 255, 133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 171, 255, 255, 255, 255, 192, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 136, 255, 255, 255, 255, 255, 255, 255, 255, 255, 136, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 6, 192, 255, 255, 255, 255, 171, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 156, 255, 190, 15, 0, 0, 0, 0, 0, 0, 0, + 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 191, 0, 0, 0, 0, 0, 0, 0, 0, 15, 190, 255, 156, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, + 248, 255, 193, 82, 9, 0, 0, 0, 0, 0, 0, 0, 64, 249, + 255, 255, 255, 255, 243, 0, 0, 0, 0, 0, 0, 243, 255, 255, + 255, 255, 249, 64, 0, 0, 0, 0, 0, 0, 0, 9, 83, 193, + 255, 248, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 254, 255, 255, 255, + 237, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 139, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 14, 237, 255, 255, 255, 254, 128, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 12, 182, 255, 165, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 165, + 255, 182, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 43, 186, 255, 255, 248, 203, 175, 124, 0, 0, 0, 0, + 0, 129, 255, 255, 255, 255, 243, 0, 0, 0, 0, 0, 0, 243, + 255, 255, 255, 255, 129, 0, 0, 0, 0, 0, 125, 175, 203, 248, + 255, 255, 186, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, 254, 255, + 255, 255, 255, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 145, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 56, 255, 255, 255, 255, 254, 79, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 24, 204, 255, 136, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 136, 255, 204, 24, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 49, 141, 208, 249, 255, 228, 0, 0, + 0, 0, 0, 125, 255, 255, 255, 255, 243, 0, 0, 0, 0, 0, + 0, 243, 255, 255, 255, 255, 125, 0, 0, 0, 0, 0, 228, 255, + 249, 208, 140, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, + 251, 255, 255, 255, 255, 116, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 255, 255, 251, 25, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 39, 222, 252, 106, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 106, 252, 222, 39, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 19, 9, + 0, 0, 0, 0, 0, 125, 255, 255, 255, 255, 243, 0, 0, 0, + 0, 0, 0, 243, 255, 255, 255, 255, 125, 0, 0, 0, 0, 0, + 9, 19, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 217, 255, 255, 255, 255, 169, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 3, 169, 255, 255, 255, 255, + 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 59, 236, 245, 79, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 79, 245, 236, 59, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 125, 255, 255, 255, 255, 243, 0, + 0, 0, 0, 0, 0, 243, 255, 255, 255, 255, 125, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 157, 254, 255, 255, 255, 213, 9, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 148, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 213, 255, 255, + 255, 254, 157, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 83, 246, 234, 56, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 234, 246, + 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 255, 255, 255, 255, + 243, 0, 0, 0, 0, 0, 0, 243, 255, 255, 255, 255, 125, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 101, 254, 255, 255, 255, 246, 16, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 147, 255, 255, 255, 255, 255, 255, 255, 255, 255, 146, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 246, + 255, 255, 255, 254, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 111, 252, 220, 37, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 37, 220, 252, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 255, 255, + 255, 255, 243, 0, 0, 0, 0, 0, 0, 243, 255, 255, 255, 255, + 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 64, 252, 255, 255, 255, 255, + 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 141, 255, 255, 255, 255, 255, 255, 255, 255, 255, 141, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 33, 255, 255, 255, 255, 252, 63, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 141, 255, 201, 21, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 21, 201, 255, 141, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, + 255, 255, 255, 255, 243, 0, 0, 0, 0, 0, 0, 243, 255, 255, + 255, 255, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 252, 255, 255, + 255, 255, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 139, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 45, 255, 255, 255, 255, 252, 52, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 169, 255, 178, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 10, 178, 255, 169, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3, 19, 9, 0, 0, 0, 0, + 0, 125, 255, 255, 255, 255, 243, 0, 0, 0, 0, 0, 0, 243, + 255, 255, 255, 255, 125, 0, 0, 0, 0, 0, 116, 150, 128, 84, + 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 253, + 255, 255, 255, 255, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 134, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 34, 255, 255, 255, 255, 253, 69, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 193, 255, 151, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 151, 255, 193, 17, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 49, 141, 208, 249, 255, 228, 0, 0, + 0, 0, 0, 125, 255, 255, 255, 255, 243, 0, 0, 0, 0, 0, + 0, 243, 255, 255, 255, 255, 125, 0, 0, 0, 0, 0, 231, 255, + 255, 255, 251, 180, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 114, 254, 255, 255, 255, 245, 16, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 124, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 16, 245, 255, 255, 255, 254, 113, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 213, 254, + 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, + 254, 213, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 43, 186, 255, 255, 248, 203, 175, 125, + 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 243, 0, 0, 0, + 0, 0, 0, 243, 255, 255, 255, 255, 127, 0, 0, 0, 0, 0, + 15, 44, 72, 125, 206, 255, 255, 168, 17, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 175, 255, 255, 255, 255, 207, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 114, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 9, 207, 255, 255, 255, 255, + 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 229, + 249, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 92, 249, 229, 48, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 107, 248, 255, 193, 82, 9, 0, + 0, 0, 0, 0, 0, 0, 58, 247, 255, 255, 255, 255, 243, 0, + 0, 0, 0, 0, 0, 243, 255, 255, 255, 255, 247, 58, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 62, 204, 255, 221, 36, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4, 235, 255, 255, 255, 255, 154, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 102, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 154, 255, 255, + 255, 255, 235, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, + 242, 240, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 67, 240, 242, 70, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 133, 255, 236, 84, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 64, 247, 255, 255, 255, 255, 255, + 243, 0, 0, 0, 0, 0, 0, 243, 255, 255, 255, 255, 255, 247, + 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 140, 255, 227, + 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 46, 253, 255, 255, 255, 255, 90, 0, 0, + 0, 0, 0, 0, 0, 12, 57, 51, 7, 0, 0, 0, 0, 0, + 91, 255, 255, 255, 255, 255, 255, 255, 255, 255, 90, 0, 0, 0, + 0, 0, 7, 51, 57, 13, 0, 0, 0, 0, 0, 0, 0, 90, + 255, 255, 255, 255, 253, 46, 0, 0, 0, 0, 0, 0, 0, 0, + 96, 250, 227, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 46, 228, 250, 96, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 222, 35, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 64, 249, 255, 255, 255, 255, + 255, 255, 121, 0, 0, 0, 0, 0, 0, 121, 255, 255, 255, 255, + 255, 255, 249, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 127, 255, 199, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 93, 254, 255, 255, 255, 252, 30, + 0, 0, 0, 0, 0, 0, 5, 162, 251, 246, 126, 3, 0, 0, + 0, 0, 76, 254, 255, 255, 255, 255, 255, 255, 255, 254, 75, 0, + 0, 0, 0, 3, 126, 246, 251, 162, 5, 0, 0, 0, 0, 0, + 0, 30, 252, 255, 255, 255, 254, 93, 0, 0, 0, 0, 0, 0, + 0, 91, 254, 211, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 212, 254, + 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 247, 238, 36, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 249, 255, 255, 255, + 255, 255, 255, 121, 0, 0, 0, 0, 0, 0, 0, 0, 121, 255, + 255, 255, 255, 255, 255, 249, 64, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 174, 255, 110, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 254, 255, 255, 255, + 225, 10, 0, 0, 0, 0, 0, 0, 44, 237, 255, 255, 216, 23, + 0, 0, 0, 0, 65, 248, 255, 255, 255, 255, 255, 255, 255, 248, + 65, 0, 0, 0, 0, 23, 217, 255, 255, 237, 44, 0, 0, 0, + 0, 0, 0, 10, 225, 255, 255, 255, 254, 125, 0, 0, 0, 0, + 0, 0, 0, 219, 220, 16, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16, 220, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 178, 255, + 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 249, 255, 255, + 255, 255, 255, 255, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 121, 255, 255, 255, 255, 255, 255, 249, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 25, 243, 233, 8, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 255, 255, + 255, 255, 202, 7, 0, 0, 0, 0, 0, 0, 26, 223, 254, 254, + 193, 15, 0, 0, 0, 0, 46, 225, 254, 255, 255, 255, 255, 255, + 254, 225, 46, 0, 0, 0, 0, 15, 193, 254, 254, 223, 26, 0, + 0, 0, 0, 0, 0, 7, 203, 255, 255, 255, 255, 141, 0, 0, + 0, 0, 0, 0, 0, 254, 153, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 15, 116, 240, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 240, 116, 15, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 153, 254, 0, 0, 0, 0, 0, 0, 0, 0, 39, + 254, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 249, 255, + 255, 255, 255, 255, 255, 121, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 121, 255, 255, 255, 255, 255, 255, 249, 64, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 145, 255, 87, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, + 255, 255, 255, 255, 198, 6, 0, 0, 0, 0, 0, 0, 0, 77, + 192, 181, 54, 0, 0, 0, 0, 0, 1, 51, 114, 152, 170, 176, + 170, 152, 114, 51, 1, 0, 0, 0, 0, 0, 54, 181, 192, 77, + 0, 0, 0, 0, 0, 0, 0, 6, 198, 255, 255, 255, 255, 146, + 0, 0, 0, 0, 0, 0, 0, 255, 151, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 33, 143, 242, 255, 232, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 232, 255, 242, 143, 33, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 151, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 127, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 69, 249, + 255, 255, 255, 255, 255, 254, 116, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 116, 254, 255, 255, 255, 255, 255, + 249, 69, 0, 0, 0, 0, 0, 0, 0, 0, 52, 255, 163, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 134, 254, 255, 255, 255, 213, 8, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 213, 255, 255, 255, + 254, 134, 0, 0, 0, 0, 0, 0, 0, 255, 151, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 57, 171, 252, 255, 192, 78, 2, 159, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 159, 2, 78, 192, 255, 252, 171, + 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 151, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 192, 254, 20, 0, 0, 0, 0, 0, 0, 0, 69, + 250, 255, 255, 255, 255, 255, 254, 112, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 254, 255, 255, + 255, 255, 255, 250, 69, 0, 0, 0, 0, 0, 0, 0, 2, 242, + 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 106, 254, 255, 255, 255, 246, 22, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 246, 255, + 255, 255, 254, 106, 0, 0, 0, 0, 0, 0, 0, 255, 151, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 85, 198, 255, 250, 164, 50, 0, 0, 0, 1, 121, + 248, 255, 255, 255, 255, 255, 255, 248, 122, 1, 0, 0, 0, 50, + 164, 250, 255, 198, 85, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 234, 224, 0, 0, 0, 0, 0, 0, 0, + 43, 249, 255, 255, 255, 255, 255, 254, 112, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3, 95, 21, 0, 0, 0, 0, 112, + 254, 255, 255, 255, 255, 255, 249, 43, 0, 0, 0, 0, 0, 0, + 0, 208, 247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 58, 253, 255, 255, 255, 255, 101, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, + 255, 255, 255, 255, 253, 58, 0, 0, 0, 0, 0, 0, 0, 255, + 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 13, 113, 222, 255, 238, 136, 28, 0, 0, 0, 0, 0, + 0, 0, 36, 163, 250, 255, 255, 250, 163, 36, 0, 0, 0, 0, + 0, 0, 0, 28, 136, 238, 255, 222, 113, 13, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 252, 199, 0, 0, 0, 0, 0, + 0, 0, 125, 255, 255, 255, 255, 255, 254, 112, 0, 0, 0, 0, + 0, 79, 219, 30, 0, 0, 0, 0, 59, 255, 128, 0, 0, 0, + 0, 0, 112, 254, 255, 255, 255, 255, 255, 125, 0, 0, 0, 0, + 0, 0, 0, 197, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 7, 235, 255, 255, 255, 255, 211, + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16, 211, 255, 255, 255, 255, 234, 7, 0, 0, 0, 0, 0, 0, + 0, 255, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 30, 140, 240, 255, 220, 109, 12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 26, 119, 119, 26, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 12, 109, 220, 255, 240, 140, 30, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 251, 201, 0, 0, 0, + 0, 0, 0, 0, 124, 255, 255, 255, 255, 254, 112, 0, 0, 0, + 0, 0, 0, 138, 255, 59, 0, 0, 0, 0, 51, 255, 148, 0, + 0, 0, 0, 0, 0, 112, 254, 255, 255, 255, 255, 124, 0, 0, + 0, 0, 0, 0, 0, 211, 244, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 254, 255, 255, + 255, 255, 138, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 138, 255, 255, 255, 255, 254, 149, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 54, 167, 251, 255, 195, 81, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 81, 195, + 255, 251, 167, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 151, 255, 0, 0, 0, 0, 0, 0, 0, 0, 231, 227, 0, + 0, 0, 0, 0, 0, 0, 35, 243, 255, 255, 254, 107, 0, 0, + 0, 0, 0, 0, 0, 164, 255, 38, 0, 0, 0, 0, 18, 255, + 188, 0, 0, 0, 0, 0, 0, 0, 107, 254, 255, 255, 243, 35, + 0, 0, 0, 0, 0, 0, 5, 246, 211, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 246, + 255, 255, 255, 255, 251, 109, 5, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 109, 251, 255, 255, 255, 255, 246, 41, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 151, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 81, 195, 255, 251, 167, 54, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 54, 167, 251, 255, 195, 81, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 151, 255, 0, 0, 0, 0, 0, 0, 0, 0, 187, + 254, 25, 0, 0, 0, 0, 0, 0, 0, 39, 137, 147, 69, 0, + 0, 0, 0, 0, 0, 0, 0, 217, 245, 4, 0, 0, 0, 0, + 0, 218, 245, 10, 0, 0, 0, 0, 0, 0, 0, 69, 147, 137, + 39, 0, 0, 0, 0, 0, 0, 0, 62, 255, 155, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 147, 254, 255, 255, 255, 255, 250, 134, 15, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 15, 134, 250, 255, 255, 255, 255, 254, 147, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 151, 0, 0, 0, 0, 0, + 0, 11, 109, 220, 255, 240, 140, 30, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 6, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 6, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 30, 140, 240, 255, 220, 109, 11, 0, + 0, 0, 0, 0, 0, 151, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 120, 255, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 42, 255, 183, 0, 0, 0, + 0, 0, 0, 139, 255, 95, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 158, 255, 76, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 17, 215, 254, 255, 255, 255, 255, 255, 204, 82, 13, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 13, 82, 204, 255, 255, 255, 255, 255, 254, 214, 17, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 151, 0, 0, 0, + 0, 28, 136, 238, 255, 222, 113, 13, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 100, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 100, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 113, 222, 255, + 238, 136, 28, 0, 0, 0, 0, 151, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 32, 252, 213, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 255, 92, 0, + 0, 0, 0, 0, 0, 37, 252, 217, 6, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 249, 222, + 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 49, 235, 254, 255, 255, 255, 255, 255, + 255, 220, 156, 110, 85, 83, 90, 46, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 46, + 90, 83, 85, 110, 156, 220, 255, 255, 255, 255, 255, 255, 254, 235, + 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 185, 0, + 0, 50, 164, 250, 255, 198, 85, 3, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 141, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 141, 40, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 85, 198, 255, 250, 164, 50, 0, 0, 185, 240, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 166, 255, 106, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 251, 223, + 5, 0, 0, 0, 0, 0, 0, 0, 155, 255, 134, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 193, + 255, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 236, 254, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 249, 120, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, + 120, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, + 235, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 255, 179, 193, 255, 252, 171, 57, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 57, 171, 252, 255, 193, 179, 255, 148, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 29, 241, 245, 49, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 216, + 255, 79, 0, 0, 0, 0, 0, 0, 0, 0, 18, 225, 253, 95, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 155, 255, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 215, + 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 236, 35, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 35, 236, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 254, 215, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 9, 156, 246, 235, 143, 33, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 143, 235, 246, 156, + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 254, 232, + 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, + 200, 255, 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, + 241, 253, 121, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 13, 169, 255, 211, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 16, 148, 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 253, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 49, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 254, 247, 148, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 111, 253, 245, 108, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 68, 225, 255, 168, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 53, 232, 255, 197, 62, 0, 0, 0, 0, 0, 0, 0, + 4, 93, 226, 255, 201, 21, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 45, 159, 243, 254, 254, 255, 255, 255, 255, + 255, 254, 208, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 20, 208, 254, 255, 255, 255, 255, 255, 254, + 254, 243, 159, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 85, 241, 255, 214, 106, 25, 0, 0, 0, 0, 13, + 84, 186, 255, 253, 133, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 25, 177, 255, 255, 214, 141, 94, 72, 75, + 103, 156, 233, 255, 250, 137, 7, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 89, 152, 200, 234, + 250, 253, 242, 188, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 47, 188, 242, 253, 250, 234, + 200, 152, 88, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 27, 163, 251, 255, 254, 227, 198, 195, + 217, 251, 255, 255, 194, 55, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 174, 248, 255, 255, + 255, 255, 255, 255, 237, 149, 36, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 117, 184, 229, + 251, 253, 237, 197, 136, 50, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, + 66, 105, 123, 120, 97, 53, 3, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 21, 177, 252, 255, 255, 230, 193, + 155, 100, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 255, 255, 255, 255, + 255, 255, 255, 255, 253, 214, 125, 33, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 248, 164, 51, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 249, + 149, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 19, 153, 242, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 230, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 22, 51, 92, 147, 211, 255, 255, + 255, 255, 255, 255, 255, 255, 250, 142, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 47, 143, 238, 255, 255, 255, 255, 255, 255, 253, 167, 10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 19, 140, 246, 255, 255, 255, 255, 255, 254, 180, + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 46, 200, 255, 255, 255, 255, + 255, 253, 158, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 40, + 56, 31, 21, 0, 0, 0, 0, 0, 0, 0, 0, 13, 147, 255, + 255, 255, 255, 255, 252, 117, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, + 210, 255, 255, 255, 255, 240, 196, 139, 63, 2, 0, 0, 0, 0, + 5, 128, 255, 255, 255, 255, 255, 245, 58, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 54, 102, 146, 180, 209, 229, 244, 251, 250, 244, 229, 208, 179, + 145, 101, 53, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 163, 255, 255, 255, 255, 255, 255, 255, 255, 255, 213, 91, 2, + 0, 0, 0, 4, 130, 255, 255, 255, 255, 255, 210, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, + 117, 190, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 245, 188, 115, 38, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 70, + 104, 126, 107, 83, 37, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 253, 183, 29, 0, 0, 0, 6, 167, 255, 255, 255, 255, 253, 120, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 239, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 239, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 115, + 209, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, 112, 19, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 144, + 242, 255, 255, 255, 255, 255, 255, 233, 165, 78, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 116, 252, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 220, 41, 0, 0, 0, 21, 217, 255, 255, 255, + 255, 232, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 239, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 239, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 160, + 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 247, 157, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, + 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 130, + 22, 0, 0, 0, 0, 0, 0, 11, 119, 204, 223, 229, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 207, 21, 0, 0, 0, 74, 255, + 255, 255, 255, 254, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 82, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 239, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 82, 3, 3, 3, 3, 3, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 173, + 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 253, 169, 40, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 14, 204, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 240, 131, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 48, 111, 210, 255, 255, 255, 255, 255, 254, 151, 0, 0, 0, + 5, 175, 255, 255, 255, 255, 222, 3, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 182, 182, 182, 182, 182, 182, + 205, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 250, 182, + 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 250, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 205, 182, 182, 182, + 182, 182, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 153, + 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 149, 18, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 169, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 227, 91, 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5, 96, 238, 255, 255, 255, 255, 240, 50, + 0, 0, 0, 64, 255, 255, 255, 255, 253, 69, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, + 237, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 247, 239, 239, 247, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 234, + 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 89, 252, 255, 255, 255, 255, 255, 221, 113, 59, 53, + 79, 127, 205, 252, 255, 255, 255, 255, 255, 255, 152, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 246, 255, 255, 255, + 255, 144, 0, 0, 0, 8, 198, 255, 255, 255, 255, 152, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, + 183, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 246, 193, 139, 95, 58, 28, 5, 0, 0, 0, 0, 6, 29, 59, + 97, 141, 194, 247, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 179, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 15, 228, 255, 255, 255, 255, 255, 225, 28, 0, + 0, 0, 0, 0, 0, 45, 154, 246, 255, 255, 252, 180, 36, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 149, 255, + 255, 255, 255, 222, 6, 0, 0, 0, 114, 255, 255, 255, 255, 220, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 251, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, + 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, + 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, + 237, 237, 237, 237, 237, 251, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 72, 234, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 228, + 144, 66, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 8, 69, 147, 230, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 231, 67, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 122, 254, 255, 255, 255, 255, 255, 109, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 114, 111, 41, 0, + 0, 0, 0, 3, 49, 89, 68, 17, 0, 0, 0, 0, 0, 0, + 45, 246, 255, 255, 255, 243, 57, 0, 0, 0, 37, 255, 255, 255, + 255, 253, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 212, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 205, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 126, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 173, + 69, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 73, 177, 253, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 119, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 7, 234, 255, 255, 255, 255, 255, + 255, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 60, 211, 255, 255, 255, 244, 135, 9, 0, 0, + 0, 0, 15, 200, 255, 255, 255, 252, 93, 0, 0, 0, 9, 219, + 255, 255, 255, 254, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 160, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 155, + 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 38, 162, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 155, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 254, 255, 255, 255, + 255, 255, 255, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 62, 235, 255, 255, 255, 255, 255, 255, 160, + 4, 0, 0, 0, 1, 162, 255, 255, 255, 255, 130, 0, 0, 0, + 5, 183, 255, 255, 255, 254, 110, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 87, 253, 255, 255, 255, 255, 255, 255, 255, 179, + 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 48, 186, 255, 255, 255, 255, 255, 255, 255, + 254, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 187, 255, 255, + 255, 255, 255, 255, 255, 73, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 10, 197, 255, 255, 255, 255, 255, 255, + 255, 247, 78, 0, 0, 0, 0, 131, 255, 255, 255, 255, 159, 0, + 0, 0, 0, 147, 255, 255, 255, 255, 126, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 113, 255, 255, 255, 255, 255, 229, + 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 232, 255, 255, 255, + 255, 255, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 252, + 255, 255, 255, 255, 255, 255, 255, 133, 0, 0, 0, 0, 0, 0, + 0, 10, 63, 95, 123, 152, 180, 208, 235, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 155, 0, 0, 0, 0, 98, 255, 255, 255, 255, + 167, 0, 0, 0, 0, 140, 255, 255, 255, 255, 158, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 255, 255, 255, + 174, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 17, 51, 65, 78, 81, 71, 52, 22, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 179, + 255, 255, 255, 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 78, 254, 255, 255, 255, 255, 255, 255, 255, 198, 11, 0, 0, 0, + 0, 0, 35, 219, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 183, 0, 0, 0, 0, 98, 255, 255, + 255, 255, 167, 0, 0, 0, 0, 112, 255, 255, 255, 255, 158, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 167, + 252, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 59, 130, 189, 235, 255, 255, 255, 255, 255, 255, 255, 255, 237, + 187, 128, 58, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 121, 253, 173, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 134, 255, 255, 255, 255, 255, 255, 255, 255, 251, 50, 0, + 0, 0, 0, 0, 116, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 167, 0, 0, 0, 0, 52, + 249, 255, 255, 254, 131, 0, 0, 0, 0, 63, 255, 255, 255, 254, + 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 7, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 40, 140, 226, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 229, 140, 38, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 64, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 174, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 160, 3, 0, 0, 0, 0, 110, 253, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 94, 0, 0, 0, + 0, 5, 120, 245, 253, 183, 19, 0, 0, 0, 0, 3, 134, 251, + 254, 173, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 53, 179, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 176, 50, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 198, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 245, 47, 0, 0, 0, 0, 16, 157, 220, 217, 191, 163, + 133, 106, 78, 124, 249, 255, 255, 255, 255, 255, 255, 191, 14, 0, + 0, 0, 0, 0, 0, 11, 23, 0, 0, 0, 0, 0, 0, 0, + 0, 12, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 171, 128, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 28, 168, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, + 164, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 222, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 171, 7, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 110, 243, 255, 255, 255, 255, 188, 30, + 0, 1, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 191, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 102, 241, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 237, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 222, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 251, 83, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 26, 217, 255, 255, 255, 111, + 4, 0, 0, 79, 210, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, + 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 15, 174, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 166, 10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 222, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 224, 28, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 231, 255, 255, + 246, 61, 0, 0, 17, 203, 255, 159, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 40, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 7, 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 254, 227, 190, 169, 157, 158, 170, 192, 229, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 205, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 198, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 167, + 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 245, + 255, 255, 233, 45, 0, 0, 66, 255, 255, 245, 74, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 40, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 108, 254, 255, 255, 255, 255, 255, 255, 255, + 255, 240, 160, 84, 21, 0, 0, 0, 0, 0, 0, 0, 0, 23, + 86, 166, 242, 255, 255, 255, 255, 255, 255, 255, 255, 255, 108, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 166, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 114, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 73, 255, 255, 255, 221, 29, 0, 0, 110, 255, 255, 255, 202, 11, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, 100, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 96, 0, 0, 0, 0, 0, 0, 0, + 52, 140, 140, 140, 140, 158, 255, 226, 140, 140, 140, 140, 136, 0, + 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 131, 255, 255, 255, 255, 255, + 255, 219, 104, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 12, 107, 222, 255, 255, 255, 255, 255, 255, 137, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 122, 254, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 246, 75, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 101, 255, 255, 255, 209, 13, 0, 0, 85, 252, 255, 255, + 250, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, 183, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 175, 0, 0, 0, 0, 0, + 0, 0, 96, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 247, 0, 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 255, 255, + 255, 238, 107, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 112, 241, 255, 255, 255, + 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 62, 253, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 237, 54, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 129, 255, 255, 255, 195, 0, 0, 0, 13, 191, + 255, 255, 255, 208, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, + 60, 84, 84, 84, 84, 84, 84, 84, 84, 84, 58, 0, 0, 0, + 0, 0, 0, 0, 31, 84, 84, 84, 84, 111, 255, 212, 84, 84, + 84, 84, 82, 0, 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 185, 255, 184, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 190, + 255, 186, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 7, 240, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 227, 51, 0, 0, + 0, 0, 0, 0, 0, 0, 161, 255, 255, 255, 166, 0, 0, 0, + 0, 78, 252, 255, 255, 249, 86, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 191, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 11, 108, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 112, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 227, + 52, 0, 0, 0, 0, 0, 0, 0, 105, 255, 255, 253, 104, 0, + 0, 0, 0, 13, 196, 255, 255, 255, 185, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, + 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, + 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 228, 54, 0, 0, 0, 0, 0, 0, 12, 125, 187, 121, + 10, 0, 0, 0, 0, 0, 109, 255, 255, 255, 235, 38, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 40, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 16, 91, 153, 193, 210, 209, 192, 152, 91, 15, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 202, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 236, 76, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 40, 244, 255, 255, 252, 101, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 40, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 41, 164, 248, 255, 255, 255, 255, 255, 255, 255, 255, 247, + 161, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 72, 252, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 247, 115, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 200, 255, 255, + 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 18, 115, 86, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 10, 144, 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 250, 138, 8, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 175, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 169, 22, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 179, + 255, 255, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 12, 211, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 206, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 235, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 219, 73, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 176, 255, 255, 255, 229, 15, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 123, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 123, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 251, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 252, 170, 38, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 16, 203, 255, 255, 255, 232, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 151, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 151, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 242, 145, 38, 0, 0, 0, + 0, 0, 0, 0, 84, 252, 255, 255, 255, 231, 21, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 179, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 173, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 181, 254, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 249, 184, + 103, 35, 2, 0, 18, 110, 239, 255, 255, 255, 255, 216, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 207, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 197, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 197, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 20, 201, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 251, 242, 255, 255, 255, 255, 255, 255, 255, 160, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 199, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 19, 216, 255, 255, 255, 255, 255, 255, 255, 255, 212, + 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 199, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 243, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 242, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, + 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, + 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, + 186, 186, 186, 186, 186, 240, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 34, 230, 255, 255, 255, 255, 255, 255, + 228, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 182, + 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 253, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 241, 255, 255, 255, + 255, 238, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 6, 140, 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 253, 164, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 249, + 255, 255, 248, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 77, 225, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 243, 113, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 93, 253, 253, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 18, 144, 247, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 250, 171, 37, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 116, 108, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 151, + 241, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 241, 162, 46, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 13, 96, 174, 238, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 232, 171, 93, 18, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 4, 42, 85, 121, 129, 153, 138, + 127, 111, 79, 34, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 67, 161, 65, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 22, 166, 252, 255, 255, 199, 91, 10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 37, 216, 255, 255, 255, 255, 255, 255, 238, 162, 91, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 34, 226, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 223, 184, 151, 129, 125, 117, 127, 133, 162, 196, 180, 33, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 194, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 223, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 110, 252, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 205, 21, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 6, 221, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 137, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 249, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 82, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 55, 118, 168, 208, 236, 253, 255, 253, 236, 208, 168, 118, + 55, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 43, 145, 232, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 232, 145, 43, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 192, 49, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 49, 179, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 254, 180, 49, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, 236, 255, + 230, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 15, 152, 253, 255, 255, 255, 255, 255, 255, 255, 255, 239, 218, + 212, 218, 239, 255, 255, 255, 255, 255, 255, 255, 255, 253, 153, 15, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 98, 243, + 255, 255, 255, 238, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 56, 224, 255, 255, 255, 255, 255, 255, 208, 130, 67, 18, + 0, 0, 0, 0, 0, 18, 67, 130, 208, 255, 255, 255, 255, 255, + 255, 224, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 117, + 248, 255, 255, 255, 255, 255, 244, 103, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 93, 249, 255, 255, 255, 255, 250, 158, 47, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 158, 250, + 255, 255, 255, 255, 249, 94, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, + 137, 252, 255, 255, 255, 255, 255, 255, 255, 249, 122, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 108, 254, 255, 255, 255, 255, 183, 38, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 37, 183, 255, 255, 255, 255, 254, 108, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 12, 158, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 142, + 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 97, 254, 255, 255, 255, 246, 102, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 101, 246, 255, 255, 255, 254, 98, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 18, 177, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 163, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 62, 250, 255, 255, 255, 233, 53, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 53, 232, 255, 255, 255, 250, + 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 27, 195, 255, 255, 255, 255, 255, 255, 255, 238, 255, 255, + 255, 255, 255, 255, 255, 182, 20, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 19, 228, 255, 255, 255, 232, 41, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 232, 255, + 255, 255, 229, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 39, 211, 255, 255, 255, 255, 255, 255, 253, 153, 26, + 168, 254, 255, 255, 255, 255, 255, 255, 199, 29, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 255, 255, 255, 245, + 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 52, 245, 255, 255, 255, 163, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 53, 181, 189, 189, 189, 189, 189, 189, 189, 189, 189, + 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, + 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, + 189, 189, 189, 189, 189, 189, 189, 189, 189, 162, 27, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 53, 224, 255, 255, 255, 255, 255, 255, 250, 134, + 6, 0, 9, 149, 252, 255, 255, 255, 255, 255, 255, 214, 42, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 254, 255, 255, + 255, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 97, 255, 255, 255, 254, 60, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 231, 254, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 158, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 70, 233, 255, 255, 255, 255, 255, 255, 246, + 114, 2, 0, 0, 0, 5, 129, 249, 255, 255, 255, 255, 255, 255, + 227, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 193, 255, + 255, 255, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 179, 255, 255, 255, 193, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 7, 245, 254, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 88, 242, 255, 255, 255, 255, 255, 255, + 241, 94, 0, 0, 0, 0, 0, 0, 2, 109, 245, 255, 255, 255, + 255, 255, 255, 236, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, + 255, 255, 255, 249, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 35, 249, 255, 255, 255, 56, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 217, 225, 225, 225, + 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, + 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, + 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, + 225, 199, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3, 107, 248, 255, 255, 255, 255, 255, + 255, 235, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 240, + 255, 255, 255, 255, 255, 255, 244, 92, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 158, 255, 255, 255, 154, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 10, 53, 70, 54, 10, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 154, 255, 255, + 255, 158, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 5, 127, 252, 255, 255, 255, 255, + 255, 255, 225, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 70, 232, 255, 255, 255, 255, 255, 255, 249, 112, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 6, 241, 255, 255, 255, 43, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 43, 170, 248, 255, 255, 255, 248, 171, + 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, + 255, 255, 255, 241, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 149, 255, 255, 255, 255, + 255, 255, 255, 213, 42, 0, 0, 0, 0, 0, 12, 85, 8, 0, + 0, 0, 0, 0, 53, 223, 255, 255, 255, 255, 255, 255, 253, 132, + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 67, 255, 255, 255, 206, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 105, 249, 255, 255, 255, 255, 255, + 255, 255, 250, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 206, 255, 255, 255, 67, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 171, 255, 255, 255, + 255, 255, 255, 255, 198, 30, 0, 0, 0, 0, 0, 19, 177, 254, + 164, 14, 0, 0, 0, 0, 0, 38, 210, 255, 255, 255, 255, 255, + 255, 255, 155, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 129, 255, 255, 255, 129, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 99, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 102, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 129, 255, 255, 255, 129, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 190, 255, 255, + 255, 255, 255, 255, 254, 182, 20, 0, 0, 0, 0, 0, 28, 195, + 255, 255, 255, 183, 21, 0, 0, 0, 0, 0, 27, 195, 255, 255, + 255, 255, 255, 255, 255, 176, 15, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 178, 255, 255, 255, 68, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 245, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 246, 32, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 68, 255, 255, 255, 178, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 207, 255, + 255, 255, 255, 255, 255, 253, 164, 12, 0, 0, 0, 0, 0, 39, + 210, 255, 255, 255, 255, 255, 200, 31, 0, 0, 0, 0, 0, 18, + 178, 254, 255, 255, 255, 255, 255, 255, 194, 23, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 215, 255, 255, 255, + 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 147, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 22, 255, 255, 255, 215, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 247, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 221, + 255, 255, 255, 255, 255, 255, 251, 144, 6, 0, 0, 0, 0, 0, + 54, 223, 255, 255, 255, 255, 255, 255, 255, 214, 44, 0, 0, 0, + 0, 0, 10, 159, 252, 255, 255, 255, 255, 255, 255, 211, 34, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, + 255, 246, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 227, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 255, 255, + 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 247, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, + 232, 255, 255, 255, 255, 255, 255, 248, 124, 2, 0, 0, 0, 0, + 0, 71, 233, 255, 255, 255, 255, 255, 255, 255, 255, 255, 226, 59, + 0, 0, 0, 0, 0, 5, 140, 250, 255, 255, 255, 255, 255, 255, + 224, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 71, 126, 143, 119, + 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 247, 38, 0, 0, 0, 0, 0, 0, 0, 1, + 60, 119, 143, 126, 71, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 77, 241, 255, 255, 255, 255, 255, 255, 245, 102, 0, 0, 0, 0, + 0, 1, 89, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 236, 76, 0, 0, 0, 0, 0, 1, 119, 248, 255, 255, 255, + 255, 255, 255, 234, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 225, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 225, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 95, 227, 254, 255, + 255, 254, 254, 207, 69, 0, 0, 0, 0, 0, 0, 0, 96, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 247, 38, 0, 0, 0, 0, 0, 0, + 70, 207, 254, 254, 255, 255, 254, 227, 88, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 96, 248, 255, 255, 255, 255, 255, 255, 239, 81, 0, 0, 0, + 0, 0, 3, 108, 247, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 243, 95, 1, 0, 0, 0, 0, 0, 97, 244, + 255, 255, 255, 255, 255, 255, 243, 82, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 247, 255, 255, 237, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 244, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 246, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 237, 255, 255, 247, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 154, 255, 254, + 255, 255, 255, 255, 255, 254, 251, 119, 0, 0, 0, 0, 0, 0, + 96, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 247, 38, 0, 0, 0, 0, + 0, 119, 253, 254, 255, 255, 255, 255, 255, 254, 255, 152, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 118, 253, 255, 255, 255, 255, 255, 255, 231, 63, 0, 0, + 0, 0, 0, 6, 127, 251, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 248, 114, 3, 0, 0, 0, 0, + 0, 77, 238, 255, 255, 255, 255, 255, 255, 250, 101, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 225, 255, 255, 254, 8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 180, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 184, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 254, 255, 255, 226, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, 254, + 254, 255, 255, 255, 255, 255, 255, 255, 255, 254, 85, 0, 0, 0, + 0, 0, 96, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 247, 38, 0, 0, + 0, 0, 88, 254, 255, 255, 255, 255, 255, 255, 255, 255, 254, 254, + 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 142, 255, 255, 255, 255, 255, 255, 255, 220, 46, 0, + 0, 0, 0, 0, 9, 148, 254, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 134, 7, 0, + 0, 0, 0, 0, 58, 229, 255, 255, 255, 255, 255, 255, 253, 124, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 193, 255, 255, 255, 48, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 80, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 47, 255, 255, 255, 194, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, + 247, 255, 255, 255, 254, 255, 255, 255, 254, 255, 255, 254, 227, 8, + 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 247, 38, + 0, 0, 0, 9, 230, 254, 255, 255, 254, 255, 255, 255, 254, 255, + 255, 255, 247, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 165, 255, 255, 255, 255, 255, 255, 255, 207, 32, + 0, 0, 0, 0, 0, 14, 169, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, + 155, 10, 0, 0, 0, 0, 0, 42, 218, 255, 255, 255, 255, 255, + 255, 255, 148, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 255, + 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 182, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 102, 255, 255, 255, 149, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 136, 254, 255, 255, 254, 229, 100, 51, 119, 250, 254, 255, 255, + 254, 97, 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 247, 38, 0, 0, 0, 99, 254, 255, 255, 254, 243, 116, 51, 100, + 229, 254, 255, 255, 254, 136, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 14, 185, 255, 255, 255, 255, 255, 255, 254, 191, + 22, 0, 0, 0, 0, 0, 22, 188, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 175, 16, 0, 0, 0, 0, 0, 29, 204, 255, 255, + 255, 255, 255, 255, 255, 170, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 255, + 255, 255, 174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 15, 201, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 16, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 255, + 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 206, 255, 255, 255, 250, 61, 0, 0, 0, 92, 255, + 255, 255, 254, 162, 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 247, 38, 0, 0, 0, 166, 254, 255, 255, 255, 92, 0, + 0, 0, 61, 250, 255, 255, 255, 203, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 23, 204, 255, 255, 255, 255, 255, 255, 253, + 174, 12, 0, 0, 0, 0, 0, 33, 205, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 194, 25, 0, 0, 0, 0, 0, 19, + 188, 254, 255, 255, 255, 255, 255, 255, 190, 16, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 22, 253, 255, 255, 245, 15, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 142, 249, 255, 255, 255, 255, 255, 249, 144, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 244, 255, + 255, 253, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 231, 255, 255, 255, 218, 8, 0, 0, 0, + 23, 246, 255, 255, 255, 181, 0, 0, 0, 0, 96, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 247, 38, 0, 0, 0, 188, 255, 255, 255, 245, + 22, 0, 0, 0, 8, 218, 255, 255, 255, 231, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 35, 219, 255, 255, 255, 255, 255, 255, + 252, 154, 6, 0, 0, 0, 0, 0, 46, 219, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 210, 37, 0, 0, 0, + 0, 0, 10, 170, 253, 255, 255, 255, 255, 255, 255, 208, 25, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 191, 255, 255, 255, 111, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 25, 107, 157, 174, 157, 108, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, + 255, 255, 255, 194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 228, 255, 255, 255, 231, 18, 0, + 0, 0, 32, 251, 255, 255, 255, 181, 0, 0, 0, 0, 96, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 247, 38, 0, 0, 0, 183, 255, 255, + 255, 250, 32, 0, 0, 0, 19, 232, 255, 255, 255, 224, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, 255, 255, 255, + 255, 251, 133, 2, 0, 0, 0, 0, 0, 61, 230, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 223, 50, + 0, 0, 0, 0, 0, 4, 150, 252, 255, 255, 255, 255, 255, 255, + 199, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 94, 255, 255, 255, 225, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 223, 255, 255, 255, 97, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 254, 255, 255, 254, + 133, 6, 0, 9, 173, 254, 255, 255, 254, 134, 0, 0, 0, 0, + 96, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 247, 38, 0, 0, 0, 136, + 254, 255, 255, 254, 173, 9, 0, 6, 133, 254, 255, 255, 254, 181, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 234, 255, 255, + 255, 255, 250, 109, 0, 0, 0, 0, 0, 0, 78, 239, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 234, 66, 0, 0, 0, 0, 0, 1, 127, 251, 255, 255, 255, + 255, 240, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 7, 229, 255, 255, 255, 122, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 119, 255, 255, 255, 231, 7, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 254, 255, + 255, 254, 254, 205, 170, 219, 254, 255, 255, 255, 254, 44, 0, 0, + 0, 0, 96, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 247, 38, 0, 0, + 0, 51, 254, 255, 255, 255, 254, 219, 170, 207, 254, 254, 255, 255, + 254, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, + 242, 255, 255, 245, 87, 0, 0, 0, 0, 0, 2, 97, 246, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 242, 84, 1, 0, 0, 0, 0, 0, 104, 249, + 255, 255, 248, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 255, 255, 255, + 249, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 49, 248, 255, 255, 255, 112, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 210, 254, 255, 255, 255, 255, 255, 254, 255, 255, 255, 254, 172, 0, + 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 247, 38, + 0, 0, 0, 0, 175, 254, 255, 255, 255, 254, 255, 255, 255, 255, + 255, 254, 210, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 73, 249, 238, 67, 0, 0, 0, 0, 0, 3, 117, 251, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 248, 104, 2, 0, 0, 0, 0, + 0, 82, 244, 252, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 215, + 255, 255, 255, 217, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 10, 215, 255, 255, 255, 217, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 46, 239, 254, 255, 255, 255, 255, 255, 255, 255, 254, 224, + 26, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 247, 38, 0, 0, 0, 0, 26, 224, 254, 255, 255, 255, 255, 255, + 255, 255, 254, 234, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 85, 49, 0, 0, 0, 0, 0, 5, 140, + 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 125, 4, 0, + 0, 0, 0, 0, 63, 101, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 49, 248, 255, 255, 255, 157, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 155, 255, 255, 255, 249, 51, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 54, 217, 254, 255, 255, 255, 255, 255, 254, + 198, 32, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 247, 38, 0, 0, 0, 0, 0, 31, 201, 254, 255, 255, + 255, 255, 255, 254, 217, 51, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 162, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 148, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 115, 255, 255, 255, 255, 86, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 84, 255, 255, 255, 255, 117, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 255, 255, 255, 255, + 255, 240, 10, 0, 0, 0, 0, 0, 0, 0, 58, 151, 151, 151, + 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, + 151, 151, 151, 151, 148, 22, 0, 0, 0, 0, 0, 0, 10, 240, + 255, 255, 255, 255, 255, 255, 37, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16, 183, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 169, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 185, 255, 255, 255, 239, 31, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 29, 238, 255, 255, 255, 186, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, 255, 255, + 255, 255, 255, 240, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 240, 255, 255, 255, 255, 255, 255, 36, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 25, 201, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 189, 19, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 233, 255, 255, 255, + 195, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 193, 255, 255, 255, + 234, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, + 255, 255, 255, 255, 255, 240, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 240, 255, 255, 255, 255, 255, 255, 36, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 37, 216, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, 28, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 254, + 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 255, 255, + 255, 254, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 36, 255, 255, 255, 255, 255, 255, 240, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 9, 240, 255, 255, 255, 255, 255, 255, 36, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 201, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 187, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 145, 255, 255, 255, 251, 59, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 251, + 255, 255, 255, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 36, 255, 255, 255, 255, 255, 255, 240, 9, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 9, 240, 255, 255, 255, 255, 255, 255, + 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 7, 212, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 197, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 208, 255, 255, 255, 224, 15, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, + 223, 255, 255, 255, 210, 8, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 36, 255, 255, 255, 255, 255, 255, 240, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 9, 240, 255, 255, 255, 255, + 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 7, 212, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 197, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 42, 245, 255, 255, 255, 168, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 166, 255, 255, 255, 246, 43, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 36, 255, 255, 255, 255, 255, 255, 240, + 9, 0, 0, 0, 0, 156, 254, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 243, 84, 0, 0, 0, 9, 240, 255, 255, + 255, 255, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 212, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 197, 5, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 255, 255, + 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 95, 255, 255, 255, 255, 106, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, 255, 255, 255, 255, + 255, 240, 9, 0, 0, 0, 43, 248, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 254, 201, 3, 0, 0, 9, 240, + 255, 255, 255, 255, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 212, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 197, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 175, 255, + 255, 255, 243, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 242, 255, 255, 255, 176, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, 255, 255, + 255, 255, 255, 240, 9, 0, 0, 0, 18, 229, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 161, 0, 0, 0, + 9, 240, 255, 255, 255, 255, 255, 255, 36, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, + 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 197, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 18, 228, 255, 255, 255, 204, 5, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 202, 255, 255, 255, 228, 19, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, + 255, 255, 255, 255, 255, 240, 9, 0, 0, 0, 0, 49, 137, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 125, 18, 0, + 0, 0, 9, 240, 255, 255, 255, 255, 255, 255, 36, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 7, 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 197, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 65, 253, 255, 255, 255, 138, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 136, 255, 255, 255, 253, 66, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 26, 254, 255, 255, 255, 255, 255, 235, 8, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 235, 255, 255, 255, 255, 255, 254, 23, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 197, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 134, 255, 255, 255, 253, 68, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 67, 253, 255, 255, 255, 135, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 212, 254, 255, 255, 255, 254, 170, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 176, 254, 255, 255, 255, 254, 212, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 7, 212, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 197, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 4, 201, 255, 255, 255, 229, + 20, 0, 0, 0, 0, 0, 0, 0, 20, 229, 255, 255, 255, 202, + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 61, 239, 254, 254, 255, 225, 38, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 228, 255, 254, 254, + 236, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 7, 212, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 197, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 241, 255, + 255, 255, 178, 0, 0, 0, 0, 0, 0, 0, 177, 255, 255, 255, + 242, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 31, 128, 161, 116, 17, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 116, + 161, 128, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 212, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 197, 5, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 93, 255, 255, 255, 255, 107, 0, 0, 0, 0, 0, 107, 255, 255, + 255, 255, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 212, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 197, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 164, 255, 255, 255, 246, 44, 0, 0, 0, 44, 246, + 255, 255, 255, 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, + 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 197, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 14, 221, 255, 255, 255, 211, 8, 0, 8, + 210, 255, 255, 255, 222, 14, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 7, 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 197, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 56, 251, 255, 255, 255, 148, + 0, 148, 255, 255, 255, 251, 57, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 212, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 197, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123, 255, 255, + 255, 254, 146, 254, 255, 255, 255, 124, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5, 192, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 176, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + 192, 255, 255, 255, 255, 255, 255, 255, 192, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 89, 253, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 251, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 28, 237, 255, 255, 255, 255, 255, 237, 29, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 89, 213, 253, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 252, 206, 79, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 82, 255, 255, 255, 255, 255, 83, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 154, 255, 255, 255, 154, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 180, 252, 180, + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; diff --git a/libpdraw-gles2hud/src/pdraw_gles2hud_instruments.cpp b/libpdraw-gles2hud/src/pdraw_gles2hud_instruments.cpp index 9051db0..477668f 100644 --- a/libpdraw-gles2hud/src/pdraw_gles2hud_instruments.cpp +++ b/libpdraw-gles2hud/src/pdraw_gles2hud_instruments.cpp @@ -1,1121 +1,1121 @@ -/** - * Parrot Drones Awesome Video Viewer - * OpenGL ES 2.0 HUD rendering library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "pdraw_gles2hud_priv.h" - - -void pdraw_gles2hud_draw_vumeter(struct pdraw_gles2hud *self, - float x, - float y, - float r, - float value, - float val_min, - float val_max, - float critical_min, - float critical_max, - const float color[4], - const float critical_color[4]) -{ - x *= self->ratio_w; - y *= self->ratio_h; - if (value < val_min) - value = val_min; - if (value > val_max) - value = val_max; - float span = 4. * M_PI / 3.; - float start = (M_PI - span) / 2.; - pdraw_gles2hud_draw_arc(self, - x, - y, - r * self->ratio_w, - r * self->ratio_w * self->aspect_ratio, - start, - span, - 20, - color, - 2.); - if ((critical_min >= val_min) && (critical_min <= val_max) && - (critical_max >= val_min) && (critical_max <= val_max) && - (critical_min < critical_max)) { - float start2 = start + (1. - (critical_max - val_min) / - (val_max - val_min)) * - span; - float end2 = start + (1. - (critical_min - val_min) / - (val_max - val_min)) * - span; - pdraw_gles2hud_draw_arc(self, - x, - y, - r * self->ratio_w * 0.9, - r * self->ratio_w * self->aspect_ratio * - 0.9, - start2, - end2 - start2, - 10, - critical_color, - 2.); - } - float angle = - start + (1. - (value - val_min) / (val_max - val_min)) * span; - float x1 = x + r * self->ratio_w * 0.4 * cosf(angle); - float y1 = - y + r * self->ratio_w * self->aspect_ratio * 0.4 * sinf(angle); - float x2 = x + r * self->ratio_w * 0.9 * cosf(angle); - float y2 = - y + r * self->ratio_w * self->aspect_ratio * 0.9 * sinf(angle); - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); -} - - -void pdraw_gles2hud_draw_artificial_horizon(struct pdraw_gles2hud *self, - const struct vmeta_euler *drone, - const struct vmeta_euler *frame, - const float color[4]) -{ - int i; - float x1, y1, x2, y2; - float height = self->config.central_zone_size * self->ratio_w * - self->aspect_ratio; - int steps = 6; - - /* Scale */ - for (i = -steps; i <= steps; i++) { - if (i != 0) { - if (i & 1) { - pdraw_gles2hud_draw_line(self, - -0.01 * self->ratio_w, - i * height / 2 / steps, - 0.01 * self->ratio_w, - i * height / 2 / steps, - color, - 2.); - } - } - } - - /* Horizon */ - x1 = -0.5 * self->config.central_zone_size * self->ratio_w * - cosf(frame->phi); - y1 = -0.5 * self->config.central_zone_size * self->ratio_w * - self->aspect_ratio * sinf(frame->phi); - x2 = 0.5 * self->config.central_zone_size * self->ratio_w * - cosf(frame->phi); - y2 = 0.5 * self->config.central_zone_size * self->ratio_w * - self->aspect_ratio * sinf(frame->phi); - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); - - /* Drone */ - float vertices[10]; - float drone_y = drone->theta / (M_PI / 18 * steps) * height / 2; - vertices[0] = -0.06 * self->ratio_w * cosf(frame->phi - drone->phi); - vertices[1] = -0.06 * self->ratio_w * self->aspect_ratio * - sinf(frame->phi - drone->phi) + - drone_y; - vertices[2] = -0.015 * self->ratio_w * cosf(frame->phi - drone->phi); - vertices[3] = -0.015 * self->ratio_w * self->aspect_ratio * - sinf(frame->phi - drone->phi) + - drone_y; - vertices[4] = 0.015 * self->ratio_w * sinf(frame->phi - drone->phi); - vertices[5] = -0.015 * self->ratio_w * self->aspect_ratio * - cosf(frame->phi - drone->phi) + - drone_y; - vertices[6] = 0.015 * self->ratio_w * cosf(frame->phi - drone->phi); - vertices[7] = 0.015 * self->ratio_w * self->aspect_ratio * - sinf(frame->phi - drone->phi) + - drone_y; - vertices[8] = 0.06 * self->ratio_w * cosf(frame->phi - drone->phi); - vertices[9] = 0.06 * self->ratio_w * self->aspect_ratio * - sinf(frame->phi - drone->phi) + - drone_y; - GLCHK(glLineWidth(6.)); - GLCHK(glVertexAttribPointer( - self->position_handle, 2, GL_FLOAT, false, 0, vertices)); - GLCHK(glUniform4fv(self->color_handle, 1, color)); - GLCHK(glDrawArrays(GL_LINE_STRIP, 0, 5)); -} - - -void pdraw_gles2hud_draw_roll(struct pdraw_gles2hud *self, - float drone_roll, - const float color[4]) -{ - int i; - float rotation, x1, y1, x2, y2; - float width = 0.12 * self->ratio_w; - float y_offset = self->config.roll_zone_v_offset * self->ratio_h; - int steps = 6; - - pdraw_gles2hud_draw_arc(self, - 0., - y_offset, - width, - width * self->aspect_ratio, - M_PI * (90. - 10. * steps) / 180., - M_PI * 20. * steps / 180., - 100, - color, - 2.); - rotation = M_PI / 2. - drone_roll; - x1 = (width - 0.012 * self->ratio_w) * cosf(rotation); - y1 = (width - 0.012 * self->ratio_w) * self->aspect_ratio * - sinf(rotation) + - y_offset; - x2 = (width + 0.012 * self->ratio_w) * cosf(rotation); - y2 = (width + 0.012 * self->ratio_w) * self->aspect_ratio * - sinf(rotation) + - y_offset; - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); - - for (i = -steps, rotation = M_PI * (90. - 10. * steps) / 180.; - i <= steps; - i++, rotation += M_PI * 10. / 180.) { - int angle = (i * 10 + 60 + 360) % 360; - if (angle <= 120) { - x1 = width * cosf(rotation); - y1 = width * self->aspect_ratio * sinf(rotation) + - y_offset; - x2 = (width - 0.008 * self->ratio_w) * cosf(rotation); - y2 = (width - 0.008 * self->ratio_w) * - self->aspect_ratio * sinf(rotation) + - y_offset; - pdraw_gles2hud_draw_line( - self, x1, y1, x2, y2, color, 2.); - } - } - - x1 = 0.; - y1 = y_offset; - x2 = 0.; - y2 = y_offset; - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); -} - - -void pdraw_gles2hud_draw_heading(struct pdraw_gles2hud *self, - float drone_yaw, - float horizontal_speed, - float speed_psi, - const float color[4]) -{ - int i; - int heading = ((int)(drone_yaw * RAD_TO_DEG) + 360) % 360; - float rotation, x1, y1, x2, y2; - char heading_str[20]; - snprintf(heading_str, sizeof(heading_str), "%d", heading); - - float width = 0.12 * self->ratio_w; - float y_offset = self->config.heading_zone_v_offset * self->ratio_h; - - pdraw_gles2hud_draw_arc(self, - 0., - y_offset, - width, - width * self->aspect_ratio, - M_PI * 20. / 180., - M_PI * 140. / 180., - 100, - color, - 2.); - x1 = 0.; - y1 = y_offset + width * self->aspect_ratio; - x2 = 0.; - y2 = y_offset + (width + 0.01 * self->ratio_w) * self->aspect_ratio; - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); - - for (i = 0, rotation = drone_yaw + M_PI / 2.; i < 36; - i++, rotation += M_PI * 10. / 180.) { - int angle = (heading + i * 10 + 70 + 360) % 360; - if (angle <= 140) { - x1 = width * cosf(rotation); - y1 = width * self->aspect_ratio * sinf(rotation) + - y_offset; - x2 = (width - 0.01 * self->ratio_w) * cosf(rotation); - y2 = (width - 0.01 * self->ratio_w) * - self->aspect_ratio * sinf(rotation) + - y_offset; - pdraw_gles2hud_draw_line( - self, x1, y1, x2, y2, color, 2.); - } - } - - if (horizontal_speed >= 0.2) { - rotation = drone_yaw - speed_psi + M_PI / 2.; - x1 = 0.045 * self->ratio_w * cosf(rotation); - y1 = 0.045 * self->ratio_w * self->aspect_ratio * - sinf(rotation) + - y_offset; - x2 = 0.020 * self->ratio_w * cosf(rotation); - y2 = 0.020 * self->ratio_w * self->aspect_ratio * - sinf(rotation) + - y_offset; - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); - x1 = 0.045 * self->ratio_w * cosf(rotation); - y1 = 0.045 * self->ratio_w * self->aspect_ratio * - sinf(rotation) + - y_offset; - x2 = 0.010 * self->ratio_w * cosf(rotation - 5. * M_PI / 6.) + - x1; - y2 = 0.010 * self->ratio_w * self->aspect_ratio * - sinf(rotation - 5. * M_PI / 6.) + - y1; - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); - x1 = 0.045 * self->ratio_w * cosf(rotation); - y1 = 0.045 * self->ratio_w * self->aspect_ratio * - sinf(rotation) + - y_offset; - x2 = 0.010 * self->ratio_w * cosf(rotation + 5. * M_PI / 6.) + - x1; - y2 = 0.010 * self->ratio_w * self->aspect_ratio * - sinf(rotation + 5. * M_PI / 6.) + - y1; - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); - } -} - - -void pdraw_gles2hud_draw_altitude(struct pdraw_gles2hud *self, - double altitude, - float ground_distance, - float down_speed, - const float color[4]) -{ - char altitude_str[20]; - snprintf(altitude_str, sizeof(altitude_str), "%.1fm", altitude); - - float x_offset = self->config.central_zone_size * self->ratio_w; - float height = self->config.central_zone_size * self->ratio_w * - self->aspect_ratio; - float altitude_interval = height / 20.; - - pdraw_gles2hud_draw_line( - self, x_offset, -height / 2., x_offset, height / 2., color, 2.); - pdraw_gles2hud_draw_line(self, - x_offset, - -height / 2., - x_offset + 0.08 * self->ratio_w, - -height / 2., - color, - 2.); - pdraw_gles2hud_draw_line(self, - x_offset, - height / 2., - x_offset + 0.08 * self->ratio_w, - height / 2., - color, - 2.); - pdraw_gles2hud_draw_rect(self, - x_offset + 0.03 * self->ratio_w, - -0.017 * self->ratio_w * self->aspect_ratio, - x_offset + 0.03 * self->ratio_w + - 0.1 * self->ratio_w, - 0.017 * self->ratio_w * self->aspect_ratio, - color, - 2.); - pdraw_gles2hud_draw_line(self, - x_offset, - 0., - x_offset + 0.03 * self->ratio_w, - -0.017 * self->ratio_w * self->aspect_ratio, - color, - 2.); - pdraw_gles2hud_draw_line(self, - x_offset, - 0., - x_offset + 0.03 * self->ratio_w, - 0.017 * self->ratio_w * self->aspect_ratio, - color, - 2.); - - float y = (ceil(altitude) - altitude) * altitude_interval; - int alt_int = ((int)ceil(altitude)); - int alt_mod5 = alt_int % 5; - while (y < height / 2.) { - pdraw_gles2hud_draw_line( - self, - x_offset, - y, - x_offset + ((alt_mod5 == 0) ? 0.03 * self->ratio_w - : 0.01 * self->ratio_w), - y, - color, - 2.); - if ((!alt_mod5) && (y > -height / 2. + 0.03 * self->ratio_w) && - (y < height / 2. - 0.03 * self->ratio_w) && - (!((y > -0.03 * self->ratio_w) && - (y < 0.03 * self->ratio_w)))) { - snprintf(altitude_str, - sizeof(altitude_str), - "%d", - alt_int); - /* drawText(altitude_str); */ - } - y += altitude_interval; - alt_int++; - alt_mod5 = alt_int % 5; - } - y = -(altitude - floor(altitude)) * altitude_interval; - alt_int = ((int)floor(altitude)); - alt_mod5 = alt_int % 5; - while (y > -height / 2.) { - pdraw_gles2hud_draw_line( - self, - x_offset, - y, - x_offset + ((alt_mod5 == 0) ? 0.03 * self->ratio_w - : 0.01 * self->ratio_w), - y, - color, - 2.); - if ((!alt_mod5) && - (y > -height / 2. + - 0.017 * self->ratio_w * self->aspect_ratio) && - (y < height / 2. - - 0.017 * self->ratio_w * self->aspect_ratio) && - (!((y > -0.017 * self->ratio_w * self->aspect_ratio) && - (y < 0.017 * self->ratio_w * self->aspect_ratio)))) { - snprintf(altitude_str, - sizeof(altitude_str), - "%d", - alt_int); - /* drawText(altitude_str); */ - } - y -= altitude_interval; - alt_int--; - alt_mod5 = alt_int % 5; - } - - /* Ground distance */ - y = -ground_distance * height / 20.; - if ((y < height / 2.) && - (y > -height / 2. + 0.01 * self->ratio_w * self->aspect_ratio)) { - if ((y > -0.012 * self->ratio_w * self->aspect_ratio) && - (y < 0.022 * self->ratio_w * self->aspect_ratio)) { - pdraw_gles2hud_draw_line(self, - x_offset, - y, - x_offset + - 0.03 * self->ratio_w, - y, - color, - 2.); - } else { - pdraw_gles2hud_draw_line(self, - x_offset, - y, - x_offset + - 0.06 * self->ratio_w, - y, - color, - 2.); - pdraw_gles2hud_draw_line( - self, - x_offset + 0.03 * self->ratio_w, - y, - x_offset + 0.04 * self->ratio_w, - y - 0.01 * self->ratio_w * self->aspect_ratio, - color, - 2.); - pdraw_gles2hud_draw_line( - self, - x_offset + 0.04 * self->ratio_w, - y, - x_offset + 0.05 * self->ratio_w, - y - 0.01 * self->ratio_w * self->aspect_ratio, - color, - 2.); - pdraw_gles2hud_draw_line( - self, - x_offset + 0.05 * self->ratio_w, - y, - x_offset + 0.06 * self->ratio_w, - y - 0.01 * self->ratio_w * self->aspect_ratio, - color, - 2.); - } - pdraw_gles2hud_draw_line(self, - x_offset, - y, - x_offset + 0.01 * self->ratio_w, - y - 0.01 * self->ratio_w * - self->aspect_ratio, - color, - 2.); - pdraw_gles2hud_draw_line(self, - x_offset + 0.01 * self->ratio_w, - y, - x_offset + 0.02 * self->ratio_w, - y - 0.01 * self->ratio_w * - self->aspect_ratio, - color, - 2.); - pdraw_gles2hud_draw_line(self, - x_offset + 0.02 * self->ratio_w, - y, - x_offset + 0.03 * self->ratio_w, - y - 0.01 * self->ratio_w * - self->aspect_ratio, - color, - 2.); - } - - /* Speed indication */ - if (fabs(down_speed) >= 0.2) { - float x1, y1, x2, y2; - x1 = x_offset + 0.15 * self->ratio_w; - y1 = -0.017 * self->ratio_w * self->aspect_ratio; - x2 = x_offset + 0.15 * self->ratio_w; - y2 = 0.017 * self->ratio_w * self->aspect_ratio; - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); - if (down_speed < 0.) { - x1 = x_offset + 0.15 * self->ratio_w; - y1 = 0.017 * self->ratio_w * self->aspect_ratio; - x2 = x_offset + 0.15 * self->ratio_w - - 0.0056 * self->ratio_w; - y2 = 0.017 * self->ratio_w * self->aspect_ratio - - 0.0098 * self->ratio_w * self->aspect_ratio; - pdraw_gles2hud_draw_line( - self, x1, y1, x2, y2, color, 2.); - x1 = x_offset + 0.15 * self->ratio_w; - y1 = 0.017 * self->ratio_w * self->aspect_ratio; - x2 = x_offset + 0.15 * self->ratio_w + - 0.0056 * self->ratio_w; - y2 = 0.017 * self->ratio_w * self->aspect_ratio - - 0.0098 * self->ratio_w * self->aspect_ratio; - pdraw_gles2hud_draw_line( - self, x1, y1, x2, y2, color, 2.); - } else { - x1 = x_offset + 0.15 * self->ratio_w; - y1 = -0.017 * self->ratio_w * self->aspect_ratio; - x2 = x_offset + 0.15 * self->ratio_w - - 0.0056 * self->ratio_w; - y2 = -0.017 * self->ratio_w * self->aspect_ratio + - 0.0098 * self->ratio_w * self->aspect_ratio; - pdraw_gles2hud_draw_line( - self, x1, y1, x2, y2, color, 2.); - x1 = x_offset + 0.15 * self->ratio_w; - y1 = -0.017 * self->ratio_w * self->aspect_ratio; - x2 = x_offset + 0.15 * self->ratio_w + - 0.0056 * self->ratio_w; - y2 = -0.017 * self->ratio_w * self->aspect_ratio + - 0.0098 * self->ratio_w * self->aspect_ratio; - pdraw_gles2hud_draw_line( - self, x1, y1, x2, y2, color, 2.); - } - } -} - - -void pdraw_gles2hud_draw_speed(struct pdraw_gles2hud *self, - float horizontal_speed, - const float color[4]) -{ - char speed_str[20]; - snprintf(speed_str, sizeof(speed_str), "%.1fm/s", horizontal_speed); - - float x_offset = -self->config.central_zone_size * self->ratio_w; - float height = self->config.central_zone_size * self->ratio_w * - self->aspect_ratio; - float speed_interval = height / 20.; - - pdraw_gles2hud_draw_line( - self, x_offset, -height / 2., x_offset, height / 2., color, 2.); - pdraw_gles2hud_draw_line(self, - x_offset, - -height / 2., - x_offset - 0.08 * self->ratio_w, - -height / 2., - color, - 2.); - pdraw_gles2hud_draw_line(self, - x_offset, - height / 2., - x_offset - 0.08 * self->ratio_w, - height / 2., - color, - 2.); - pdraw_gles2hud_draw_rect(self, - x_offset - 0.03 * self->ratio_w, - -0.017 * self->ratio_w * self->aspect_ratio, - x_offset - 0.03 * self->ratio_w - - 0.1 * self->ratio_w, - 0.017 * self->ratio_w * self->aspect_ratio, - color, - 2.); - pdraw_gles2hud_draw_line(self, - x_offset, - 0., - x_offset - 0.03 * self->ratio_w, - -0.017 * self->ratio_w * self->aspect_ratio, - color, - 2.); - pdraw_gles2hud_draw_line(self, - x_offset, - 0., - x_offset - 0.03 * self->ratio_w, - 0.017 * self->ratio_w * self->aspect_ratio, - color, - 2.); - - float y = (ceil(horizontal_speed) - horizontal_speed) * speed_interval; - int spd_int = ((int)ceil(horizontal_speed)); - int spd_mod5 = spd_int % 5; - while (y < height / 2.) { - pdraw_gles2hud_draw_line( - self, - x_offset, - y, - x_offset - ((spd_mod5 == 0) ? 0.03 * self->ratio_w - : 0.01 * self->ratio_w), - y, - color, - 2.); - if ((!spd_mod5) && - (y > -height / 2. + - 0.017 * self->ratio_w * self->aspect_ratio) && - (y < height / 2. - - 0.017 * self->ratio_w * self->aspect_ratio) && - (!((y > -0.017 * self->ratio_w * self->aspect_ratio) && - (y < 0.017 * self->ratio_w * self->aspect_ratio)))) { - snprintf(speed_str, sizeof(speed_str), "%d", spd_int); - /* drawText(strAltitude); */ - } - y += speed_interval; - spd_int++; - spd_mod5 = spd_int % 5; - } - y = -(horizontal_speed - floor(horizontal_speed)) * speed_interval; - spd_int = ((int)floor(horizontal_speed)); - spd_mod5 = spd_int % 5; - while (y > -height / 2.) { - pdraw_gles2hud_draw_line( - self, - x_offset, - y, - x_offset - ((spd_mod5 == 0) ? 0.03 * self->ratio_w - : 0.01 * self->ratio_w), - y, - color, - 2.); - if ((!spd_mod5) && - (y > -height / 2. + - 0.017 * self->ratio_w * self->aspect_ratio) && - (y < height / 2. - - 0.017 * self->ratio_w * self->aspect_ratio) && - (!((y > -0.017 * self->ratio_w * self->aspect_ratio) && - (y < 0.017 * self->ratio_w * self->aspect_ratio)))) { - snprintf(speed_str, sizeof(speed_str), "%d", spd_int); - /* drawText(strAltitude); */ - } - y -= speed_interval; - spd_int--; - spd_mod5 = spd_int % 5; - } -} - - -void pdraw_gles2hud_draw_controller_radar(struct pdraw_gles2hud *self, - double distance, - double bearing, - float controller_yaw, - float drone_yaw, - float controller_radar_angle, - const float color[4]) -{ - float width = 0.08 * self->ratio_w; - float x_offset = self->config.radar_zone_h_offset * self->ratio_w; - float y_offset = self->config.radar_zone_v_offset * self->ratio_h; - float x1, y1, x2, y2; - - pdraw_gles2hud_draw_ellipse(self, - x_offset, - y_offset, - width, - width * self->aspect_ratio, - 100, - color, - 2.); - x1 = x_offset; - y1 = y_offset + width * self->aspect_ratio; - x2 = x_offset; - y2 = y_offset + (width + 0.008 * self->ratio_w) * self->aspect_ratio; - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); - - if (distance > 50.) { - x1 = x_offset - width / 3. * sinf(controller_radar_angle / 2.); - y1 = y_offset + width / 3. * cosf(controller_radar_angle / 2.) * - self->aspect_ratio; - x2 = x_offset - width * sinf(controller_radar_angle / 2.); - y2 = y_offset + width * cosf(controller_radar_angle / 2.) * - self->aspect_ratio; - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); - x1 = x_offset + width / 3. * sinf(controller_radar_angle / 2.); - y1 = y_offset + width / 3. * cosf(controller_radar_angle / 2.) * - self->aspect_ratio; - x2 = x_offset + width * sinf(controller_radar_angle / 2.); - y2 = y_offset + width * cosf(controller_radar_angle / 2.) * - self->aspect_ratio; - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); - } -} - - -void pdraw_gles2hud_draw_record_timeline(struct pdraw_gles2hud *self, - uint64_t current_time, - uint64_t duration, - const float color[4]) -{ - float x_offset = self->config.right_zone_h_offset * self->ratio_w; - float y_offset = self->config.roll_zone_v_offset * self->ratio_h + - 0.12 * self->ratio_w * self->aspect_ratio; - float width = 0.4 * self->ratio_w; - float height = 0.015 * self->ratio_w * self->aspect_ratio; - float x1, y1, x2, y2; - float cw = 0., rw = 0.; - - uint64_t remaining_time = duration - current_time; - unsigned int c_hrs = 0, c_min = 0, c_sec = 0, c_msec = 0; - unsigned int r_hrs = 0, r_min = 0, r_sec = 0, r_msec = 0; - unsigned int d_hrs = 0, d_min = 0, d_sec = 0, d_msec = 0; - pdraw_gles2hud_friendly_time_from_us( - current_time, &c_hrs, &c_min, &c_sec, &c_msec); - pdraw_gles2hud_friendly_time_from_us( - remaining_time, &r_hrs, &r_min, &r_sec, &r_msec); - pdraw_gles2hud_friendly_time_from_us( - duration, &d_hrs, &d_min, &d_sec, &d_msec); - char str[20]; - if (d_hrs) { - snprintf(str, - sizeof(str), - "+%02d:%02d:%02d.%03d", - c_hrs, - c_min, - c_sec, - c_msec); - } else { - snprintf(str, - sizeof(str), - "+%02d:%02d.%03d", - c_min, - c_sec, - c_msec); - } - pdraw_gles2hud_get_text_dimensions(self, - str, - 0.15 * self->ratio_w, - 1., - self->aspect_ratio, - &cw, - nullptr); - cw += 0.012; - if (d_hrs) { - snprintf(str, - sizeof(str), - "-%02d:%02d:%02d.%03d", - r_hrs, - r_min, - r_sec, - r_msec); - } else { - snprintf(str, - sizeof(str), - "-%02d:%02d.%03d", - r_min, - r_sec, - r_msec); - } - pdraw_gles2hud_get_text_dimensions(self, - str, - 0.15 * self->ratio_w, - 1., - self->aspect_ratio, - &rw, - nullptr); - rw += 0.01; - - x1 = x_offset - rw; - y1 = y2 = y_offset; - x2 = x_offset - width + cw; - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); - x1 = x2 = x_offset - rw; - y1 = y_offset + height / 2.; - y2 = y_offset - height / 2.; - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); - x1 = x2 = x_offset - width + cw; - y1 = y_offset + height / 2.; - y2 = y_offset - height / 2.; - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); - x1 = x2 = x_offset - rw - - (1. - (float)current_time / (float)duration) * - (width - rw - cw); - y1 = y_offset + height / 2.; - y2 = y_offset - height / 2.; - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); -} - - -void pdraw_gles2hud_draw_recording_status(struct pdraw_gles2hud *self, - uint64_t recording_duration, - const float color[4]) -{ - float x_offset = self->config.right_zone_h_offset * self->ratio_w; - float y_offset = self->config.roll_zone_v_offset * self->ratio_h + - 0.12 * self->ratio_w * self->aspect_ratio; - float rec_size = 0.008 * self->ratio_w; - float w = 0.; - - if (recording_duration > 0) { - unsigned int d_hrs = 0, d_min = 0, d_sec = 0, d_msec = 0; - pdraw_gles2hud_friendly_time_from_us( - recording_duration, &d_hrs, &d_min, &d_sec, &d_msec); - char str[20]; - if (d_hrs) { - snprintf(str, - sizeof(str), - "REC %02d:%02d:%02d", - d_hrs, - d_min, - d_sec); - } else { - snprintf(str, - sizeof(str), - "REC %02d:%02d", - d_min, - d_sec); - } - pdraw_gles2hud_get_text_dimensions(self, - str, - 0.15 * self->ratio_w, - 1., - self->aspect_ratio, - &w, - nullptr); - w += 0.02; - pdraw_gles2hud_draw_filled_ellipse(self, - x_offset - w - rec_size / 2., - y_offset, - rec_size, - rec_size * - self->aspect_ratio, - 30, - color); - } else { - pdraw_gles2hud_get_text_dimensions(self, - "REC", - 0.15 * self->ratio_w, - 1., - self->aspect_ratio, - &w, - nullptr); - w += 0.03; - pdraw_gles2hud_draw_ellipse(self, - x_offset - w - rec_size / 2., - y_offset, - rec_size, - rec_size * self->aspect_ratio, - 30, - color, - 2.); - float x1, y1, x2, y2; - x1 = x_offset + 0.008 * self->ratio_w; - y1 = y_offset + rec_size * self->aspect_ratio + - 0.008 * self->ratio_w * self->aspect_ratio; - x2 = x1 - w - rec_size - 0.016 * self->ratio_w; - y2 = y1 - rec_size * 2. * self->aspect_ratio - - 0.016 * self->ratio_w * self->aspect_ratio; - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); - pdraw_gles2hud_draw_line(self, x1, y2, x2, y1, color, 2.); - } -} - - -void pdraw_gles2hud_draw_flight_path_vector(struct pdraw_gles2hud *self, - const struct vmeta_euler *frame, - float speed_theta, - float speed_psi, - const float color[4]) -{ - float x = (speed_psi - frame->psi) / self->h_fov * 2. * self->ratio_w; - float y = - (speed_theta - frame->theta) / self->v_fov * 2. * self->ratio_h; - float x1, y1, x2, y2, tx, ty; - float rotation = frame->phi; - - if ((x > -self->ratio_w) && (x < self->ratio_w) && - (y > -self->ratio_h) && (y < self->ratio_h)) { - pdraw_gles2hud_draw_ellipse(self, - x, - y, - 0.02 * self->ratio_w, - 0.02 * self->ratio_w * - self->aspect_ratio, - 40, - color, - 2.); - tx = 0.; - ty = 0.02; - x1 = x + (tx * cosf(rotation) - ty * sinf(rotation)) * - self->ratio_w; - y1 = y + (tx * sinf(rotation) + ty * cosf(rotation)) * - self->ratio_w * self->aspect_ratio; - tx = 0.; - ty = 0.03; - x2 = x + (tx * cosf(rotation) - ty * sinf(rotation)) * - self->ratio_w; - y2 = y + (tx * sinf(rotation) + ty * cosf(rotation)) * - self->ratio_w * self->aspect_ratio; - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); - tx = -0.02; - ty = 0.; - x1 = x + (tx * cosf(rotation) - ty * sinf(rotation)) * - self->ratio_w; - y1 = y + (tx * sinf(rotation) + ty * cosf(rotation)) * - self->ratio_w * self->aspect_ratio; - tx = -0.03; - ty = 0.; - x2 = x + (tx * cosf(rotation) - ty * sinf(rotation)) * - self->ratio_w; - y2 = y + (tx * sinf(rotation) + ty * cosf(rotation)) * - self->ratio_w * self->aspect_ratio; - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); - tx = 0.02; - ty = 0.; - x1 = x + (tx * cosf(rotation) - ty * sinf(rotation)) * - self->ratio_w; - y1 = y + (tx * sinf(rotation) + ty * cosf(rotation)) * - self->ratio_w * self->aspect_ratio; - tx = 0.03; - ty = 0.; - x2 = x + (tx * cosf(rotation) - ty * sinf(rotation)) * - self->ratio_w; - y2 = y + (tx * sinf(rotation) + ty * cosf(rotation)) * - self->ratio_w * self->aspect_ratio; - pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); - } -} - - -void pdraw_gles2hud_draw_framing_grid(struct pdraw_gles2hud *self, - const struct pdraw_rect *render_pos, - const struct pdraw_rect *content_pos, - const float color[4]) -{ - float x_left, x_right, x_third, y_top, y_bottom, y_third; - - x_left = (float)content_pos->x / (float)render_pos->width * 2. - 1.; - x_right = ((float)(content_pos->x + content_pos->width) + 0.5) / - (float)render_pos->width * 2. - - 1.; - x_third = (x_right - x_left) / 3.; - y_top = 1. - (float)content_pos->y / (float)render_pos->height * 2.; - y_bottom = 1. - ((float)(content_pos->y + content_pos->height) + 0.5) / - (float)render_pos->height * 2.; - y_third = (y_top - y_bottom) / 3.; - - if (x_left != -1.) { - pdraw_gles2hud_draw_line( - self, x_left, y_top, x_left, y_bottom, color, 1.); - } - pdraw_gles2hud_draw_line(self, - x_left + x_third, - y_top, - x_left + x_third, - y_bottom, - color, - 1.); - pdraw_gles2hud_draw_line(self, - x_right - x_third, - y_top, - x_right - x_third, - y_bottom, - color, - 1.); - if (x_right != 1.) { - pdraw_gles2hud_draw_line( - self, x_right, y_top, x_right, y_bottom, color, 1.); - } - if (y_top != 1.) { - pdraw_gles2hud_draw_line( - self, x_left, y_top, x_right, y_top, color, 1.); - } - pdraw_gles2hud_draw_line(self, - x_left, - y_top - y_third, - x_right, - y_top - y_third, - color, - 1.); - pdraw_gles2hud_draw_line(self, - x_left, - y_bottom + y_third, - x_right, - y_bottom + y_third, - color, - 1.); - if (y_bottom != -1.) { - pdraw_gles2hud_draw_line( - self, x_left, y_bottom, x_right, y_bottom, color, 1.); - } -} - - -void pdraw_gles2hud_draw_histograms( - struct pdraw_gles2hud *self, - const struct pdraw_video_frame_extra *frame_extra) -{ - unsigned int i, j, k; - float color_background[4] = {0.0f, 0.0f, 0.0f, 0.2f}; - float color_white[4] = {1.0f, 1.0f, 1.0f, 0.4f}; - float color[3][4] = { - {1.0f, 0.0f, 0.0f, 1.0f}, - {0.0f, 1.0f, 0.0f, 1.0f}, - {0.0f, 0.0f, 1.0f, 1.0f}, - }; - float cur_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; - float width = 0.333f * 2.0f * self->ratio_w; - float height = 0.333f * self->ratio_h; - float offset_x = (1.0f - 0.166f) * self->ratio_w - width; - float offset_y1 = (1.0f - 0.166f) * self->ratio_h - height; - float offset_y2 = (1.0f - 0.166f) * self->ratio_h - height * 2. - 0.04f; - float x, y1, y2, bin_width; - float val[3], prev_val; - int idx[3]; - - if (frame_extra->histogram[PDRAW_HISTOGRAM_CHANNEL_LUMA]) { - pdraw_gles2hud_draw_filled_rect(self, - offset_x, - offset_y1, - offset_x + width, - offset_y1 + height, - color_background); - bin_width = - width / - frame_extra - ->histogram_len[PDRAW_HISTOGRAM_CHANNEL_LUMA]; - for (i = 0; - i < - frame_extra->histogram_len[PDRAW_HISTOGRAM_CHANNEL_LUMA]; - i++) { - y1 = offset_y1 + - frame_extra->histogram - [PDRAW_HISTOGRAM_CHANNEL_LUMA][i] * - height; - x = offset_x + (float)i * bin_width; - pdraw_gles2hud_draw_filled_rect(self, - x, - offset_y1, - x + bin_width, - y1, - color_white); - } - } - if (frame_extra->histogram[PDRAW_HISTOGRAM_CHANNEL_RED] && - frame_extra->histogram[PDRAW_HISTOGRAM_CHANNEL_GREEN] && - frame_extra->histogram[PDRAW_HISTOGRAM_CHANNEL_BLUE] && - (frame_extra->histogram_len[PDRAW_HISTOGRAM_CHANNEL_RED] == - frame_extra->histogram_len[PDRAW_HISTOGRAM_CHANNEL_GREEN]) && - (frame_extra->histogram_len[PDRAW_HISTOGRAM_CHANNEL_RED] == - frame_extra->histogram_len[PDRAW_HISTOGRAM_CHANNEL_BLUE])) { - pdraw_gles2hud_draw_filled_rect(self, - offset_x, - offset_y2, - offset_x + width, - offset_y2 + height, - color_background); - bin_width = - width / - frame_extra->histogram_len[PDRAW_HISTOGRAM_CHANNEL_RED]; - for (i = 0; - i < - frame_extra->histogram_len[PDRAW_HISTOGRAM_CHANNEL_RED]; - i++) { - x = offset_x + (float)i * bin_width; - val[0] = - frame_extra - ->histogram[PDRAW_HISTOGRAM_CHANNEL_RED] - [i]; - val[1] = frame_extra->histogram - [PDRAW_HISTOGRAM_CHANNEL_GREEN][i]; - val[2] = frame_extra->histogram - [PDRAW_HISTOGRAM_CHANNEL_BLUE][i]; - if (val[0] < val[1]) { - if (val[0] < val[2]) { - idx[0] = 0; - if (val[1] < val[2]) { - idx[1] = 1; - idx[2] = 2; - } else { - idx[1] = 2; - idx[2] = 1; - } - } else { - idx[0] = 2; - idx[1] = 0; - idx[2] = 1; - } - } else { - if (val[1] < val[2]) { - idx[0] = 1; - if (val[0] < val[2]) { - idx[1] = 0; - idx[2] = 2; - } else { - idx[1] = 2; - idx[2] = 0; - } - } else { - idx[0] = 2; - idx[1] = 1; - idx[2] = 0; - } - } - for (j = 0, prev_val = 0.f; j < 3; j++) { - if (val[idx[j]] <= 0.f) - continue; - cur_color[0] = cur_color[1] = cur_color[2] = - 0.f; - for (k = j; k < 3; k++) { - cur_color[0] += color[idx[k]][0]; - cur_color[1] += color[idx[k]][1]; - cur_color[2] += color[idx[k]][2]; - } - y1 = offset_y2 + prev_val * height; - y2 = offset_y2 + val[idx[j]] * height; - prev_val = val[idx[j]]; - cur_color[3] = 0.4f; - pdraw_gles2hud_draw_filled_rect(self, - x, - y1, - x + bin_width, - y2, - cur_color); - } - } - } -} +/** + * Parrot Drones Awesome Video Viewer + * OpenGL ES 2.0 HUD rendering library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 "pdraw_gles2hud_priv.h" + + +void pdraw_gles2hud_draw_vumeter(struct pdraw_gles2hud *self, + float x, + float y, + float r, + float value, + float val_min, + float val_max, + float critical_min, + float critical_max, + const float color[4], + const float critical_color[4]) +{ + x *= self->ratio_w; + y *= self->ratio_h; + if (value < val_min) + value = val_min; + if (value > val_max) + value = val_max; + float span = 4. * M_PI / 3.; + float start = (M_PI - span) / 2.; + pdraw_gles2hud_draw_arc(self, + x, + y, + r * self->ratio_w, + r * self->ratio_w * self->aspect_ratio, + start, + span, + 20, + color, + 2.); + if ((critical_min >= val_min) && (critical_min <= val_max) && + (critical_max >= val_min) && (critical_max <= val_max) && + (critical_min < critical_max)) { + float start2 = start + (1. - (critical_max - val_min) / + (val_max - val_min)) * + span; + float end2 = start + (1. - (critical_min - val_min) / + (val_max - val_min)) * + span; + pdraw_gles2hud_draw_arc(self, + x, + y, + r * self->ratio_w * 0.9, + r * self->ratio_w * self->aspect_ratio * + 0.9, + start2, + end2 - start2, + 10, + critical_color, + 2.); + } + float angle = + start + (1. - (value - val_min) / (val_max - val_min)) * span; + float x1 = x + r * self->ratio_w * 0.4 * cosf(angle); + float y1 = + y + r * self->ratio_w * self->aspect_ratio * 0.4 * sinf(angle); + float x2 = x + r * self->ratio_w * 0.9 * cosf(angle); + float y2 = + y + r * self->ratio_w * self->aspect_ratio * 0.9 * sinf(angle); + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); +} + + +void pdraw_gles2hud_draw_artificial_horizon(struct pdraw_gles2hud *self, + const struct vmeta_euler *drone, + const struct vmeta_euler *frame, + const float color[4]) +{ + int i; + float x1, y1, x2, y2; + float height = self->config.central_zone_size * self->ratio_w * + self->aspect_ratio; + int steps = 6; + + /* Scale */ + for (i = -steps; i <= steps; i++) { + if (i != 0) { + if (i & 1) { + pdraw_gles2hud_draw_line(self, + -0.01 * self->ratio_w, + i * height / 2 / steps, + 0.01 * self->ratio_w, + i * height / 2 / steps, + color, + 2.); + } + } + } + + /* Horizon */ + x1 = -0.5 * self->config.central_zone_size * self->ratio_w * + cosf(frame->phi); + y1 = -0.5 * self->config.central_zone_size * self->ratio_w * + self->aspect_ratio * sinf(frame->phi); + x2 = 0.5 * self->config.central_zone_size * self->ratio_w * + cosf(frame->phi); + y2 = 0.5 * self->config.central_zone_size * self->ratio_w * + self->aspect_ratio * sinf(frame->phi); + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); + + /* Drone */ + float vertices[10]; + float drone_y = drone->theta / (M_PI / 18 * steps) * height / 2; + vertices[0] = -0.06 * self->ratio_w * cosf(frame->phi - drone->phi); + vertices[1] = -0.06 * self->ratio_w * self->aspect_ratio * + sinf(frame->phi - drone->phi) + + drone_y; + vertices[2] = -0.015 * self->ratio_w * cosf(frame->phi - drone->phi); + vertices[3] = -0.015 * self->ratio_w * self->aspect_ratio * + sinf(frame->phi - drone->phi) + + drone_y; + vertices[4] = 0.015 * self->ratio_w * sinf(frame->phi - drone->phi); + vertices[5] = -0.015 * self->ratio_w * self->aspect_ratio * + cosf(frame->phi - drone->phi) + + drone_y; + vertices[6] = 0.015 * self->ratio_w * cosf(frame->phi - drone->phi); + vertices[7] = 0.015 * self->ratio_w * self->aspect_ratio * + sinf(frame->phi - drone->phi) + + drone_y; + vertices[8] = 0.06 * self->ratio_w * cosf(frame->phi - drone->phi); + vertices[9] = 0.06 * self->ratio_w * self->aspect_ratio * + sinf(frame->phi - drone->phi) + + drone_y; + GLCHK(glLineWidth(6.)); + GLCHK(glVertexAttribPointer( + self->position_handle, 2, GL_FLOAT, false, 0, vertices)); + GLCHK(glUniform4fv(self->color_handle, 1, color)); + GLCHK(glDrawArrays(GL_LINE_STRIP, 0, 5)); +} + + +void pdraw_gles2hud_draw_roll(struct pdraw_gles2hud *self, + float drone_roll, + const float color[4]) +{ + int i; + float rotation, x1, y1, x2, y2; + float width = 0.12 * self->ratio_w; + float y_offset = self->config.roll_zone_v_offset * self->ratio_h; + int steps = 6; + + pdraw_gles2hud_draw_arc(self, + 0., + y_offset, + width, + width * self->aspect_ratio, + M_PI * (90. - 10. * steps) / 180., + M_PI * 20. * steps / 180., + 100, + color, + 2.); + rotation = M_PI / 2. - drone_roll; + x1 = (width - 0.012 * self->ratio_w) * cosf(rotation); + y1 = (width - 0.012 * self->ratio_w) * self->aspect_ratio * + sinf(rotation) + + y_offset; + x2 = (width + 0.012 * self->ratio_w) * cosf(rotation); + y2 = (width + 0.012 * self->ratio_w) * self->aspect_ratio * + sinf(rotation) + + y_offset; + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); + + for (i = -steps, rotation = M_PI * (90. - 10. * steps) / 180.; + i <= steps; + i++, rotation += M_PI * 10. / 180.) { + int angle = (i * 10 + 60 + 360) % 360; + if (angle <= 120) { + x1 = width * cosf(rotation); + y1 = width * self->aspect_ratio * sinf(rotation) + + y_offset; + x2 = (width - 0.008 * self->ratio_w) * cosf(rotation); + y2 = (width - 0.008 * self->ratio_w) * + self->aspect_ratio * sinf(rotation) + + y_offset; + pdraw_gles2hud_draw_line( + self, x1, y1, x2, y2, color, 2.); + } + } + + x1 = 0.; + y1 = y_offset; + x2 = 0.; + y2 = y_offset; + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); +} + + +void pdraw_gles2hud_draw_heading(struct pdraw_gles2hud *self, + float drone_yaw, + float horizontal_speed, + float speed_psi, + const float color[4]) +{ + int i; + int heading = ((int)(drone_yaw * RAD_TO_DEG) + 360) % 360; + float rotation, x1, y1, x2, y2; + char heading_str[20]; + snprintf(heading_str, sizeof(heading_str), "%d", heading); + + float width = 0.12 * self->ratio_w; + float y_offset = self->config.heading_zone_v_offset * self->ratio_h; + + pdraw_gles2hud_draw_arc(self, + 0., + y_offset, + width, + width * self->aspect_ratio, + M_PI * 20. / 180., + M_PI * 140. / 180., + 100, + color, + 2.); + x1 = 0.; + y1 = y_offset + width * self->aspect_ratio; + x2 = 0.; + y2 = y_offset + (width + 0.01 * self->ratio_w) * self->aspect_ratio; + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); + + for (i = 0, rotation = drone_yaw + M_PI / 2.; i < 36; + i++, rotation += M_PI * 10. / 180.) { + int angle = (heading + i * 10 + 70 + 360) % 360; + if (angle <= 140) { + x1 = width * cosf(rotation); + y1 = width * self->aspect_ratio * sinf(rotation) + + y_offset; + x2 = (width - 0.01 * self->ratio_w) * cosf(rotation); + y2 = (width - 0.01 * self->ratio_w) * + self->aspect_ratio * sinf(rotation) + + y_offset; + pdraw_gles2hud_draw_line( + self, x1, y1, x2, y2, color, 2.); + } + } + + if (horizontal_speed >= 0.2) { + rotation = drone_yaw - speed_psi + M_PI / 2.; + x1 = 0.045 * self->ratio_w * cosf(rotation); + y1 = 0.045 * self->ratio_w * self->aspect_ratio * + sinf(rotation) + + y_offset; + x2 = 0.020 * self->ratio_w * cosf(rotation); + y2 = 0.020 * self->ratio_w * self->aspect_ratio * + sinf(rotation) + + y_offset; + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); + x1 = 0.045 * self->ratio_w * cosf(rotation); + y1 = 0.045 * self->ratio_w * self->aspect_ratio * + sinf(rotation) + + y_offset; + x2 = 0.010 * self->ratio_w * cosf(rotation - 5. * M_PI / 6.) + + x1; + y2 = 0.010 * self->ratio_w * self->aspect_ratio * + sinf(rotation - 5. * M_PI / 6.) + + y1; + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); + x1 = 0.045 * self->ratio_w * cosf(rotation); + y1 = 0.045 * self->ratio_w * self->aspect_ratio * + sinf(rotation) + + y_offset; + x2 = 0.010 * self->ratio_w * cosf(rotation + 5. * M_PI / 6.) + + x1; + y2 = 0.010 * self->ratio_w * self->aspect_ratio * + sinf(rotation + 5. * M_PI / 6.) + + y1; + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); + } +} + + +void pdraw_gles2hud_draw_altitude(struct pdraw_gles2hud *self, + double altitude, + float ground_distance, + float down_speed, + const float color[4]) +{ + char altitude_str[20]; + snprintf(altitude_str, sizeof(altitude_str), "%.1fm", altitude); + + float x_offset = self->config.central_zone_size * self->ratio_w; + float height = self->config.central_zone_size * self->ratio_w * + self->aspect_ratio; + float altitude_interval = height / 20.; + + pdraw_gles2hud_draw_line( + self, x_offset, -height / 2., x_offset, height / 2., color, 2.); + pdraw_gles2hud_draw_line(self, + x_offset, + -height / 2., + x_offset + 0.08 * self->ratio_w, + -height / 2., + color, + 2.); + pdraw_gles2hud_draw_line(self, + x_offset, + height / 2., + x_offset + 0.08 * self->ratio_w, + height / 2., + color, + 2.); + pdraw_gles2hud_draw_rect(self, + x_offset + 0.03 * self->ratio_w, + -0.017 * self->ratio_w * self->aspect_ratio, + x_offset + 0.03 * self->ratio_w + + 0.1 * self->ratio_w, + 0.017 * self->ratio_w * self->aspect_ratio, + color, + 2.); + pdraw_gles2hud_draw_line(self, + x_offset, + 0., + x_offset + 0.03 * self->ratio_w, + -0.017 * self->ratio_w * self->aspect_ratio, + color, + 2.); + pdraw_gles2hud_draw_line(self, + x_offset, + 0., + x_offset + 0.03 * self->ratio_w, + 0.017 * self->ratio_w * self->aspect_ratio, + color, + 2.); + + float y = (ceil(altitude) - altitude) * altitude_interval; + int alt_int = ((int)ceil(altitude)); + int alt_mod5 = alt_int % 5; + while (y < height / 2.) { + pdraw_gles2hud_draw_line( + self, + x_offset, + y, + x_offset + ((alt_mod5 == 0) ? 0.03 * self->ratio_w + : 0.01 * self->ratio_w), + y, + color, + 2.); + if ((!alt_mod5) && (y > -height / 2. + 0.03 * self->ratio_w) && + (y < height / 2. - 0.03 * self->ratio_w) && + (!((y > -0.03 * self->ratio_w) && + (y < 0.03 * self->ratio_w)))) { + snprintf(altitude_str, + sizeof(altitude_str), + "%d", + alt_int); + /* drawText(altitude_str); */ + } + y += altitude_interval; + alt_int++; + alt_mod5 = alt_int % 5; + } + y = -(altitude - floor(altitude)) * altitude_interval; + alt_int = ((int)floor(altitude)); + alt_mod5 = alt_int % 5; + while (y > -height / 2.) { + pdraw_gles2hud_draw_line( + self, + x_offset, + y, + x_offset + ((alt_mod5 == 0) ? 0.03 * self->ratio_w + : 0.01 * self->ratio_w), + y, + color, + 2.); + if ((!alt_mod5) && + (y > -height / 2. + + 0.017 * self->ratio_w * self->aspect_ratio) && + (y < height / 2. - + 0.017 * self->ratio_w * self->aspect_ratio) && + (!((y > -0.017 * self->ratio_w * self->aspect_ratio) && + (y < 0.017 * self->ratio_w * self->aspect_ratio)))) { + snprintf(altitude_str, + sizeof(altitude_str), + "%d", + alt_int); + /* drawText(altitude_str); */ + } + y -= altitude_interval; + alt_int--; + alt_mod5 = alt_int % 5; + } + + /* Ground distance */ + y = -ground_distance * height / 20.; + if ((y < height / 2.) && + (y > -height / 2. + 0.01 * self->ratio_w * self->aspect_ratio)) { + if ((y > -0.012 * self->ratio_w * self->aspect_ratio) && + (y < 0.022 * self->ratio_w * self->aspect_ratio)) { + pdraw_gles2hud_draw_line(self, + x_offset, + y, + x_offset + + 0.03 * self->ratio_w, + y, + color, + 2.); + } else { + pdraw_gles2hud_draw_line(self, + x_offset, + y, + x_offset + + 0.06 * self->ratio_w, + y, + color, + 2.); + pdraw_gles2hud_draw_line( + self, + x_offset + 0.03 * self->ratio_w, + y, + x_offset + 0.04 * self->ratio_w, + y - 0.01 * self->ratio_w * self->aspect_ratio, + color, + 2.); + pdraw_gles2hud_draw_line( + self, + x_offset + 0.04 * self->ratio_w, + y, + x_offset + 0.05 * self->ratio_w, + y - 0.01 * self->ratio_w * self->aspect_ratio, + color, + 2.); + pdraw_gles2hud_draw_line( + self, + x_offset + 0.05 * self->ratio_w, + y, + x_offset + 0.06 * self->ratio_w, + y - 0.01 * self->ratio_w * self->aspect_ratio, + color, + 2.); + } + pdraw_gles2hud_draw_line(self, + x_offset, + y, + x_offset + 0.01 * self->ratio_w, + y - 0.01 * self->ratio_w * + self->aspect_ratio, + color, + 2.); + pdraw_gles2hud_draw_line(self, + x_offset + 0.01 * self->ratio_w, + y, + x_offset + 0.02 * self->ratio_w, + y - 0.01 * self->ratio_w * + self->aspect_ratio, + color, + 2.); + pdraw_gles2hud_draw_line(self, + x_offset + 0.02 * self->ratio_w, + y, + x_offset + 0.03 * self->ratio_w, + y - 0.01 * self->ratio_w * + self->aspect_ratio, + color, + 2.); + } + + /* Speed indication */ + if (fabs(down_speed) >= 0.2) { + float x1, y1, x2, y2; + x1 = x_offset + 0.15 * self->ratio_w; + y1 = -0.017 * self->ratio_w * self->aspect_ratio; + x2 = x_offset + 0.15 * self->ratio_w; + y2 = 0.017 * self->ratio_w * self->aspect_ratio; + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); + if (down_speed < 0.) { + x1 = x_offset + 0.15 * self->ratio_w; + y1 = 0.017 * self->ratio_w * self->aspect_ratio; + x2 = x_offset + 0.15 * self->ratio_w - + 0.0056 * self->ratio_w; + y2 = 0.017 * self->ratio_w * self->aspect_ratio - + 0.0098 * self->ratio_w * self->aspect_ratio; + pdraw_gles2hud_draw_line( + self, x1, y1, x2, y2, color, 2.); + x1 = x_offset + 0.15 * self->ratio_w; + y1 = 0.017 * self->ratio_w * self->aspect_ratio; + x2 = x_offset + 0.15 * self->ratio_w + + 0.0056 * self->ratio_w; + y2 = 0.017 * self->ratio_w * self->aspect_ratio - + 0.0098 * self->ratio_w * self->aspect_ratio; + pdraw_gles2hud_draw_line( + self, x1, y1, x2, y2, color, 2.); + } else { + x1 = x_offset + 0.15 * self->ratio_w; + y1 = -0.017 * self->ratio_w * self->aspect_ratio; + x2 = x_offset + 0.15 * self->ratio_w - + 0.0056 * self->ratio_w; + y2 = -0.017 * self->ratio_w * self->aspect_ratio + + 0.0098 * self->ratio_w * self->aspect_ratio; + pdraw_gles2hud_draw_line( + self, x1, y1, x2, y2, color, 2.); + x1 = x_offset + 0.15 * self->ratio_w; + y1 = -0.017 * self->ratio_w * self->aspect_ratio; + x2 = x_offset + 0.15 * self->ratio_w + + 0.0056 * self->ratio_w; + y2 = -0.017 * self->ratio_w * self->aspect_ratio + + 0.0098 * self->ratio_w * self->aspect_ratio; + pdraw_gles2hud_draw_line( + self, x1, y1, x2, y2, color, 2.); + } + } +} + + +void pdraw_gles2hud_draw_speed(struct pdraw_gles2hud *self, + float horizontal_speed, + const float color[4]) +{ + char speed_str[20]; + snprintf(speed_str, sizeof(speed_str), "%.1fm/s", horizontal_speed); + + float x_offset = -self->config.central_zone_size * self->ratio_w; + float height = self->config.central_zone_size * self->ratio_w * + self->aspect_ratio; + float speed_interval = height / 20.; + + pdraw_gles2hud_draw_line( + self, x_offset, -height / 2., x_offset, height / 2., color, 2.); + pdraw_gles2hud_draw_line(self, + x_offset, + -height / 2., + x_offset - 0.08 * self->ratio_w, + -height / 2., + color, + 2.); + pdraw_gles2hud_draw_line(self, + x_offset, + height / 2., + x_offset - 0.08 * self->ratio_w, + height / 2., + color, + 2.); + pdraw_gles2hud_draw_rect(self, + x_offset - 0.03 * self->ratio_w, + -0.017 * self->ratio_w * self->aspect_ratio, + x_offset - 0.03 * self->ratio_w - + 0.1 * self->ratio_w, + 0.017 * self->ratio_w * self->aspect_ratio, + color, + 2.); + pdraw_gles2hud_draw_line(self, + x_offset, + 0., + x_offset - 0.03 * self->ratio_w, + -0.017 * self->ratio_w * self->aspect_ratio, + color, + 2.); + pdraw_gles2hud_draw_line(self, + x_offset, + 0., + x_offset - 0.03 * self->ratio_w, + 0.017 * self->ratio_w * self->aspect_ratio, + color, + 2.); + + float y = (ceil(horizontal_speed) - horizontal_speed) * speed_interval; + int spd_int = ((int)ceil(horizontal_speed)); + int spd_mod5 = spd_int % 5; + while (y < height / 2.) { + pdraw_gles2hud_draw_line( + self, + x_offset, + y, + x_offset - ((spd_mod5 == 0) ? 0.03 * self->ratio_w + : 0.01 * self->ratio_w), + y, + color, + 2.); + if ((!spd_mod5) && + (y > -height / 2. + + 0.017 * self->ratio_w * self->aspect_ratio) && + (y < height / 2. - + 0.017 * self->ratio_w * self->aspect_ratio) && + (!((y > -0.017 * self->ratio_w * self->aspect_ratio) && + (y < 0.017 * self->ratio_w * self->aspect_ratio)))) { + snprintf(speed_str, sizeof(speed_str), "%d", spd_int); + /* drawText(strAltitude); */ + } + y += speed_interval; + spd_int++; + spd_mod5 = spd_int % 5; + } + y = -(horizontal_speed - floor(horizontal_speed)) * speed_interval; + spd_int = ((int)floor(horizontal_speed)); + spd_mod5 = spd_int % 5; + while (y > -height / 2.) { + pdraw_gles2hud_draw_line( + self, + x_offset, + y, + x_offset - ((spd_mod5 == 0) ? 0.03 * self->ratio_w + : 0.01 * self->ratio_w), + y, + color, + 2.); + if ((!spd_mod5) && + (y > -height / 2. + + 0.017 * self->ratio_w * self->aspect_ratio) && + (y < height / 2. - + 0.017 * self->ratio_w * self->aspect_ratio) && + (!((y > -0.017 * self->ratio_w * self->aspect_ratio) && + (y < 0.017 * self->ratio_w * self->aspect_ratio)))) { + snprintf(speed_str, sizeof(speed_str), "%d", spd_int); + /* drawText(strAltitude); */ + } + y -= speed_interval; + spd_int--; + spd_mod5 = spd_int % 5; + } +} + + +void pdraw_gles2hud_draw_controller_radar(struct pdraw_gles2hud *self, + double distance, + double bearing, + float controller_yaw, + float drone_yaw, + float controller_radar_angle, + const float color[4]) +{ + float width = 0.08 * self->ratio_w; + float x_offset = self->config.radar_zone_h_offset * self->ratio_w; + float y_offset = self->config.radar_zone_v_offset * self->ratio_h; + float x1, y1, x2, y2; + + pdraw_gles2hud_draw_ellipse(self, + x_offset, + y_offset, + width, + width * self->aspect_ratio, + 100, + color, + 2.); + x1 = x_offset; + y1 = y_offset + width * self->aspect_ratio; + x2 = x_offset; + y2 = y_offset + (width + 0.008 * self->ratio_w) * self->aspect_ratio; + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); + + if (distance > 50.) { + x1 = x_offset - width / 3. * sinf(controller_radar_angle / 2.); + y1 = y_offset + width / 3. * cosf(controller_radar_angle / 2.) * + self->aspect_ratio; + x2 = x_offset - width * sinf(controller_radar_angle / 2.); + y2 = y_offset + width * cosf(controller_radar_angle / 2.) * + self->aspect_ratio; + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); + x1 = x_offset + width / 3. * sinf(controller_radar_angle / 2.); + y1 = y_offset + width / 3. * cosf(controller_radar_angle / 2.) * + self->aspect_ratio; + x2 = x_offset + width * sinf(controller_radar_angle / 2.); + y2 = y_offset + width * cosf(controller_radar_angle / 2.) * + self->aspect_ratio; + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); + } +} + + +void pdraw_gles2hud_draw_record_timeline(struct pdraw_gles2hud *self, + uint64_t current_time, + uint64_t duration, + const float color[4]) +{ + float x_offset = self->config.right_zone_h_offset * self->ratio_w; + float y_offset = self->config.roll_zone_v_offset * self->ratio_h + + 0.12 * self->ratio_w * self->aspect_ratio; + float width = 0.4 * self->ratio_w; + float height = 0.015 * self->ratio_w * self->aspect_ratio; + float x1, y1, x2, y2; + float cw = 0., rw = 0.; + + uint64_t remaining_time = duration - current_time; + unsigned int c_hrs = 0, c_min = 0, c_sec = 0, c_msec = 0; + unsigned int r_hrs = 0, r_min = 0, r_sec = 0, r_msec = 0; + unsigned int d_hrs = 0, d_min = 0, d_sec = 0, d_msec = 0; + pdraw_gles2hud_friendly_time_from_us( + current_time, &c_hrs, &c_min, &c_sec, &c_msec); + pdraw_gles2hud_friendly_time_from_us( + remaining_time, &r_hrs, &r_min, &r_sec, &r_msec); + pdraw_gles2hud_friendly_time_from_us( + duration, &d_hrs, &d_min, &d_sec, &d_msec); + char str[20]; + if (d_hrs) { + snprintf(str, + sizeof(str), + "+%02d:%02d:%02d.%03d", + c_hrs, + c_min, + c_sec, + c_msec); + } else { + snprintf(str, + sizeof(str), + "+%02d:%02d.%03d", + c_min, + c_sec, + c_msec); + } + pdraw_gles2hud_get_text_dimensions(self, + str, + 0.15 * self->ratio_w, + 1., + self->aspect_ratio, + &cw, + nullptr); + cw += 0.012; + if (d_hrs) { + snprintf(str, + sizeof(str), + "-%02d:%02d:%02d.%03d", + r_hrs, + r_min, + r_sec, + r_msec); + } else { + snprintf(str, + sizeof(str), + "-%02d:%02d.%03d", + r_min, + r_sec, + r_msec); + } + pdraw_gles2hud_get_text_dimensions(self, + str, + 0.15 * self->ratio_w, + 1., + self->aspect_ratio, + &rw, + nullptr); + rw += 0.01; + + x1 = x_offset - rw; + y1 = y2 = y_offset; + x2 = x_offset - width + cw; + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); + x1 = x2 = x_offset - rw; + y1 = y_offset + height / 2.; + y2 = y_offset - height / 2.; + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); + x1 = x2 = x_offset - width + cw; + y1 = y_offset + height / 2.; + y2 = y_offset - height / 2.; + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); + x1 = x2 = x_offset - rw - + (1. - (float)current_time / (float)duration) * + (width - rw - cw); + y1 = y_offset + height / 2.; + y2 = y_offset - height / 2.; + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); +} + + +void pdraw_gles2hud_draw_recording_status(struct pdraw_gles2hud *self, + uint64_t recording_duration, + const float color[4]) +{ + float x_offset = self->config.right_zone_h_offset * self->ratio_w; + float y_offset = self->config.roll_zone_v_offset * self->ratio_h + + 0.12 * self->ratio_w * self->aspect_ratio; + float rec_size = 0.008 * self->ratio_w; + float w = 0.; + + if (recording_duration > 0) { + unsigned int d_hrs = 0, d_min = 0, d_sec = 0, d_msec = 0; + pdraw_gles2hud_friendly_time_from_us( + recording_duration, &d_hrs, &d_min, &d_sec, &d_msec); + char str[20]; + if (d_hrs) { + snprintf(str, + sizeof(str), + "REC %02d:%02d:%02d", + d_hrs, + d_min, + d_sec); + } else { + snprintf(str, + sizeof(str), + "REC %02d:%02d", + d_min, + d_sec); + } + pdraw_gles2hud_get_text_dimensions(self, + str, + 0.15 * self->ratio_w, + 1., + self->aspect_ratio, + &w, + nullptr); + w += 0.02; + pdraw_gles2hud_draw_filled_ellipse(self, + x_offset - w - rec_size / 2., + y_offset, + rec_size, + rec_size * + self->aspect_ratio, + 30, + color); + } else { + pdraw_gles2hud_get_text_dimensions(self, + "REC", + 0.15 * self->ratio_w, + 1., + self->aspect_ratio, + &w, + nullptr); + w += 0.03; + pdraw_gles2hud_draw_ellipse(self, + x_offset - w - rec_size / 2., + y_offset, + rec_size, + rec_size * self->aspect_ratio, + 30, + color, + 2.); + float x1, y1, x2, y2; + x1 = x_offset + 0.008 * self->ratio_w; + y1 = y_offset + rec_size * self->aspect_ratio + + 0.008 * self->ratio_w * self->aspect_ratio; + x2 = x1 - w - rec_size - 0.016 * self->ratio_w; + y2 = y1 - rec_size * 2. * self->aspect_ratio - + 0.016 * self->ratio_w * self->aspect_ratio; + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); + pdraw_gles2hud_draw_line(self, x1, y2, x2, y1, color, 2.); + } +} + + +void pdraw_gles2hud_draw_flight_path_vector(struct pdraw_gles2hud *self, + const struct vmeta_euler *frame, + float speed_theta, + float speed_psi, + const float color[4]) +{ + float x = (speed_psi - frame->psi) / self->h_fov * 2. * self->ratio_w; + float y = + (speed_theta - frame->theta) / self->v_fov * 2. * self->ratio_h; + float x1, y1, x2, y2, tx, ty; + float rotation = frame->phi; + + if ((x > -self->ratio_w) && (x < self->ratio_w) && + (y > -self->ratio_h) && (y < self->ratio_h)) { + pdraw_gles2hud_draw_ellipse(self, + x, + y, + 0.02 * self->ratio_w, + 0.02 * self->ratio_w * + self->aspect_ratio, + 40, + color, + 2.); + tx = 0.; + ty = 0.02; + x1 = x + (tx * cosf(rotation) - ty * sinf(rotation)) * + self->ratio_w; + y1 = y + (tx * sinf(rotation) + ty * cosf(rotation)) * + self->ratio_w * self->aspect_ratio; + tx = 0.; + ty = 0.03; + x2 = x + (tx * cosf(rotation) - ty * sinf(rotation)) * + self->ratio_w; + y2 = y + (tx * sinf(rotation) + ty * cosf(rotation)) * + self->ratio_w * self->aspect_ratio; + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); + tx = -0.02; + ty = 0.; + x1 = x + (tx * cosf(rotation) - ty * sinf(rotation)) * + self->ratio_w; + y1 = y + (tx * sinf(rotation) + ty * cosf(rotation)) * + self->ratio_w * self->aspect_ratio; + tx = -0.03; + ty = 0.; + x2 = x + (tx * cosf(rotation) - ty * sinf(rotation)) * + self->ratio_w; + y2 = y + (tx * sinf(rotation) + ty * cosf(rotation)) * + self->ratio_w * self->aspect_ratio; + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); + tx = 0.02; + ty = 0.; + x1 = x + (tx * cosf(rotation) - ty * sinf(rotation)) * + self->ratio_w; + y1 = y + (tx * sinf(rotation) + ty * cosf(rotation)) * + self->ratio_w * self->aspect_ratio; + tx = 0.03; + ty = 0.; + x2 = x + (tx * cosf(rotation) - ty * sinf(rotation)) * + self->ratio_w; + y2 = y + (tx * sinf(rotation) + ty * cosf(rotation)) * + self->ratio_w * self->aspect_ratio; + pdraw_gles2hud_draw_line(self, x1, y1, x2, y2, color, 2.); + } +} + + +void pdraw_gles2hud_draw_framing_grid(struct pdraw_gles2hud *self, + const struct pdraw_rect *render_pos, + const struct pdraw_rect *content_pos, + const float color[4]) +{ + float x_left, x_right, x_third, y_top, y_bottom, y_third; + + x_left = (float)content_pos->x / (float)render_pos->width * 2. - 1.; + x_right = ((float)(content_pos->x + content_pos->width) + 0.5) / + (float)render_pos->width * 2. - + 1.; + x_third = (x_right - x_left) / 3.; + y_top = 1. - (float)content_pos->y / (float)render_pos->height * 2.; + y_bottom = 1. - ((float)(content_pos->y + content_pos->height) + 0.5) / + (float)render_pos->height * 2.; + y_third = (y_top - y_bottom) / 3.; + + if (x_left != -1.) { + pdraw_gles2hud_draw_line( + self, x_left, y_top, x_left, y_bottom, color, 1.); + } + pdraw_gles2hud_draw_line(self, + x_left + x_third, + y_top, + x_left + x_third, + y_bottom, + color, + 1.); + pdraw_gles2hud_draw_line(self, + x_right - x_third, + y_top, + x_right - x_third, + y_bottom, + color, + 1.); + if (x_right != 1.) { + pdraw_gles2hud_draw_line( + self, x_right, y_top, x_right, y_bottom, color, 1.); + } + if (y_top != 1.) { + pdraw_gles2hud_draw_line( + self, x_left, y_top, x_right, y_top, color, 1.); + } + pdraw_gles2hud_draw_line(self, + x_left, + y_top - y_third, + x_right, + y_top - y_third, + color, + 1.); + pdraw_gles2hud_draw_line(self, + x_left, + y_bottom + y_third, + x_right, + y_bottom + y_third, + color, + 1.); + if (y_bottom != -1.) { + pdraw_gles2hud_draw_line( + self, x_left, y_bottom, x_right, y_bottom, color, 1.); + } +} + + +void pdraw_gles2hud_draw_histograms( + struct pdraw_gles2hud *self, + const struct pdraw_video_frame_extra *frame_extra) +{ + unsigned int i, j, k; + float color_background[4] = {0.0f, 0.0f, 0.0f, 0.2f}; + float color_white[4] = {1.0f, 1.0f, 1.0f, 0.4f}; + float color[3][4] = { + {1.0f, 0.0f, 0.0f, 1.0f}, + {0.0f, 1.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 1.0f, 1.0f}, + }; + float cur_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + float width = 0.333f * 2.0f * self->ratio_w; + float height = 0.333f * self->ratio_h; + float offset_x = (1.0f - 0.166f) * self->ratio_w - width; + float offset_y1 = (1.0f - 0.166f) * self->ratio_h - height; + float offset_y2 = (1.0f - 0.166f) * self->ratio_h - height * 2. - 0.04f; + float x, y1, y2, bin_width; + float val[3], prev_val; + int idx[3]; + + if (frame_extra->histogram[PDRAW_HISTOGRAM_CHANNEL_LUMA]) { + pdraw_gles2hud_draw_filled_rect(self, + offset_x, + offset_y1, + offset_x + width, + offset_y1 + height, + color_background); + bin_width = + width / + frame_extra + ->histogram_len[PDRAW_HISTOGRAM_CHANNEL_LUMA]; + for (i = 0; + i < + frame_extra->histogram_len[PDRAW_HISTOGRAM_CHANNEL_LUMA]; + i++) { + y1 = offset_y1 + + frame_extra->histogram + [PDRAW_HISTOGRAM_CHANNEL_LUMA][i] * + height; + x = offset_x + (float)i * bin_width; + pdraw_gles2hud_draw_filled_rect(self, + x, + offset_y1, + x + bin_width, + y1, + color_white); + } + } + if (frame_extra->histogram[PDRAW_HISTOGRAM_CHANNEL_RED] && + frame_extra->histogram[PDRAW_HISTOGRAM_CHANNEL_GREEN] && + frame_extra->histogram[PDRAW_HISTOGRAM_CHANNEL_BLUE] && + (frame_extra->histogram_len[PDRAW_HISTOGRAM_CHANNEL_RED] == + frame_extra->histogram_len[PDRAW_HISTOGRAM_CHANNEL_GREEN]) && + (frame_extra->histogram_len[PDRAW_HISTOGRAM_CHANNEL_RED] == + frame_extra->histogram_len[PDRAW_HISTOGRAM_CHANNEL_BLUE])) { + pdraw_gles2hud_draw_filled_rect(self, + offset_x, + offset_y2, + offset_x + width, + offset_y2 + height, + color_background); + bin_width = + width / + frame_extra->histogram_len[PDRAW_HISTOGRAM_CHANNEL_RED]; + for (i = 0; + i < + frame_extra->histogram_len[PDRAW_HISTOGRAM_CHANNEL_RED]; + i++) { + x = offset_x + (float)i * bin_width; + val[0] = + frame_extra + ->histogram[PDRAW_HISTOGRAM_CHANNEL_RED] + [i]; + val[1] = frame_extra->histogram + [PDRAW_HISTOGRAM_CHANNEL_GREEN][i]; + val[2] = frame_extra->histogram + [PDRAW_HISTOGRAM_CHANNEL_BLUE][i]; + if (val[0] < val[1]) { + if (val[0] < val[2]) { + idx[0] = 0; + if (val[1] < val[2]) { + idx[1] = 1; + idx[2] = 2; + } else { + idx[1] = 2; + idx[2] = 1; + } + } else { + idx[0] = 2; + idx[1] = 0; + idx[2] = 1; + } + } else { + if (val[1] < val[2]) { + idx[0] = 1; + if (val[0] < val[2]) { + idx[1] = 0; + idx[2] = 2; + } else { + idx[1] = 2; + idx[2] = 0; + } + } else { + idx[0] = 2; + idx[1] = 1; + idx[2] = 0; + } + } + for (j = 0, prev_val = 0.f; j < 3; j++) { + if (val[idx[j]] <= 0.f) + continue; + cur_color[0] = cur_color[1] = cur_color[2] = + 0.f; + for (k = j; k < 3; k++) { + cur_color[0] += color[idx[k]][0]; + cur_color[1] += color[idx[k]][1]; + cur_color[2] += color[idx[k]][2]; + } + y1 = offset_y2 + prev_val * height; + y2 = offset_y2 + val[idx[j]] * height; + prev_val = val[idx[j]]; + cur_color[3] = 0.4f; + pdraw_gles2hud_draw_filled_rect(self, + x, + y1, + x + bin_width, + y2, + cur_color); + } + } + } +} diff --git a/libpdraw-gles2hud/src/pdraw_gles2hud_priv.h b/libpdraw-gles2hud/src/pdraw_gles2hud_priv.h index e28c659..0e547a0 100644 --- a/libpdraw-gles2hud/src/pdraw_gles2hud_priv.h +++ b/libpdraw-gles2hud/src/pdraw_gles2hud_priv.h @@ -1,413 +1,413 @@ -/** - * Parrot Drones Awesome Video Viewer - * OpenGL ES 2.0 HUD rendering library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_GLES2HUD_PRIV_HPP_ -#define _PDRAW_GLES2HUD_PRIV_HPP_ - -#include -#include -#include - -#define ULOG_TAG pdraw_gles2hud -#include -#include -#include - -#include - -#include - - -/* OpenGL headers */ -#if defined(__APPLE__) -# include -# if TARGET_OS_IPHONE -# include -# elif defined(USE_GLFW3) -# include -# include -# include -# endif -#elif defined(_WIN32) -# include -#elif defined(USE_GLFW3) -# define GLFW_INCLUDE_ES2 -# include -#else -# include -#endif - - -/* Uncomment to enable extra GL error checking */ -/* #define CHECK_GL_ERRORS */ -#if defined(CHECK_GL_ERRORS) -# warning CHECK_GL_ERRORS is enabled -# include -# define GLCHK(X) \ - do { \ - GLenum err = GL_NO_ERROR; \ - X; \ - while ((err = glGetError())) { \ - ULOGE("%s:%d: GL error 0x%x in " #X, \ - __func__, \ - __LINE__, \ - err); \ - assert(err == GL_NO_ERROR); \ - } \ - } while (0) -#else /* CHECK_GL_ERRORS */ -# define GLCHK(X) X -#endif /* CHECK_GL_ERRORS */ - - -#define RAD_TO_DEG (57.295779513f) - -/* Default FOV from Bebop 2 */ -#define PDRAW_GLES2HUD_DEFAULT_HFOV (78.) -#define PDRAW_GLES2HUD_DEFAULT_VFOV (49.) - - -extern const int hud_icons_width; -extern const int hud_icons_height; -extern const uint8_t hud_icons[]; - - -namespace profont_36 { - -struct glyph_info { - struct { /* pixel oriented data */ - int u, v; - int width, height; - int advance; - int offx, offy; - } pix; - struct { /* normalized data */ - float u, v; /* position in the map in normalized coords */ - float width, height; - float advance; - float offx, offy; - } norm; -}; - -struct file_header { - int texwidth, texheight; - struct { - int ascent; - int descent; - int linegap; - } pix; - struct { - float ascent; - float descent; - float linegap; - } norm; - glyph_info glyphs[256]; -}; - -extern file_header font; -extern int image_width; -extern int image_height; -extern unsigned char image[]; - -} /* namespace profont_36 */ - - -enum pdraw_gles2hud_text_align { - PDRAW_GLES2HUD_TEXT_ALIGN_LEFT = 0, - PDRAW_GLES2HUD_TEXT_ALIGN_TOP = 0, - PDRAW_GLES2HUD_TEXT_ALIGN_CENTER = 1, - PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE = 1, - PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT = 2, - PDRAW_GLES2HUD_TEXT_ALIGN_BOTTOM = 2, -}; - - -struct pdraw_gles2hud { - struct pdraw_gles2hud_config config; - - unsigned int first_texunit; - - float ratio_w; - float ratio_h; - float aspect_ratio; - float h_fov; - float v_fov; - - /* Shape drawing */ - GLint program; - GLint position_handle; - GLint transform_matrix_handle; - GLint color_handle; - - /* Texture drawing */ - GLint tex_program; - GLint tex_uniform_sampler; - GLint tex_position_handle; - GLint tex_texcoord_handle; - GLint tex_transform_matrix_handle; - GLint tex_color_handle; - GLuint icons_texture; - unsigned int icons_texunit; - GLuint text_texture; - unsigned int text_texunit; -}; - - -int pdraw_gles2hud_create_programs(struct pdraw_gles2hud *self); - - -void pdraw_gles2hud_draw_line(struct pdraw_gles2hud *self, - float x1, - float y1, - float x2, - float y2, - const float color[4], - float line_width); - - -void pdraw_gles2hud_draw_rect(struct pdraw_gles2hud *self, - float x1, - float y1, - float x2, - float y2, - const float color[4], - float line_width); - - -void pdraw_gles2hud_draw_filled_rect(struct pdraw_gles2hud *self, - float x1, - float y1, - float x2, - float y2, - const float color[4]); - - -void pdraw_gles2hud_draw_arc(struct pdraw_gles2hud *self, - float cx, - float cy, - float rx, - float ry, - float start_angle, - float span_angle, - int num_segments, - const float color[4], - float line_width); - - -void pdraw_gles2hud_draw_ellipse(struct pdraw_gles2hud *self, - float cx, - float cy, - float rx, - float ry, - int num_segments, - const float color[4], - float line_width); - - -void pdraw_gles2hud_draw_filled_ellipse(struct pdraw_gles2hud *self, - float cx, - float cy, - float rx, - float ry, - int num_segments, - const float color[4]); - - -void pdraw_gles2hud_draw_icon(struct pdraw_gles2hud *self, - int index, - float x, - float y, - float size, - float scalew, - float scaleh, - const float color[4]); - - -void pdraw_gles2hud_get_text_dimensions(struct pdraw_gles2hud *self, - const char *str, - float size, - float scalew, - float scaleh, - float *width, - float *height); - - -void pdraw_gles2hud_draw_text(struct pdraw_gles2hud *self, - const char *str, - float x, - float y, - float size, - float scalew, - float scaleh, - enum pdraw_gles2hud_text_align halign, - enum pdraw_gles2hud_text_align valign, - const float color[4]); - - -void pdraw_gles2hud_draw_vumeter(struct pdraw_gles2hud *self, - float x, - float y, - float r, - float value, - float val_min, - float val_max, - float critical_min, - float critical_max, - const float color[4], - const float critical_color[4]); - - -void pdraw_gles2hud_draw_artificial_horizon(struct pdraw_gles2hud *self, - const struct vmeta_euler *drone, - const struct vmeta_euler *frame, - const float color[4]); - - -void pdraw_gles2hud_draw_roll(struct pdraw_gles2hud *self, - float drone_roll, - const float color[4]); - - -void pdraw_gles2hud_draw_heading(struct pdraw_gles2hud *self, - float drone_yaw, - float horizontal_speed, - float speed_psi, - const float color[4]); - - -void pdraw_gles2hud_draw_altitude(struct pdraw_gles2hud *self, - double altitude, - float ground_distance, - float down_speed, - const float color[4]); - - -void pdraw_gles2hud_draw_speed(struct pdraw_gles2hud *self, - float horizontal_speed, - const float color[4]); - - -void pdraw_gles2hud_draw_controller_radar(struct pdraw_gles2hud *self, - double distance, - double bearing, - float controller_yaw, - float drone_yaw, - float controller_radar_angle, - const float color[4]); - - -void pdraw_gles2hud_draw_record_timeline(struct pdraw_gles2hud *self, - uint64_t current_time, - uint64_t duration, - const float color[4]); - - -void pdraw_gles2hud_draw_recording_status(struct pdraw_gles2hud *self, - uint64_t recording_duration, - const float color[4]); - - -void pdraw_gles2hud_draw_flight_path_vector(struct pdraw_gles2hud *self, - const struct vmeta_euler *frame, - float speed_theta, - float speed_psi, - const float color[4]); - - -void pdraw_gles2hud_draw_framing_grid(struct pdraw_gles2hud *self, - const struct pdraw_rect *render_pos, - const struct pdraw_rect *content_pos, - const float color[4]); - - -void pdraw_gles2hud_draw_histograms( - struct pdraw_gles2hud *self, - const struct pdraw_video_frame_extra *frame_extra); - - -static inline void pdraw_gles2hud_friendly_time_from_us(uint64_t time, - unsigned int *hrs, - unsigned int *min, - unsigned int *sec, - unsigned int *msec) -{ - unsigned int _hrs = - (unsigned int)((time + 500) / 1000 / 60 / 60) / 1000; - unsigned int _min = - (unsigned int)((time + 500) / 1000 / 60 - _hrs * 60000) / 1000; - unsigned int _sec = (unsigned int)((time + 500) / 1000 - - _hrs * 60 * 60000 - _min * 60000) / - 1000; - unsigned int _msec = - (unsigned int)((time + 500) / 1000 - _hrs * 60 * 60000 - - _min * 60000 - _sec * 1000); - if (hrs) - *hrs = _hrs; - if (min) - *min = _min; - if (sec) - *sec = _sec; - if (msec) - *msec = _msec; -} - - -static inline void pdraw_gles2hud_coords_distance_and_bearing(double latitude1, - double longitude1, - double latitude2, - double longitude2, - double *distance, - double *bearing) -{ - /* http://www.igismap.com/haversine-formula-calculate-geographic-distance-earth/ - */ - /* http://www.igismap.com/formula-to-find-bearing-or-heading-angle-between-two-points-latitude-longitude/ - */ - double a, c, d, x, y, b; - double r = 6371000.; /* earth radius */ - double lat1 = latitude1 * M_PI / 180.; - double lon1 = longitude1 * M_PI / 180.; - double lat2 = latitude2 * M_PI / 180.; - double lon2 = longitude2 * M_PI / 180.; - a = sin((lat2 - lat1) / 2) * sin((lat2 - lat1) / 2) + - cos(lat1) * cos(lat2) * sin((lon2 - lon1) / 2) * - sin((lon2 - lon1) / 2); - c = 2 * atan2(sqrt(a), sqrt(1. - a)); - d = r * c; - x = cos(lat2) * sin(lon2 - lon1); - y = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon2 - lon1); - b = atan2(x, y); - if (distance) - *distance = d; - if (bearing) - *bearing = b; -} - -#endif /* !_PDRAW_GLES2HUD_PRIV_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer + * OpenGL ES 2.0 HUD rendering library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_GLES2HUD_PRIV_HPP_ +#define _PDRAW_GLES2HUD_PRIV_HPP_ + +#include +#include +#include + +#define ULOG_TAG pdraw_gles2hud +#include +#include +#include + +#include + +#include + + +/* OpenGL headers */ +#if defined(__APPLE__) +# include +# if TARGET_OS_IPHONE +# include +# elif defined(USE_GLFW3) +# include +# include +# include +# endif +#elif defined(_WIN32) +# include +#elif defined(USE_GLFW3) +# define GLFW_INCLUDE_ES2 +# include +#else +# include +#endif + + +/* Uncomment to enable extra GL error checking */ +/* #define CHECK_GL_ERRORS */ +#if defined(CHECK_GL_ERRORS) +# warning CHECK_GL_ERRORS is enabled +# include +# define GLCHK(X) \ + do { \ + GLenum err = GL_NO_ERROR; \ + X; \ + while ((err = glGetError())) { \ + ULOGE("%s:%d: GL error 0x%x in " #X, \ + __func__, \ + __LINE__, \ + err); \ + assert(err == GL_NO_ERROR); \ + } \ + } while (0) +#else /* CHECK_GL_ERRORS */ +# define GLCHK(X) X +#endif /* CHECK_GL_ERRORS */ + + +#define RAD_TO_DEG (57.295779513f) + +/* Default FOV from Bebop 2 */ +#define PDRAW_GLES2HUD_DEFAULT_HFOV (78.) +#define PDRAW_GLES2HUD_DEFAULT_VFOV (49.) + + +extern const int hud_icons_width; +extern const int hud_icons_height; +extern const uint8_t hud_icons[]; + + +namespace profont_36 { + +struct glyph_info { + struct { /* pixel oriented data */ + int u, v; + int width, height; + int advance; + int offx, offy; + } pix; + struct { /* normalized data */ + float u, v; /* position in the map in normalized coords */ + float width, height; + float advance; + float offx, offy; + } norm; +}; + +struct file_header { + int texwidth, texheight; + struct { + int ascent; + int descent; + int linegap; + } pix; + struct { + float ascent; + float descent; + float linegap; + } norm; + glyph_info glyphs[256]; +}; + +extern file_header font; +extern int image_width; +extern int image_height; +extern unsigned char image[]; + +} /* namespace profont_36 */ + + +enum pdraw_gles2hud_text_align { + PDRAW_GLES2HUD_TEXT_ALIGN_LEFT = 0, + PDRAW_GLES2HUD_TEXT_ALIGN_TOP = 0, + PDRAW_GLES2HUD_TEXT_ALIGN_CENTER = 1, + PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE = 1, + PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT = 2, + PDRAW_GLES2HUD_TEXT_ALIGN_BOTTOM = 2, +}; + + +struct pdraw_gles2hud { + struct pdraw_gles2hud_config config; + + unsigned int first_texunit; + + float ratio_w; + float ratio_h; + float aspect_ratio; + float h_fov; + float v_fov; + + /* Shape drawing */ + GLint program; + GLint position_handle; + GLint transform_matrix_handle; + GLint color_handle; + + /* Texture drawing */ + GLint tex_program; + GLint tex_uniform_sampler; + GLint tex_position_handle; + GLint tex_texcoord_handle; + GLint tex_transform_matrix_handle; + GLint tex_color_handle; + GLuint icons_texture; + unsigned int icons_texunit; + GLuint text_texture; + unsigned int text_texunit; +}; + + +int pdraw_gles2hud_create_programs(struct pdraw_gles2hud *self); + + +void pdraw_gles2hud_draw_line(struct pdraw_gles2hud *self, + float x1, + float y1, + float x2, + float y2, + const float color[4], + float line_width); + + +void pdraw_gles2hud_draw_rect(struct pdraw_gles2hud *self, + float x1, + float y1, + float x2, + float y2, + const float color[4], + float line_width); + + +void pdraw_gles2hud_draw_filled_rect(struct pdraw_gles2hud *self, + float x1, + float y1, + float x2, + float y2, + const float color[4]); + + +void pdraw_gles2hud_draw_arc(struct pdraw_gles2hud *self, + float cx, + float cy, + float rx, + float ry, + float start_angle, + float span_angle, + int num_segments, + const float color[4], + float line_width); + + +void pdraw_gles2hud_draw_ellipse(struct pdraw_gles2hud *self, + float cx, + float cy, + float rx, + float ry, + int num_segments, + const float color[4], + float line_width); + + +void pdraw_gles2hud_draw_filled_ellipse(struct pdraw_gles2hud *self, + float cx, + float cy, + float rx, + float ry, + int num_segments, + const float color[4]); + + +void pdraw_gles2hud_draw_icon(struct pdraw_gles2hud *self, + int index, + float x, + float y, + float size, + float scalew, + float scaleh, + const float color[4]); + + +void pdraw_gles2hud_get_text_dimensions(struct pdraw_gles2hud *self, + const char *str, + float size, + float scalew, + float scaleh, + float *width, + float *height); + + +void pdraw_gles2hud_draw_text(struct pdraw_gles2hud *self, + const char *str, + float x, + float y, + float size, + float scalew, + float scaleh, + enum pdraw_gles2hud_text_align halign, + enum pdraw_gles2hud_text_align valign, + const float color[4]); + + +void pdraw_gles2hud_draw_vumeter(struct pdraw_gles2hud *self, + float x, + float y, + float r, + float value, + float val_min, + float val_max, + float critical_min, + float critical_max, + const float color[4], + const float critical_color[4]); + + +void pdraw_gles2hud_draw_artificial_horizon(struct pdraw_gles2hud *self, + const struct vmeta_euler *drone, + const struct vmeta_euler *frame, + const float color[4]); + + +void pdraw_gles2hud_draw_roll(struct pdraw_gles2hud *self, + float drone_roll, + const float color[4]); + + +void pdraw_gles2hud_draw_heading(struct pdraw_gles2hud *self, + float drone_yaw, + float horizontal_speed, + float speed_psi, + const float color[4]); + + +void pdraw_gles2hud_draw_altitude(struct pdraw_gles2hud *self, + double altitude, + float ground_distance, + float down_speed, + const float color[4]); + + +void pdraw_gles2hud_draw_speed(struct pdraw_gles2hud *self, + float horizontal_speed, + const float color[4]); + + +void pdraw_gles2hud_draw_controller_radar(struct pdraw_gles2hud *self, + double distance, + double bearing, + float controller_yaw, + float drone_yaw, + float controller_radar_angle, + const float color[4]); + + +void pdraw_gles2hud_draw_record_timeline(struct pdraw_gles2hud *self, + uint64_t current_time, + uint64_t duration, + const float color[4]); + + +void pdraw_gles2hud_draw_recording_status(struct pdraw_gles2hud *self, + uint64_t recording_duration, + const float color[4]); + + +void pdraw_gles2hud_draw_flight_path_vector(struct pdraw_gles2hud *self, + const struct vmeta_euler *frame, + float speed_theta, + float speed_psi, + const float color[4]); + + +void pdraw_gles2hud_draw_framing_grid(struct pdraw_gles2hud *self, + const struct pdraw_rect *render_pos, + const struct pdraw_rect *content_pos, + const float color[4]); + + +void pdraw_gles2hud_draw_histograms( + struct pdraw_gles2hud *self, + const struct pdraw_video_frame_extra *frame_extra); + + +static inline void pdraw_gles2hud_friendly_time_from_us(uint64_t time, + unsigned int *hrs, + unsigned int *min, + unsigned int *sec, + unsigned int *msec) +{ + unsigned int _hrs = + (unsigned int)((time + 500) / 1000 / 60 / 60) / 1000; + unsigned int _min = + (unsigned int)((time + 500) / 1000 / 60 - _hrs * 60000) / 1000; + unsigned int _sec = (unsigned int)((time + 500) / 1000 - + _hrs * 60 * 60000 - _min * 60000) / + 1000; + unsigned int _msec = + (unsigned int)((time + 500) / 1000 - _hrs * 60 * 60000 - + _min * 60000 - _sec * 1000); + if (hrs) + *hrs = _hrs; + if (min) + *min = _min; + if (sec) + *sec = _sec; + if (msec) + *msec = _msec; +} + + +static inline void pdraw_gles2hud_coords_distance_and_bearing(double latitude1, + double longitude1, + double latitude2, + double longitude2, + double *distance, + double *bearing) +{ + /* http://www.igismap.com/haversine-formula-calculate-geographic-distance-earth/ + */ + /* http://www.igismap.com/formula-to-find-bearing-or-heading-angle-between-two-points-latitude-longitude/ + */ + double a, c, d, x, y, b; + double r = 6371000.; /* earth radius */ + double lat1 = latitude1 * M_PI / 180.; + double lon1 = longitude1 * M_PI / 180.; + double lat2 = latitude2 * M_PI / 180.; + double lon2 = longitude2 * M_PI / 180.; + a = sin((lat2 - lat1) / 2) * sin((lat2 - lat1) / 2) + + cos(lat1) * cos(lat2) * sin((lon2 - lon1) / 2) * + sin((lon2 - lon1) / 2); + c = 2 * atan2(sqrt(a), sqrt(1. - a)); + d = r * c; + x = cos(lat2) * sin(lon2 - lon1); + y = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon2 - lon1); + b = atan2(x, y); + if (distance) + *distance = d; + if (bearing) + *bearing = b; +} + +#endif /* !_PDRAW_GLES2HUD_PRIV_HPP_ */ diff --git a/libpdraw-gles2hud/src/pdraw_gles2hud_shaders.cpp b/libpdraw-gles2hud/src/pdraw_gles2hud_shaders.cpp index 71323e3..be6baee 100644 --- a/libpdraw-gles2hud/src/pdraw_gles2hud_shaders.cpp +++ b/libpdraw-gles2hud/src/pdraw_gles2hud_shaders.cpp @@ -1,294 +1,294 @@ -/** - * Parrot Drones Awesome Video Viewer - * OpenGL ES 2.0 HUD rendering library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "pdraw_gles2hud_priv.h" - - -static const GLchar *hud_vertex_shader = - "uniform mat4 transform_matrix;\n" - "attribute vec4 vPosition;\n" - "void main() {\n" - " gl_Position = transform_matrix * vPosition;\n" - "}\n"; - -static const GLchar *hud_fragment_shader = -#if defined(GL_ES_VERSION_2_0) && \ - (defined(ANDROID) || defined(__APPLE__) || defined(MESON)) - "precision mediump float;\n" -#endif - "uniform vec4 vColor;\n" - "void main() {\n" - " gl_FragColor = vColor;\n" - "}\n"; - -static const GLchar *hud_tex_vertex_shader = - "uniform mat4 transform_matrix;\n" - "attribute vec4 position;\n" - "attribute vec2 texcoord;\n" - "varying vec2 v_texcoord;\n" - "\n" - "void main()\n" - "{\n" - " gl_Position = transform_matrix * position;\n" - " v_texcoord = texcoord;\n" - "}\n"; - -static const GLchar *hud_tex_fragment_shader = -#if defined(GL_ES_VERSION_2_0) && \ - (defined(ANDROID) || defined(__APPLE__) || defined(MESON)) - "precision mediump float;\n" -#endif - "uniform vec4 vColor;\n" - "varying vec2 v_texcoord;\n" - "uniform sampler2D s_texture;\n" - "\n" - "void main()\n" - "{\n" - " gl_FragColor = vec4(vColor.r, vColor.g, vColor.b, " - " texture2D(s_texture, v_texcoord).r);\n" - "}\n"; - - -static int pdraw_gles2hud_load_texture_from_buffer(struct pdraw_gles2hud *self, - const uint8_t *buffer, - int width, - int height, - int texunit) -{ - GLuint tex; - - if (buffer == nullptr) - return -EINVAL; - if ((width <= 0) || (height <= 0)) - return -EINVAL; - - GLCHK(glGenTextures(1, &tex)); - - GLCHK(glActiveTexture(GL_TEXTURE0 + texunit)); - GLCHK(glBindTexture(GL_TEXTURE_2D, tex)); - - GLCHK(glTexImage2D(GL_TEXTURE_2D, - 0, - GL_LUMINANCE, - width, - height, - 0, - GL_LUMINANCE, - GL_UNSIGNED_BYTE, - buffer)); - - GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GLCHK(glTexParameterf( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GLCHK(glTexParameterf( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - - return (int)tex; -} - - -int pdraw_gles2hud_create_programs(struct pdraw_gles2hud *self) -{ - int res; - GLint vertex_shader = 0, fragment_shader = 0; - GLint success = 0; - - GLCHK(); - - /* Shape drawing shaders */ - vertex_shader = glCreateShader(GL_VERTEX_SHADER); - if ((vertex_shader == 0) || (vertex_shader == GL_INVALID_ENUM)) { - ULOGE("failed to create vertex shader"); - goto error; - } - - glShaderSource(vertex_shader, 1, &hud_vertex_shader, nullptr); - glCompileShader(vertex_shader); - glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success); - if (!success) { - GLchar info_log[512]; - glGetShaderInfoLog(vertex_shader, 512, nullptr, info_log); - ULOGE("vertex shader compilation failed '%s'", info_log); - goto error; - } - - fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); - if ((fragment_shader == 0) || (fragment_shader == GL_INVALID_ENUM)) { - ULOGE("failed to create fragment shader"); - goto error; - } - - glShaderSource(fragment_shader, 1, &hud_fragment_shader, nullptr); - glCompileShader(fragment_shader); - glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success); - if (!success) { - GLchar info_log[512]; - glGetShaderInfoLog(fragment_shader, 512, nullptr, info_log); - ULOGE("fragment shader compilation failed '%s'", info_log); - goto error; - } - - self->program = glCreateProgram(); - glAttachShader(self->program, vertex_shader); - glAttachShader(self->program, fragment_shader); - glLinkProgram(self->program); - glGetProgramiv(self->program, GL_LINK_STATUS, &success); - if (!success) { - GLchar info_log[512]; - glGetProgramInfoLog(self->program, 512, nullptr, info_log); - ULOGE("program link failed '%s'", info_log); - goto error; - } - - glDeleteShader(vertex_shader); - vertex_shader = 0; - glDeleteShader(fragment_shader); - fragment_shader = 0; - - self->position_handle = glGetAttribLocation(self->program, "vPosition"); - self->transform_matrix_handle = - glGetUniformLocation(self->program, "transform_matrix"); - self->color_handle = glGetUniformLocation(self->program, "vColor"); - - /* Texture drawing shaders */ - vertex_shader = glCreateShader(GL_VERTEX_SHADER); - if ((vertex_shader == 0) || (vertex_shader == GL_INVALID_ENUM)) { - ULOGE("failed to create vertex shader"); - goto error; - } - - GLCHK(); - - glShaderSource(vertex_shader, 1, &hud_tex_vertex_shader, nullptr); - glCompileShader(vertex_shader); - glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success); - if (!success) { - GLchar info_log[512]; - glGetShaderInfoLog(vertex_shader, 512, nullptr, info_log); - ULOGE("vertex shader compilation failed '%s'", info_log); - goto error; - } - - fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); - if ((fragment_shader == 0) || (fragment_shader == GL_INVALID_ENUM)) { - ULOGE("failed to create fragment shader"); - goto error; - } - - glShaderSource(fragment_shader, 1, &hud_tex_fragment_shader, nullptr); - glCompileShader(fragment_shader); - glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success); - if (!success) { - GLchar info_log[512]; - glGetShaderInfoLog(fragment_shader, 512, nullptr, info_log); - ULOGE("fragment shader compilation failed '%s'", info_log); - goto error; - } - - self->tex_program = glCreateProgram(); - glAttachShader(self->tex_program, vertex_shader); - glAttachShader(self->tex_program, fragment_shader); - glLinkProgram(self->tex_program); - glGetProgramiv(self->tex_program, GL_LINK_STATUS, &success); - if (!success) { - GLchar info_log[512]; - glGetProgramInfoLog(self->tex_program, 512, nullptr, info_log); - ULOGE("program link failed '%s'", info_log); - goto error; - } - - glDeleteShader(vertex_shader); - vertex_shader = 0; - glDeleteShader(fragment_shader); - fragment_shader = 0; - - self->tex_uniform_sampler = - glGetUniformLocation(self->tex_program, "s_texture"); - self->tex_position_handle = - glGetAttribLocation(self->tex_program, "position"); - self->tex_texcoord_handle = - glGetAttribLocation(self->tex_program, "texcoord"); - self->tex_transform_matrix_handle = - glGetUniformLocation(self->tex_program, "transform_matrix"); - self->tex_color_handle = - glGetUniformLocation(self->tex_program, "vColor"); - - GLCHK(); - - self->icons_texunit = self->first_texunit; - res = pdraw_gles2hud_load_texture_from_buffer(self, - hud_icons, - hud_icons_width, - hud_icons_height, - self->icons_texunit); - if (res < 0) { - ULOG_ERRNO("pdraw_gles2hud_load_texture_from_buffer", -res); - goto error; - } - self->icons_texture = (GLuint)res; - - self->text_texunit = self->first_texunit + 1; - res = pdraw_gles2hud_load_texture_from_buffer(self, - profont_36::image, - profont_36::image_width, - profont_36::image_height, - self->text_texunit); - if (res < 0) { - ULOG_ERRNO("pdraw_gles2hud_load_texture_from_buffer", -res); - goto error; - } - self->text_texture = (GLuint)res; - - return 0; - -error: - if (vertex_shader != 0) - glDeleteShader(vertex_shader); - if (fragment_shader != 0) - glDeleteShader(fragment_shader); - if (self->program > 0) { - glDeleteProgram(self->program); - self->program = 0; - } - if (self->tex_program > 0) { - glDeleteProgram(self->tex_program); - self->tex_program = 0; - } - if (self->icons_texture > 0) { - glDeleteTextures(1, &self->icons_texture); - self->icons_texture = 0; - } - if (self->text_texture > 0) { - glDeleteTextures(1, &self->text_texture); - self->text_texture = 0; - } - return -EPROTO; -} +/** + * Parrot Drones Awesome Video Viewer + * OpenGL ES 2.0 HUD rendering library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 "pdraw_gles2hud_priv.h" + + +static const GLchar *hud_vertex_shader = + "uniform mat4 transform_matrix;\n" + "attribute vec4 vPosition;\n" + "void main() {\n" + " gl_Position = transform_matrix * vPosition;\n" + "}\n"; + +static const GLchar *hud_fragment_shader = +#if defined(GL_ES_VERSION_2_0) && \ + (defined(ANDROID) || defined(__APPLE__) || defined(MESON)) + "precision mediump float;\n" +#endif + "uniform vec4 vColor;\n" + "void main() {\n" + " gl_FragColor = vColor;\n" + "}\n"; + +static const GLchar *hud_tex_vertex_shader = + "uniform mat4 transform_matrix;\n" + "attribute vec4 position;\n" + "attribute vec2 texcoord;\n" + "varying vec2 v_texcoord;\n" + "\n" + "void main()\n" + "{\n" + " gl_Position = transform_matrix * position;\n" + " v_texcoord = texcoord;\n" + "}\n"; + +static const GLchar *hud_tex_fragment_shader = +#if defined(GL_ES_VERSION_2_0) && \ + (defined(ANDROID) || defined(__APPLE__) || defined(MESON)) + "precision mediump float;\n" +#endif + "uniform vec4 vColor;\n" + "varying vec2 v_texcoord;\n" + "uniform sampler2D s_texture;\n" + "\n" + "void main()\n" + "{\n" + " gl_FragColor = vec4(vColor.r, vColor.g, vColor.b, " + " texture2D(s_texture, v_texcoord).r);\n" + "}\n"; + + +static int pdraw_gles2hud_load_texture_from_buffer(struct pdraw_gles2hud *self, + const uint8_t *buffer, + int width, + int height, + int texunit) +{ + GLuint tex; + + if (buffer == nullptr) + return -EINVAL; + if ((width <= 0) || (height <= 0)) + return -EINVAL; + + GLCHK(glGenTextures(1, &tex)); + + GLCHK(glActiveTexture(GL_TEXTURE0 + texunit)); + GLCHK(glBindTexture(GL_TEXTURE_2D, tex)); + + GLCHK(glTexImage2D(GL_TEXTURE_2D, + 0, + GL_LUMINANCE, + width, + height, + 0, + GL_LUMINANCE, + GL_UNSIGNED_BYTE, + buffer)); + + GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GLCHK(glTexParameterf( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GLCHK(glTexParameterf( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + + return (int)tex; +} + + +int pdraw_gles2hud_create_programs(struct pdraw_gles2hud *self) +{ + int res; + GLint vertex_shader = 0, fragment_shader = 0; + GLint success = 0; + + GLCHK(); + + /* Shape drawing shaders */ + vertex_shader = glCreateShader(GL_VERTEX_SHADER); + if ((vertex_shader == 0) || (vertex_shader == GL_INVALID_ENUM)) { + ULOGE("failed to create vertex shader"); + goto error; + } + + glShaderSource(vertex_shader, 1, &hud_vertex_shader, nullptr); + glCompileShader(vertex_shader); + glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar info_log[512]; + glGetShaderInfoLog(vertex_shader, 512, nullptr, info_log); + ULOGE("vertex shader compilation failed '%s'", info_log); + goto error; + } + + fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + if ((fragment_shader == 0) || (fragment_shader == GL_INVALID_ENUM)) { + ULOGE("failed to create fragment shader"); + goto error; + } + + glShaderSource(fragment_shader, 1, &hud_fragment_shader, nullptr); + glCompileShader(fragment_shader); + glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar info_log[512]; + glGetShaderInfoLog(fragment_shader, 512, nullptr, info_log); + ULOGE("fragment shader compilation failed '%s'", info_log); + goto error; + } + + self->program = glCreateProgram(); + glAttachShader(self->program, vertex_shader); + glAttachShader(self->program, fragment_shader); + glLinkProgram(self->program); + glGetProgramiv(self->program, GL_LINK_STATUS, &success); + if (!success) { + GLchar info_log[512]; + glGetProgramInfoLog(self->program, 512, nullptr, info_log); + ULOGE("program link failed '%s'", info_log); + goto error; + } + + glDeleteShader(vertex_shader); + vertex_shader = 0; + glDeleteShader(fragment_shader); + fragment_shader = 0; + + self->position_handle = glGetAttribLocation(self->program, "vPosition"); + self->transform_matrix_handle = + glGetUniformLocation(self->program, "transform_matrix"); + self->color_handle = glGetUniformLocation(self->program, "vColor"); + + /* Texture drawing shaders */ + vertex_shader = glCreateShader(GL_VERTEX_SHADER); + if ((vertex_shader == 0) || (vertex_shader == GL_INVALID_ENUM)) { + ULOGE("failed to create vertex shader"); + goto error; + } + + GLCHK(); + + glShaderSource(vertex_shader, 1, &hud_tex_vertex_shader, nullptr); + glCompileShader(vertex_shader); + glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar info_log[512]; + glGetShaderInfoLog(vertex_shader, 512, nullptr, info_log); + ULOGE("vertex shader compilation failed '%s'", info_log); + goto error; + } + + fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + if ((fragment_shader == 0) || (fragment_shader == GL_INVALID_ENUM)) { + ULOGE("failed to create fragment shader"); + goto error; + } + + glShaderSource(fragment_shader, 1, &hud_tex_fragment_shader, nullptr); + glCompileShader(fragment_shader); + glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar info_log[512]; + glGetShaderInfoLog(fragment_shader, 512, nullptr, info_log); + ULOGE("fragment shader compilation failed '%s'", info_log); + goto error; + } + + self->tex_program = glCreateProgram(); + glAttachShader(self->tex_program, vertex_shader); + glAttachShader(self->tex_program, fragment_shader); + glLinkProgram(self->tex_program); + glGetProgramiv(self->tex_program, GL_LINK_STATUS, &success); + if (!success) { + GLchar info_log[512]; + glGetProgramInfoLog(self->tex_program, 512, nullptr, info_log); + ULOGE("program link failed '%s'", info_log); + goto error; + } + + glDeleteShader(vertex_shader); + vertex_shader = 0; + glDeleteShader(fragment_shader); + fragment_shader = 0; + + self->tex_uniform_sampler = + glGetUniformLocation(self->tex_program, "s_texture"); + self->tex_position_handle = + glGetAttribLocation(self->tex_program, "position"); + self->tex_texcoord_handle = + glGetAttribLocation(self->tex_program, "texcoord"); + self->tex_transform_matrix_handle = + glGetUniformLocation(self->tex_program, "transform_matrix"); + self->tex_color_handle = + glGetUniformLocation(self->tex_program, "vColor"); + + GLCHK(); + + self->icons_texunit = self->first_texunit; + res = pdraw_gles2hud_load_texture_from_buffer(self, + hud_icons, + hud_icons_width, + hud_icons_height, + self->icons_texunit); + if (res < 0) { + ULOG_ERRNO("pdraw_gles2hud_load_texture_from_buffer", -res); + goto error; + } + self->icons_texture = (GLuint)res; + + self->text_texunit = self->first_texunit + 1; + res = pdraw_gles2hud_load_texture_from_buffer(self, + profont_36::image, + profont_36::image_width, + profont_36::image_height, + self->text_texunit); + if (res < 0) { + ULOG_ERRNO("pdraw_gles2hud_load_texture_from_buffer", -res); + goto error; + } + self->text_texture = (GLuint)res; + + return 0; + +error: + if (vertex_shader != 0) + glDeleteShader(vertex_shader); + if (fragment_shader != 0) + glDeleteShader(fragment_shader); + if (self->program > 0) { + glDeleteProgram(self->program); + self->program = 0; + } + if (self->tex_program > 0) { + glDeleteProgram(self->tex_program); + self->tex_program = 0; + } + if (self->icons_texture > 0) { + glDeleteTextures(1, &self->icons_texture); + self->icons_texture = 0; + } + if (self->text_texture > 0) { + glDeleteTextures(1, &self->text_texture); + self->text_texture = 0; + } + return -EPROTO; +} diff --git a/libpdraw-gles2hud/src/pdraw_gles2hud_shapes.cpp b/libpdraw-gles2hud/src/pdraw_gles2hud_shapes.cpp index 1aef310..ec3098b 100644 --- a/libpdraw-gles2hud/src/pdraw_gles2hud_shapes.cpp +++ b/libpdraw-gles2hud/src/pdraw_gles2hud_shapes.cpp @@ -1,252 +1,252 @@ -/** - * Parrot Drones Awesome Video Viewer - * OpenGL ES 2.0 HUD rendering library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "pdraw_gles2hud_priv.h" - - -void pdraw_gles2hud_draw_line(struct pdraw_gles2hud *self, - float x1, - float y1, - float x2, - float y2, - const float color[4], - float line_width) -{ - float vertices[4]; - - vertices[0] = x1; - vertices[1] = y1; - vertices[2] = x2; - vertices[3] = y2; - - GLCHK(glLineWidth(line_width)); - - GLCHK(glVertexAttribPointer( - self->position_handle, 2, GL_FLOAT, false, 0, vertices)); - - GLCHK(glUniform4fv(self->color_handle, 1, color)); - - GLCHK(glDrawArrays(GL_LINES, 0, 2)); -} - - -void pdraw_gles2hud_draw_rect(struct pdraw_gles2hud *self, - float x1, - float y1, - float x2, - float y2, - const float color[4], - float line_width) -{ - float vertices[8]; - - vertices[0] = x1; - vertices[1] = y1; - vertices[2] = x1; - vertices[3] = y2; - vertices[4] = x2; - vertices[5] = y2; - vertices[6] = x2; - vertices[7] = y1; - - GLCHK(glLineWidth(line_width)); - - GLCHK(glVertexAttribPointer( - self->position_handle, 2, GL_FLOAT, false, 0, vertices)); - - GLCHK(glUniform4fv(self->color_handle, 1, color)); - - GLCHK(glDrawArrays(GL_LINE_LOOP, 0, 4)); -} - - -void pdraw_gles2hud_draw_filled_rect(struct pdraw_gles2hud *self, - float x1, - float y1, - float x2, - float y2, - const float color[4]) -{ - float vertices[8]; - - vertices[0] = x1; - vertices[1] = y1; - vertices[2] = x1; - vertices[3] = y2; - vertices[4] = x2; - vertices[5] = y1; - vertices[6] = x2; - vertices[7] = y2; - - GLCHK(glVertexAttribPointer( - self->position_handle, 2, GL_FLOAT, false, 0, vertices)); - - GLCHK(glUniform4fv(self->color_handle, 1, color)); - - GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); -} - - -void pdraw_gles2hud_draw_arc(struct pdraw_gles2hud *self, - float cx, - float cy, - float rx, - float ry, - float start_angle, - float span_angle, - int num_segments, - const float color[4], - float line_width) -{ - int i; - float theta = span_angle / (float)num_segments; - float c = cosf(theta); - float s = sinf(theta); - float t; - - float x = cosf(start_angle); - float y = sinf(start_angle); - - float vertices[2 * (num_segments + 1)]; - - for (i = 0; i <= num_segments; i++) { - vertices[2 * i] = x * rx + cx; - vertices[2 * i + 1] = y * ry + cy; - - /* apply the rotation */ - t = x; - x = c * x - s * y; - y = s * t + c * y; - } - - GLCHK(glLineWidth(line_width)); - - GLCHK(glVertexAttribPointer( - self->position_handle, 2, GL_FLOAT, false, 0, vertices)); - - GLCHK(glUniform4fv(self->color_handle, 1, color)); - - GLCHK(glDrawArrays(GL_LINE_STRIP, 0, num_segments + 1)); -} - - -void pdraw_gles2hud_draw_ellipse(struct pdraw_gles2hud *self, - float cx, - float cy, - float rx, - float ry, - int num_segments, - const float color[4], - float line_width) -{ - int i; - float theta = 2. * M_PI / (float)num_segments; - float c = cosf(theta); - float s = sinf(theta); - float t; - - /* start at angle = 0 */ - float x = 1.; - float y = 0.; - - float vertices[2 * num_segments]; - - for (i = 0; i < num_segments; i++) { - vertices[2 * i] = x * rx + cx; - vertices[2 * i + 1] = y * ry + cy; - - /* apply the rotation */ - t = x; - x = c * x - s * y; - y = s * t + c * y; - } - - GLCHK(glLineWidth(line_width)); - - GLCHK(glVertexAttribPointer( - self->position_handle, 2, GL_FLOAT, false, 0, vertices)); - - GLCHK(glUniform4fv(self->color_handle, 1, color)); - - GLCHK(glDrawArrays(GL_LINE_LOOP, 0, num_segments)); -} - - -void pdraw_gles2hud_draw_filled_ellipse(struct pdraw_gles2hud *self, - float cx, - float cy, - float rx, - float ry, - int num_segments, - const float color[4]) -{ - int i; - num_segments &= ~1; - float theta = 2. * M_PI / (float)num_segments; - float c = cosf(theta); - float s = sinf(theta); - float t; - - /* start at angle = 0 */ - float x = 1.; - float y = 0.; - - float vertices[(3 * (num_segments / 2) + 1) * 2]; - - for (i = 0; i < num_segments / 2; i++) { - vertices[6 * i] = x * rx + cx; - vertices[6 * i + 1] = y * ry + cy; - - /* apply the rotation */ - t = x; - x = c * x - s * y; - y = s * t + c * y; - - vertices[6 * i + 2] = cx; - vertices[6 * i + 3] = cy; - - vertices[6 * i + 4] = x * rx + cx; - vertices[6 * i + 5] = y * ry + cy; - - /* apply the rotation */ - t = x; - x = c * x - s * y; - y = s * t + c * y; - } - vertices[6 * i] = x * rx + cx; - vertices[6 * i + 1] = y * ry + cy; - - GLCHK(glVertexAttribPointer( - self->position_handle, 2, GL_FLOAT, false, 0, vertices)); - - GLCHK(glUniform4fv(self->color_handle, 1, color)); - - GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 3 * (num_segments / 2) + 1)); -} +/** + * Parrot Drones Awesome Video Viewer + * OpenGL ES 2.0 HUD rendering library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 "pdraw_gles2hud_priv.h" + + +void pdraw_gles2hud_draw_line(struct pdraw_gles2hud *self, + float x1, + float y1, + float x2, + float y2, + const float color[4], + float line_width) +{ + float vertices[4]; + + vertices[0] = x1; + vertices[1] = y1; + vertices[2] = x2; + vertices[3] = y2; + + GLCHK(glLineWidth(line_width)); + + GLCHK(glVertexAttribPointer( + self->position_handle, 2, GL_FLOAT, false, 0, vertices)); + + GLCHK(glUniform4fv(self->color_handle, 1, color)); + + GLCHK(glDrawArrays(GL_LINES, 0, 2)); +} + + +void pdraw_gles2hud_draw_rect(struct pdraw_gles2hud *self, + float x1, + float y1, + float x2, + float y2, + const float color[4], + float line_width) +{ + float vertices[8]; + + vertices[0] = x1; + vertices[1] = y1; + vertices[2] = x1; + vertices[3] = y2; + vertices[4] = x2; + vertices[5] = y2; + vertices[6] = x2; + vertices[7] = y1; + + GLCHK(glLineWidth(line_width)); + + GLCHK(glVertexAttribPointer( + self->position_handle, 2, GL_FLOAT, false, 0, vertices)); + + GLCHK(glUniform4fv(self->color_handle, 1, color)); + + GLCHK(glDrawArrays(GL_LINE_LOOP, 0, 4)); +} + + +void pdraw_gles2hud_draw_filled_rect(struct pdraw_gles2hud *self, + float x1, + float y1, + float x2, + float y2, + const float color[4]) +{ + float vertices[8]; + + vertices[0] = x1; + vertices[1] = y1; + vertices[2] = x1; + vertices[3] = y2; + vertices[4] = x2; + vertices[5] = y1; + vertices[6] = x2; + vertices[7] = y2; + + GLCHK(glVertexAttribPointer( + self->position_handle, 2, GL_FLOAT, false, 0, vertices)); + + GLCHK(glUniform4fv(self->color_handle, 1, color)); + + GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); +} + + +void pdraw_gles2hud_draw_arc(struct pdraw_gles2hud *self, + float cx, + float cy, + float rx, + float ry, + float start_angle, + float span_angle, + int num_segments, + const float color[4], + float line_width) +{ + int i; + float theta = span_angle / (float)num_segments; + float c = cosf(theta); + float s = sinf(theta); + float t; + + float x = cosf(start_angle); + float y = sinf(start_angle); + + float vertices[2 * (num_segments + 1)]; + + for (i = 0; i <= num_segments; i++) { + vertices[2 * i] = x * rx + cx; + vertices[2 * i + 1] = y * ry + cy; + + /* apply the rotation */ + t = x; + x = c * x - s * y; + y = s * t + c * y; + } + + GLCHK(glLineWidth(line_width)); + + GLCHK(glVertexAttribPointer( + self->position_handle, 2, GL_FLOAT, false, 0, vertices)); + + GLCHK(glUniform4fv(self->color_handle, 1, color)); + + GLCHK(glDrawArrays(GL_LINE_STRIP, 0, num_segments + 1)); +} + + +void pdraw_gles2hud_draw_ellipse(struct pdraw_gles2hud *self, + float cx, + float cy, + float rx, + float ry, + int num_segments, + const float color[4], + float line_width) +{ + int i; + float theta = 2. * M_PI / (float)num_segments; + float c = cosf(theta); + float s = sinf(theta); + float t; + + /* start at angle = 0 */ + float x = 1.; + float y = 0.; + + float vertices[2 * num_segments]; + + for (i = 0; i < num_segments; i++) { + vertices[2 * i] = x * rx + cx; + vertices[2 * i + 1] = y * ry + cy; + + /* apply the rotation */ + t = x; + x = c * x - s * y; + y = s * t + c * y; + } + + GLCHK(glLineWidth(line_width)); + + GLCHK(glVertexAttribPointer( + self->position_handle, 2, GL_FLOAT, false, 0, vertices)); + + GLCHK(glUniform4fv(self->color_handle, 1, color)); + + GLCHK(glDrawArrays(GL_LINE_LOOP, 0, num_segments)); +} + + +void pdraw_gles2hud_draw_filled_ellipse(struct pdraw_gles2hud *self, + float cx, + float cy, + float rx, + float ry, + int num_segments, + const float color[4]) +{ + int i; + num_segments &= ~1; + float theta = 2. * M_PI / (float)num_segments; + float c = cosf(theta); + float s = sinf(theta); + float t; + + /* start at angle = 0 */ + float x = 1.; + float y = 0.; + + float vertices[(3 * (num_segments / 2) + 1) * 2]; + + for (i = 0; i < num_segments / 2; i++) { + vertices[6 * i] = x * rx + cx; + vertices[6 * i + 1] = y * ry + cy; + + /* apply the rotation */ + t = x; + x = c * x - s * y; + y = s * t + c * y; + + vertices[6 * i + 2] = cx; + vertices[6 * i + 3] = cy; + + vertices[6 * i + 4] = x * rx + cx; + vertices[6 * i + 5] = y * ry + cy; + + /* apply the rotation */ + t = x; + x = c * x - s * y; + y = s * t + c * y; + } + vertices[6 * i] = x * rx + cx; + vertices[6 * i + 1] = y * ry + cy; + + GLCHK(glVertexAttribPointer( + self->position_handle, 2, GL_FLOAT, false, 0, vertices)); + + GLCHK(glUniform4fv(self->color_handle, 1, color)); + + GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 3 * (num_segments / 2) + 1)); +} diff --git a/libpdraw-gles2hud/src/pdraw_gles2hud_text.cpp b/libpdraw-gles2hud/src/pdraw_gles2hud_text.cpp index 8dd6e5f..b90032a 100644 --- a/libpdraw-gles2hud/src/pdraw_gles2hud_text.cpp +++ b/libpdraw-gles2hud/src/pdraw_gles2hud_text.cpp @@ -1,159 +1,159 @@ -/** - * Parrot Drones Awesome Video Viewer - * OpenGL ES 2.0 HUD rendering library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "pdraw_gles2hud_priv.h" - - -void pdraw_gles2hud_get_text_dimensions(struct pdraw_gles2hud *self, - const char *str, - float size, - float scalew, - float scaleh, - float *width, - float *height) -{ - float w, h; - - profont_36::file_header *glyph_info = &profont_36::font; - float cx = 0.; - const char *c = str; - while (*c != '\0') { - if (*c == '\n') { - break; - } else { - profont_36::glyph_info &g = - glyph_info->glyphs[(int)(*c)]; - cx += g.norm.advance; - } - c++; - } - w = cx; - h = glyph_info->norm.ascent + glyph_info->norm.descent; - /* + glyph_info->norm.linegap; */ - w *= size * scalew; - h *= size * scaleh; - - if (width) - *width = w; - if (height) - *height = h; -} - - -void pdraw_gles2hud_draw_text(struct pdraw_gles2hud *self, - const char *str, - float x, - float y, - float size, - float scalew, - float scaleh, - enum pdraw_gles2hud_text_align halign, - enum pdraw_gles2hud_text_align valign, - const float color[4]) -{ - float w, h; - float vertices[8]; - float texcoords[8]; - profont_36::file_header *glyph_info = &profont_36::font; - - pdraw_gles2hud_get_text_dimensions( - self, str, size, scalew, scaleh, &w, &h); - - switch (halign) { - default: - case PDRAW_GLES2HUD_TEXT_ALIGN_LEFT: - break; - case PDRAW_GLES2HUD_TEXT_ALIGN_CENTER: - x -= w / 2; - break; - case PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT: - x -= w; - break; - } - - switch (valign) { - default: - case PDRAW_GLES2HUD_TEXT_ALIGN_TOP: - y -= h; - break; - case PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE: - y -= h / 2; - break; - case PDRAW_GLES2HUD_TEXT_ALIGN_BOTTOM: - break; - } - - GLCHK(glUniform4fv(self->tex_color_handle, 1, color)); - - const char *c = str; - float cx = 0.; - while (*c != '\0') { - if (*c == '\n') - break; - - profont_36::glyph_info &g = glyph_info->glyphs[(int)(*c)]; - vertices[0] = x + cx + g.norm.offx * size * scalew; - vertices[1] = y - g.norm.offy * size * scaleh; - vertices[2] = - x + cx + (g.norm.offx + g.norm.width) * size * scalew; - vertices[3] = y - g.norm.offy * size * scaleh; - vertices[4] = x + cx + g.norm.offx * size * scalew; - vertices[5] = y - (g.norm.offy + g.norm.height) * size * scaleh; - vertices[6] = - x + cx + (g.norm.offx + g.norm.width) * size * scalew; - vertices[7] = y - (g.norm.offy + g.norm.height) * size * scaleh; - - texcoords[0] = g.norm.u; - texcoords[1] = g.norm.v + g.norm.height; - texcoords[2] = g.norm.u + g.norm.width; - texcoords[3] = g.norm.v + g.norm.height; - texcoords[4] = g.norm.u; - texcoords[5] = g.norm.v; - texcoords[6] = g.norm.u + g.norm.width; - texcoords[7] = g.norm.v; - - GLCHK(glVertexAttribPointer(self->tex_position_handle, - 2, - GL_FLOAT, - false, - 0, - vertices)); - GLCHK(glVertexAttribPointer(self->tex_texcoord_handle, - 2, - GL_FLOAT, - false, - 0, - texcoords)); - GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - - cx += g.norm.advance * size * scalew; - c++; - } -} +/** + * Parrot Drones Awesome Video Viewer + * OpenGL ES 2.0 HUD rendering library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 "pdraw_gles2hud_priv.h" + + +void pdraw_gles2hud_get_text_dimensions(struct pdraw_gles2hud *self, + const char *str, + float size, + float scalew, + float scaleh, + float *width, + float *height) +{ + float w, h; + + profont_36::file_header *glyph_info = &profont_36::font; + float cx = 0.; + const char *c = str; + while (*c != '\0') { + if (*c == '\n') { + break; + } else { + profont_36::glyph_info &g = + glyph_info->glyphs[(int)(*c)]; + cx += g.norm.advance; + } + c++; + } + w = cx; + h = glyph_info->norm.ascent + glyph_info->norm.descent; + /* + glyph_info->norm.linegap; */ + w *= size * scalew; + h *= size * scaleh; + + if (width) + *width = w; + if (height) + *height = h; +} + + +void pdraw_gles2hud_draw_text(struct pdraw_gles2hud *self, + const char *str, + float x, + float y, + float size, + float scalew, + float scaleh, + enum pdraw_gles2hud_text_align halign, + enum pdraw_gles2hud_text_align valign, + const float color[4]) +{ + float w, h; + float vertices[8]; + float texcoords[8]; + profont_36::file_header *glyph_info = &profont_36::font; + + pdraw_gles2hud_get_text_dimensions( + self, str, size, scalew, scaleh, &w, &h); + + switch (halign) { + default: + case PDRAW_GLES2HUD_TEXT_ALIGN_LEFT: + break; + case PDRAW_GLES2HUD_TEXT_ALIGN_CENTER: + x -= w / 2; + break; + case PDRAW_GLES2HUD_TEXT_ALIGN_RIGHT: + x -= w; + break; + } + + switch (valign) { + default: + case PDRAW_GLES2HUD_TEXT_ALIGN_TOP: + y -= h; + break; + case PDRAW_GLES2HUD_TEXT_ALIGN_MIDDLE: + y -= h / 2; + break; + case PDRAW_GLES2HUD_TEXT_ALIGN_BOTTOM: + break; + } + + GLCHK(glUniform4fv(self->tex_color_handle, 1, color)); + + const char *c = str; + float cx = 0.; + while (*c != '\0') { + if (*c == '\n') + break; + + profont_36::glyph_info &g = glyph_info->glyphs[(int)(*c)]; + vertices[0] = x + cx + g.norm.offx * size * scalew; + vertices[1] = y - g.norm.offy * size * scaleh; + vertices[2] = + x + cx + (g.norm.offx + g.norm.width) * size * scalew; + vertices[3] = y - g.norm.offy * size * scaleh; + vertices[4] = x + cx + g.norm.offx * size * scalew; + vertices[5] = y - (g.norm.offy + g.norm.height) * size * scaleh; + vertices[6] = + x + cx + (g.norm.offx + g.norm.width) * size * scalew; + vertices[7] = y - (g.norm.offy + g.norm.height) * size * scaleh; + + texcoords[0] = g.norm.u; + texcoords[1] = g.norm.v + g.norm.height; + texcoords[2] = g.norm.u + g.norm.width; + texcoords[3] = g.norm.v + g.norm.height; + texcoords[4] = g.norm.u; + texcoords[5] = g.norm.v; + texcoords[6] = g.norm.u + g.norm.width; + texcoords[7] = g.norm.v; + + GLCHK(glVertexAttribPointer(self->tex_position_handle, + 2, + GL_FLOAT, + false, + 0, + vertices)); + GLCHK(glVertexAttribPointer(self->tex_texcoord_handle, + 2, + GL_FLOAT, + false, + 0, + texcoords)); + GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + + cx += g.norm.advance * size * scalew; + c++; + } +} diff --git a/libpdraw-gles2hud/src/pdraw_gles2hud_text_profont36.cpp b/libpdraw-gles2hud/src/pdraw_gles2hud_text_profont36.cpp index b2ef9cb..81e5722 100644 --- a/libpdraw-gles2hud/src/pdraw_gles2hud_text_profont36.cpp +++ b/libpdraw-gles2hud/src/pdraw_gles2hud_text_profont36.cpp @@ -1,6566 +1,6566 @@ -/** - * Parrot Drones Awesome Video Viewer - * OpenGL ES 2.0 HUD rendering library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "pdraw_gles2hud_priv.h" - - -/** - * ProFont by Andrew Welch, Carl Osterwald and Stephen C. Gilardi - * http://tobiasjung.name/profont/ - */ - -namespace profont_36 { - -file_header font = { - 256, - 202, - {27, -9, 18}, - {0.134f, -0.045f, 0.089f}, - { - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {218, 136, 0, 0, 18, 0, 0}, - {0.852f, - 0.673f, - 0.000f, - 0.000f, - 0.070f, - 0.000f, - 0.000f}, - }, - { - /*!*/ - {51, 103, 3, 21, 18, 6, -21}, - {0.199f, - 0.510f, - 0.012f, - 0.104f, - 0.070f, - 0.023f, - -0.104f}, - }, - { - /*"*/ - {121, 136, 9, 9, 18, 3, -24}, - {0.473f, - 0.673f, - 0.035f, - 0.045f, - 0.070f, - 0.012f, - -0.119f}, - }, - { - /*#*/ - {73, 103, 15, 15, 18, 0, -21}, - {0.285f, - 0.510f, - 0.059f, - 0.074f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*$*/ - {90, 103, 15, 27, 18, 0, -24}, - {0.352f, - 0.510f, - 0.059f, - 0.134f, - 0.070f, - 0.000f, - -0.119f}, - }, - { - /*%*/ - {107, 103, 15, 21, 18, 0, -21}, - {0.418f, - 0.510f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*&*/ - {142, 103, 16, 22, 18, 0, -21}, - {0.555f, - 0.510f, - 0.063f, - 0.109f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*'*/ - {108, 136, 3, 9, 18, 6, -24}, - {0.422f, - 0.673f, - 0.012f, - 0.045f, - 0.070f, - 0.023f, - -0.119f}, - }, - { - /*(*/ - {177, 103, 10, 29, 18, 3, -25}, - {0.691f, - 0.510f, - 0.039f, - 0.144f, - 0.070f, - 0.012f, - -0.124f}, - }, - { - /*)*/ - {189, 103, 10, 29, 18, 2, -25}, - {0.738f, - 0.510f, - 0.039f, - 0.144f, - 0.070f, - 0.008f, - -0.124f}, - }, - { - /***/ - {160, 103, 15, 15, 18, 0, -21}, - {0.625f, - 0.510f, - 0.059f, - 0.074f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*+*/ - {44, 136, 15, 15, 18, 0, -18}, - {0.172f, - 0.673f, - 0.059f, - 0.074f, - 0.070f, - 0.000f, - -0.089f}, - }, - { - /*,*/ - {201, 136, 7, 13, 18, 3, -7}, - {0.785f, - 0.673f, - 0.027f, - 0.064f, - 0.070f, - 0.012f, - -0.035f}, - }, - { - /*-*/ - {201, 103, 9, 3, 18, 3, -12}, - {0.785f, - 0.510f, - 0.035f, - 0.015f, - 0.070f, - 0.012f, - -0.059f}, - }, - { - /*.*/ - {210, 136, 6, 6, 18, 4, -7}, - {0.820f, - 0.673f, - 0.023f, - 0.030f, - 0.070f, - 0.016f, - -0.035f}, - }, - { - /*/*/ - {164, 136, 18, 30, 18, 0, -24}, - {0.641f, - 0.673f, - 0.070f, - 0.149f, - 0.070f, - 0.000f, - -0.119f}, - }, - { - /*0*/ - {134, 80, 15, 21, 18, 0, -21}, - {0.523f, - 0.396f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*1*/ - {151, 80, 15, 21, 18, 0, -21}, - {0.590f, - 0.396f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*2*/ - {168, 80, 15, 21, 18, 0, -21}, - {0.656f, - 0.396f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*3*/ - {185, 80, 15, 21, 18, 0, -21}, - {0.723f, - 0.396f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*4*/ - {202, 80, 15, 21, 18, 0, -21}, - {0.789f, - 0.396f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*5*/ - {219, 80, 15, 21, 18, 0, -21}, - {0.855f, - 0.396f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*6*/ - {236, 80, 15, 21, 18, 0, -21}, - {0.922f, - 0.396f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*7*/ - {0, 103, 15, 21, 18, 0, -21}, - {0.000f, - 0.510f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*8*/ - {17, 103, 15, 21, 18, 0, -21}, - {0.066f, - 0.510f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*9*/ - {34, 103, 15, 21, 18, 0, -21}, - {0.133f, - 0.510f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*:*/ - {113, 136, 6, 15, 18, 4, -16}, - {0.441f, - 0.673f, - 0.023f, - 0.074f, - 0.070f, - 0.016f, - -0.079f}, - }, - { - /*;*/ - {99, 136, 7, 22, 18, 3, -16}, - {0.387f, - 0.673f, - 0.027f, - 0.109f, - 0.070f, - 0.012f, - -0.079f}, - }, - { - /*<*/ - {132, 136, 14, 23, 18, 2, -22}, - {0.516f, - 0.673f, - 0.055f, - 0.114f, - 0.070f, - 0.008f, - -0.109f}, - }, - { - /*=*/ - {27, 136, 15, 9, 18, 0, -15}, - {0.105f, - 0.673f, - 0.059f, - 0.045f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /*>*/ - {148, 136, 14, 23, 18, 2, -22}, - {0.578f, - 0.673f, - 0.055f, - 0.114f, - 0.070f, - 0.008f, - -0.109f}, - }, - { - /*?*/ - {184, 136, 15, 21, 18, 0, -21}, - {0.719f, - 0.673f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*@*/ - {56, 103, 15, 21, 18, 0, -21}, - {0.219f, - 0.510f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*A*/ - {1, 1, 15, 21, 18, 0, -21}, - {0.004f, - 0.005f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*B*/ - {18, 1, 15, 21, 18, 0, -21}, - {0.070f, - 0.005f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*C*/ - {35, 1, 15, 21, 18, 0, -21}, - {0.137f, - 0.005f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*D*/ - {52, 1, 15, 21, 18, 0, -21}, - {0.203f, - 0.005f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*E*/ - {69, 1, 15, 21, 18, 0, -21}, - {0.270f, - 0.005f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*F*/ - {86, 1, 15, 21, 18, 0, -21}, - {0.336f, - 0.005f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*G*/ - {103, 1, 15, 21, 18, 0, -21}, - {0.402f, - 0.005f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*H*/ - {120, 1, 15, 21, 18, 0, -21}, - {0.469f, - 0.005f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*I*/ - {137, 1, 15, 21, 18, 0, -21}, - {0.535f, - 0.005f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*J*/ - {154, 1, 15, 21, 18, 0, -21}, - {0.602f, - 0.005f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*K*/ - {171, 1, 15, 21, 18, 0, -21}, - {0.668f, - 0.005f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*L*/ - {188, 1, 15, 21, 18, 0, -21}, - {0.734f, - 0.005f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*M*/ - {205, 1, 15, 21, 18, 0, -21}, - {0.801f, - 0.005f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*N*/ - {222, 1, 15, 21, 18, 0, -21}, - {0.867f, - 0.005f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*O*/ - {0, 24, 15, 21, 18, 0, -21}, - {0.000f, - 0.119f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*P*/ - {17, 24, 15, 21, 18, 0, -21}, - {0.066f, - 0.119f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*Q*/ - {34, 24, 16, 25, 18, 0, -21}, - {0.133f, - 0.119f, - 0.063f, - 0.124f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*R*/ - {52, 24, 15, 21, 18, 0, -21}, - {0.203f, - 0.119f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*S*/ - {69, 24, 15, 21, 18, 0, -21}, - {0.270f, - 0.119f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*T*/ - {86, 24, 15, 21, 18, 0, -21}, - {0.336f, - 0.119f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*U*/ - {103, 24, 15, 21, 18, 0, -21}, - {0.402f, - 0.119f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*V*/ - {120, 24, 15, 21, 18, 0, -21}, - {0.469f, - 0.119f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*W*/ - {137, 24, 15, 21, 18, 0, -21}, - {0.535f, - 0.119f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*X*/ - {154, 24, 15, 21, 18, 0, -21}, - {0.602f, - 0.119f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*Y*/ - {171, 24, 15, 21, 18, 0, -21}, - {0.668f, - 0.119f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*Z*/ - {188, 24, 15, 21, 18, 0, -21}, - {0.734f, - 0.119f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*[*/ - {61, 136, 6, 27, 18, 6, -24}, - {0.238f, - 0.673f, - 0.023f, - 0.134f, - 0.070f, - 0.023f, - -0.119f}, - }, - { - /*\*/ - {232, 103, 17, 31, 18, 1, -25}, - {0.906f, - 0.510f, - 0.066f, - 0.153f, - 0.070f, - 0.004f, - -0.124f}, - }, - { - /*]*/ - {69, 136, 6, 27, 18, 3, -24}, - {0.270f, - 0.673f, - 0.023f, - 0.134f, - 0.070f, - 0.012f, - -0.119f}, - }, - { - /*^*/ - {124, 103, 16, 11, 18, 0, -22}, - {0.484f, - 0.510f, - 0.063f, - 0.054f, - 0.070f, - 0.000f, - -0.109f}, - }, - { - /*_*/ - {212, 103, 18, 3, 18, 0, 3}, - {0.828f, - 0.510f, - 0.070f, - 0.015f, - 0.070f, - 0.000f, - 0.015f}, - }, - { - /*`*/ - {0, 136, 8, 7, 18, 2, -24}, - {0.000f, - 0.673f, - 0.031f, - 0.035f, - 0.070f, - 0.008f, - -0.119f}, - }, - { - /*a*/ - {205, 24, 15, 15, 18, 0, -15}, - {0.801f, - 0.119f, - 0.059f, - 0.074f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /*b*/ - {222, 24, 15, 21, 18, 0, -21}, - {0.867f, - 0.119f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*c*/ - {0, 51, 15, 15, 18, 0, -15}, - {0.000f, - 0.252f, - 0.059f, - 0.074f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /*d*/ - {17, 51, 15, 21, 18, 0, -21}, - {0.066f, - 0.252f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*e*/ - {34, 51, 15, 15, 18, 0, -15}, - {0.133f, - 0.252f, - 0.059f, - 0.074f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /*f*/ - {51, 51, 12, 21, 18, 3, -21}, - {0.199f, - 0.252f, - 0.047f, - 0.104f, - 0.070f, - 0.012f, - -0.104f}, - }, - { - /*g*/ - {65, 51, 15, 21, 18, 0, -15}, - {0.254f, - 0.252f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /*h*/ - {82, 51, 15, 21, 18, 0, -21}, - {0.320f, - 0.252f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*i*/ - {99, 51, 9, 21, 18, 3, -21}, - {0.387f, - 0.252f, - 0.035f, - 0.104f, - 0.070f, - 0.012f, - -0.104f}, - }, - { - /*j*/ - {110, 51, 9, 27, 18, 0, -21}, - {0.430f, - 0.252f, - 0.035f, - 0.134f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*k*/ - {121, 51, 15, 21, 18, 0, -21}, - {0.473f, - 0.252f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.104f}, - }, - { - /*l*/ - {138, 51, 9, 21, 18, 3, -21}, - {0.539f, - 0.252f, - 0.035f, - 0.104f, - 0.070f, - 0.012f, - -0.104f}, - }, - { - /*m*/ - {149, 51, 15, 15, 18, 0, -15}, - {0.582f, - 0.252f, - 0.059f, - 0.074f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /*n*/ - {166, 51, 15, 15, 18, 0, -15}, - {0.648f, - 0.252f, - 0.059f, - 0.074f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /*o*/ - {183, 51, 15, 15, 18, 0, -15}, - {0.715f, - 0.252f, - 0.059f, - 0.074f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /*p*/ - {200, 51, 15, 21, 18, 0, -15}, - {0.781f, - 0.252f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /*q*/ - {217, 51, 15, 21, 18, 0, -15}, - {0.848f, - 0.252f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /*r*/ - {234, 51, 15, 15, 18, 0, -15}, - {0.914f, - 0.252f, - 0.059f, - 0.074f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /*s*/ - {0, 80, 16, 15, 18, 0, -15}, - {0.000f, - 0.396f, - 0.063f, - 0.074f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /*t*/ - {18, 80, 12, 21, 18, 3, -21}, - {0.070f, - 0.396f, - 0.047f, - 0.104f, - 0.070f, - 0.012f, - -0.104f}, - }, - { - /*u*/ - {32, 80, 15, 15, 18, 0, -15}, - {0.125f, - 0.396f, - 0.059f, - 0.074f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /*v*/ - {49, 80, 15, 15, 18, 0, -15}, - {0.191f, - 0.396f, - 0.059f, - 0.074f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /*w*/ - {66, 80, 15, 15, 18, 0, -15}, - {0.258f, - 0.396f, - 0.059f, - 0.074f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /*x*/ - {83, 80, 15, 15, 18, 0, -15}, - {0.324f, - 0.396f, - 0.059f, - 0.074f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /*y*/ - {100, 80, 15, 21, 18, 0, -15}, - {0.391f, - 0.396f, - 0.059f, - 0.104f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /*z*/ - {117, 80, 15, 15, 18, 0, -15}, - {0.457f, - 0.396f, - 0.059f, - 0.074f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /*{*/ - {77, 136, 9, 33, 18, 3, -27}, - {0.301f, - 0.673f, - 0.035f, - 0.163f, - 0.070f, - 0.012f, - -0.134f}, - }, - { - /*|*/ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /*}*/ - {88, 136, 9, 33, 18, 3, -27}, - {0.344f, - 0.673f, - 0.035f, - 0.163f, - 0.070f, - 0.012f, - -0.134f}, - }, - { - /*~*/ - {10, 136, 15, 6, 18, 0, -15}, - {0.039f, - 0.673f, - 0.059f, - 0.030f, - 0.070f, - 0.000f, - -0.074f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - { - /* */ - {0, 0, 0, 0, 0, 0, 0}, - {0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f, - 0.000f}, - }, - }, -}; - -int image_width = 256; -int image_height = 202; -unsigned char image[] = { - // gray-scale image - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 244, 203, 113, 8, 0, 0, 0, 0, 0, 0, 7, - 111, 203, 244, 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, 0, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 244, 202, 110, 6, 0, - 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 111, 203, - 244, 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 0, 0, 0, 0, 7, 111, 203, 244, 255, 255, 255, 244, - 203, 111, 7, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 128, 255, 255, 255, 0, 0, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 208, 27, 0, 0, - 0, 0, 25, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, - 24, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 203, 24, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 25, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 24, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 0, 0, 0, 25, 206, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 205, 25, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 161, 0, 0, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 208, 7, 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 203, 6, 0, 0, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 203, 6, 0, 0, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 203, 6, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 7, 206, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 205, 7, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, - 159, 2, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 15, 110, 249, 255, 255, 112, 0, 0, 111, 255, 255, 248, 108, - 15, 0, 0, 0, 15, 110, 249, 255, 255, 109, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 15, 110, 249, 255, 255, 109, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 248, 108, 15, 0, - 0, 0, 15, 110, 249, 255, 255, 109, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 111, 255, 255, 248, 108, 15, 0, 0, 0, 15, 110, 249, - 255, 255, 111, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 128, - 255, 255, 255, 158, 2, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 202, 0, 0, 203, - 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 201, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 111, - 255, 255, 201, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, 255, 255, - 108, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 201, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 203, 255, 255, 108, 0, 0, 0, 0, - 0, 0, 0, 111, 255, 255, 202, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 128, 255, 255, 255, 158, 2, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, - 243, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, - 16, 255, 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 16, 255, 255, 243, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, 255, - 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 15, - 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 243, 0, 0, 255, - 255, 255, 0, 0, 0, 128, 255, 255, 255, 157, 1, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 16, 255, 255, 245, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 255, 255, 255, 0, 0, 128, 255, 255, 255, 156, 1, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 113, 255, 255, 195, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 128, 255, 255, - 255, 156, 1, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 143, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 16, 113, 250, 255, 255, 83, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, - 128, 255, 255, 255, 155, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 143, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, - 255, 255, 64, 0, 0, 0, 0, 0, 0, 0, 64, 255, 255, 252, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 148, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 255, 255, 255, 255, 255, 255, 154, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 143, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 160, 255, 255, 192, 0, 0, 0, 0, 0, 0, 0, - 192, 255, 255, 160, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 196, 1, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, - 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 216, 3, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 143, 255, 255, 255, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 36, 252, 255, 255, 64, 0, 0, - 0, 0, 0, 64, 255, 255, 252, 36, 0, 0, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 149, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, - 255, 154, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, - 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 143, 255, 255, - 255, 143, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, - 255, 192, 0, 0, 0, 0, 0, 192, 255, 255, 160, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 15, 110, 249, 255, 255, - 83, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, - 255, 255, 128, 255, 255, 255, 154, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 8, 255, 255, 255, - 8, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, - 143, 255, 255, 255, 143, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 35, 251, 255, 255, 64, 0, 0, 0, 64, 255, 255, 252, - 36, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 111, 255, 255, 196, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 255, 255, 255, 0, 128, 255, 255, 255, 154, 1, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 8, - 184, 255, 255, 255, 184, 8, 0, 255, 255, 255, 0, 0, 255, 255, - 255, 0, 0, 143, 255, 255, 255, 143, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 192, 0, 0, 0, - 192, 255, 255, 161, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 16, 255, 255, 245, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 128, 255, - 255, 255, 154, 1, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 8, 184, 255, 255, 255, 255, 255, 184, 8, 255, 255, 255, - 0, 0, 255, 255, 255, 0, 143, 255, 255, 255, 143, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 251, 255, - 255, 64, 0, 64, 255, 255, 252, 36, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 243, 0, - 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, 255, - 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 16, 255, 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, - 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 243, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, - 0, 0, 0, 128, 255, 255, 255, 154, 1, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 184, 255, 255, 255, 240, 255, 255, 255, - 184, 255, 255, 255, 0, 0, 255, 255, 255, 143, 255, 255, 255, 143, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 158, 255, 255, 192, 0, 192, 255, 255, 162, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 113, - 255, 255, 202, 0, 0, 202, 255, 255, 110, 0, 0, 0, 0, 0, - 0, 0, 113, 255, 255, 201, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 113, 255, 255, 201, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 202, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, - 113, 255, 255, 201, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 128, 255, 255, 255, 154, 1, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 184, - 16, 184, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, - 255, 255, 143, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 34, 251, 255, 255, 128, 255, 255, 252, - 37, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 16, 113, 250, 255, 255, 111, 0, 0, 110, 255, 255, 249, 111, - 16, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 249, 111, 16, 0, - 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 128, - 255, 255, 255, 154, 1, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 255, 255, 184, 8, 0, 8, 184, 255, 255, 255, 255, 255, 0, 0, - 255, 255, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 157, 255, 255, - 255, 255, 255, 162, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, 7, 0, 0, 6, - 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, 5, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 201, 5, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 6, 203, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, 5, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 128, 255, 255, 255, 154, 1, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 255, 184, 8, 0, 0, 0, 8, 184, 255, 255, - 255, 255, 0, 0, 255, 255, 255, 255, 143, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 33, 251, 255, 255, 255, 252, 38, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, 25, - 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 201, 22, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 201, 22, 0, 0, 0, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, - 0, 0, 24, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, - 22, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 154, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 184, 8, 0, 0, 0, 0, - 0, 8, 184, 255, 255, 255, 0, 0, 255, 255, 255, 143, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 156, 255, 255, 255, 163, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 243, - 202, 111, 7, 0, 0, 0, 0, 0, 0, 6, 109, 201, 243, 255, - 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 0, 0, 0, 0, 6, 109, 201, 243, 255, 255, 255, - 243, 201, 108, 5, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 128, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 184, 8, 0, - 0, 0, 0, 0, 0, 0, 8, 184, 255, 255, 0, 0, 255, 255, - 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 7, 111, 203, 244, 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 21, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 7, - 112, 203, 244, 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 7, 112, 203, 244, 255, 255, 255, 244, 203, - 111, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 184, 8, 0, - 0, 0, 0, 0, 0, 0, 8, 184, 255, 255, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0, 0, 0, 0, 8, 114, 204, 244, 241, - 167, 37, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 25, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 203, 24, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 150, 222, 30, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 27, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, - 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 27, 208, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 205, 25, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 64, 255, 64, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 184, 8, 0, 0, 0, 0, 0, 8, 184, 255, 255, 255, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 29, 210, - 255, 255, 255, 255, 255, 245, 87, 0, 0, 255, 255, 255, 0, 0, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 24, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 203, 6, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 150, 255, 255, 221, 23, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 7, 208, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 203, 6, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 7, 208, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 205, 7, 0, 0, - 0, 0, 0, 0, 0, 0, 192, 255, 192, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 255, 184, 8, 0, 0, 0, 8, 184, - 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, - 0, 8, 210, 255, 255, 255, 255, 255, 255, 255, 254, 99, 0, 255, - 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 203, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 248, - 108, 15, 0, 0, 0, 15, 110, 249, 255, 255, 109, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 150, 255, - 255, 255, 166, 4, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 113, 255, 255, 248, 108, - 15, 0, 0, 0, 15, 110, 249, 255, 255, 109, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 113, 255, 255, 248, 108, 15, 0, 0, 0, 15, 110, 249, 255, - 255, 111, 0, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 64, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 184, 8, - 0, 8, 184, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 114, 255, 255, 248, 108, 15, 18, 104, 237, - 255, 254, 96, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 15, 110, 249, 255, 255, 109, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 203, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, - 201, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 7, 111, 203, 244, 255, 255, - 255, 249, 255, 255, 255, 161, 3, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 204, - 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 201, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 204, 255, 255, 108, 0, 0, 0, 0, 0, - 0, 0, 111, 255, 255, 202, 0, 0, 0, 0, 0, 0, 0, 192, - 255, 255, 255, 192, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 255, 255, 255, 184, 16, 184, 255, 255, 255, 255, 255, 255, 0, 0, - 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 94, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 255, 255, 108, 0, - 0, 0, 0, 36, 224, 255, 253, 255, 255, 255, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 201, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, - 0, 16, 255, 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 206, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 243, 27, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, - 16, 255, 255, 243, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 15, 0, - 0, 0, 0, 0, 0, 0, 16, 255, 255, 243, 0, 0, 0, 0, - 0, 0, 64, 255, 255, 255, 255, 255, 64, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 184, 255, 255, 255, 240, 255, 255, 255, 184, 255, - 255, 255, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, - 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 206, 255, 255, 253, - 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, - 255, 255, 15, 0, 0, 0, 0, 0, 32, 224, 255, 255, 255, 255, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, - 255, 255, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 7, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 212, - 7, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 16, 255, 255, 245, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 16, 255, 255, 243, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 192, 255, 255, 247, 255, 255, 192, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 8, 184, 255, 255, 255, 255, - 255, 184, 8, 255, 255, 255, 0, 0, 0, 143, 255, 255, 255, 143, - 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 17, 205, 255, 255, 253, 94, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 32, - 224, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 255, 255, 249, 117, 25, 155, 255, 255, 255, - 188, 247, 255, 255, 116, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 113, 255, 255, 195, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 113, 255, 255, 201, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 64, 255, 255, 251, 70, - 252, 255, 255, 64, 0, 0, 0, 0, 0, 255, 255, 255, 0, 8, - 184, 255, 255, 255, 184, 8, 0, 255, 255, 255, 0, 0, 0, 0, - 143, 255, 255, 255, 143, 1, 143, 255, 255, 255, 143, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, - 0, 0, 0, 0, 0, 17, 205, 255, 255, 253, 94, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 32, 255, 255, 255, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 203, 255, 255, 118, 0, 21, - 222, 255, 255, 159, 2, 105, 255, 255, 204, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 16, 113, 250, 255, 255, 83, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 113, 250, 255, - 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 192, - 255, 255, 157, 0, 160, 255, 255, 192, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 8, 255, 255, 255, 8, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 143, 255, 255, 255, 207, 255, 255, 255, 143, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 255, - 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 17, 204, 255, 255, - 253, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, 0, 244, 255, - 255, 25, 0, 0, 30, 221, 158, 2, 0, 16, 255, 255, 242, 0, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 148, 0, 0, 0, 0, 0, 7, 111, 203, 244, 255, 255, 255, - 255, 255, 255, 255, 201, 5, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 64, 255, 255, 251, 33, 0, 36, 252, 255, 255, 64, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, - 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, - 255, 255, 207, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, - 0, 17, 204, 255, 255, 253, 94, 0, 0, 0, 0, 0, 0, 0, - 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 16, 255, 255, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 24, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 20, 2, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 196, 1, 0, 0, 0, 0, 25, 206, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 201, 22, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 192, 255, 255, 156, 0, 0, 0, 160, - 255, 255, 192, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 1, 207, 255, 255, 255, 207, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 143, 255, 255, 255, 143, 1, 143, 255, 255, 255, 143, 0, 0, - 0, 0, 0, 0, 0, 0, 16, 204, 255, 255, 253, 94, 0, 0, - 0, 0, 0, 0, 0, 203, 255, 255, 110, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 113, 255, 255, 201, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 203, 6, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 149, 0, 0, 0, 7, - 206, 255, 255, 255, 255, 255, 255, 255, 243, 201, 108, 5, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 64, 255, 255, 250, 32, - 0, 0, 0, 36, 252, 255, 255, 64, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 143, 255, 255, 255, 255, 255, 143, 0, 0, 0, - 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 143, 255, - 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 16, 203, 255, - 255, 253, 94, 0, 0, 0, 0, 0, 0, 113, 255, 255, 249, 111, - 16, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 15, 110, 249, 255, 255, 109, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 15, 110, 249, 255, 255, - 83, 0, 0, 111, 255, 255, 248, 108, 15, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 192, - 255, 255, 154, 0, 0, 0, 0, 0, 160, 255, 255, 192, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 143, 255, 255, 255, 207, 255, 255, - 255, 143, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, - 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, - 0, 0, 16, 202, 255, 255, 253, 94, 0, 0, 0, 0, 0, 8, - 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 201, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 201, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 111, 255, 255, 196, 0, 0, 203, 255, 255, 108, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 64, 255, 255, 250, 31, 0, 0, 0, 0, 0, 36, 252, - 255, 255, 64, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 143, 255, 255, 255, - 143, 1, 143, 255, 255, 255, 143, 0, 0, 0, 0, 255, 255, 255, - 143, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 16, 202, 255, 255, 253, 94, 0, - 0, 0, 0, 0, 27, 208, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 201, 22, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, - 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 16, 255, 255, 245, 0, 0, 244, 255, 255, - 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 192, 255, 255, 152, 0, 0, 0, 0, - 0, 0, 0, 160, 255, 255, 192, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 143, - 255, 255, 255, 143, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 202, - 255, 255, 253, 94, 0, 0, 0, 0, 0, 7, 112, 202, 243, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, - 255, 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 16, 255, 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 243, 0, - 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, 255, - 255, 243, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 30, - 0, 0, 0, 0, 0, 0, 0, 36, 255, 255, 255, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 143, 255, - 255, 255, 143, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 15, 201, 255, 255, 253, 94, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 202, 255, 255, 110, 0, 0, 0, 0, - 0, 0, 0, 113, 255, 255, 201, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 113, 255, 255, 202, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 113, - 255, 255, 202, 0, 0, 202, 255, 255, 110, 0, 0, 0, 0, 0, - 0, 0, 113, 255, 255, 201, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 255, 255, 255, 143, 0, 0, 0, 0, - 0, 0, 0, 143, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 15, 201, 255, 255, 253, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 249, - 111, 16, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 16, 113, 250, 255, 255, 111, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 16, 113, 250, 255, 255, 111, 0, 0, 110, 255, 255, 249, 111, - 16, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 6, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, - 5, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 206, 7, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, 7, 0, 0, 6, - 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, 5, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 201, 22, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 206, 25, 0, 0, 0, 244, 255, 255, 24, - 0, 0, 0, 0, 0, 0, 0, 25, 255, 255, 243, 0, 0, 0, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, 25, - 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 201, 22, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 109, 201, 243, - 255, 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 243, 202, 111, 7, 0, 0, 0, 0, - 202, 255, 255, 118, 0, 0, 0, 0, 0, 0, 0, 121, 255, 255, - 201, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 243, - 202, 111, 7, 0, 0, 0, 0, 0, 0, 6, 109, 201, 243, 255, - 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 110, 255, 255, 250, 120, 25, 0, 0, 0, 25, - 121, 250, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 6, 203, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 201, 5, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, - 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, 22, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 6, 109, 201, 243, 255, 255, 255, 243, 201, 108, - 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 111, 203, 244, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 7, - 111, 203, 244, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, - 0, 0, 7, 111, 203, 244, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 244, 203, - 111, 7, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 0, 0, 255, 255, 255, 244, 203, 111, 7, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 143, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 7, 111, 203, - 244, 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, - 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, - 0, 0, 25, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 0, 0, 0, 25, 206, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 205, 25, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, - 255, 255, 205, 25, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 143, 255, 255, 255, 0, 0, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 25, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 24, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 0, 0, 7, 206, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 205, 7, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, - 255, 255, 255, 255, 255, 255, 255, 205, 7, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 203, 6, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 111, 255, 255, 248, 108, 15, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 248, 108, - 15, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 111, 255, - 255, 248, 108, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 112, 250, 255, - 255, 111, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 24, 118, 250, 255, 255, 111, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, - 143, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, - 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 111, 255, 255, 248, 108, 15, 0, - 0, 0, 15, 110, 249, 255, 255, 109, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, 255, 255, 108, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, - 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 203, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 113, 255, 255, 202, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 255, 255, 202, 0, 0, 255, 255, 255, 143, 0, 0, 0, 0, 143, - 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 203, 255, 255, - 108, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 201, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 17, 255, 255, 244, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 24, 255, 255, 244, 0, 0, 255, 255, 255, 255, 143, - 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, 255, - 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 7, 111, 203, 244, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, - 255, 255, 255, 255, 143, 143, 255, 255, 255, 143, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, 0, 0, 0, 7, - 111, 203, 244, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 25, 206, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 255, 255, 255, 0, 0, 255, 255, 255, 32, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 24, 0, 0, - 0, 0, 25, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 0, 0, 255, 255, 255, 32, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 7, 206, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 143, 255, 255, 255, - 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 224, - 32, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 203, 6, 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 224, 32, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, - 255, 255, 243, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 244, 255, 255, 15, 0, 0, - 0, 0, 0, 0, 0, 16, 255, 255, 243, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, - 248, 108, 15, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, - 243, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, - 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 255, 255, 255, 0, 0, 16, 255, 255, 243, 0, 0, - 255, 255, 255, 255, 224, 32, 0, 0, 0, 0, 0, 16, 255, 255, - 243, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, - 16, 255, 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 15, 110, 249, 255, 255, 109, 0, 0, 111, 255, 255, 248, 108, - 15, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, - 255, 255, 224, 32, 0, 0, 0, 0, 0, 16, 255, 255, 243, 0, - 0, 0, 0, 0, 0, 0, 202, 255, 255, 110, 0, 0, 0, 0, - 0, 0, 0, 113, 255, 255, 201, 0, 0, 202, 255, 255, 110, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 202, 255, - 255, 110, 0, 0, 0, 0, 0, 0, 0, 113, 255, 255, 201, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 203, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 113, 255, 255, 201, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 255, 255, 255, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 113, 255, - 255, 201, 0, 0, 255, 255, 255, 254, 255, 224, 36, 0, 0, 0, - 0, 113, 255, 255, 202, 0, 0, 202, 255, 255, 110, 0, 0, 0, - 0, 0, 0, 0, 113, 255, 255, 201, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 201, 0, 0, 203, - 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 255, 255, 255, 254, 255, 224, 36, 0, 0, 0, 0, 113, - 255, 255, 202, 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 249, - 111, 16, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, 110, - 255, 255, 249, 111, 16, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 110, 255, 255, 249, 111, 16, 0, 0, 0, 16, 113, 250, - 255, 255, 108, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 143, 255, 255, - 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, - 16, 113, 250, 255, 255, 108, 0, 0, 255, 255, 255, 99, 254, 255, - 237, 104, 18, 16, 113, 250, 255, 255, 111, 0, 0, 110, 255, 255, - 249, 111, 16, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, - 243, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 255, 255, 255, 99, 254, 255, 237, 104, - 18, 16, 113, 250, 255, 255, 111, 0, 0, 0, 0, 0, 0, 0, - 6, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, - 5, 0, 0, 6, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 0, 0, 6, 203, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 201, 5, 0, 0, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, 5, 0, - 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 201, 5, 0, 0, 255, 255, - 255, 0, 95, 253, 255, 255, 255, 255, 255, 255, 255, 206, 7, 0, - 0, 6, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 201, 5, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, - 95, 253, 255, 255, 255, 255, 255, 255, 255, 206, 7, 0, 0, 0, - 0, 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 201, 22, 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 24, 203, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 201, 22, 0, 0, 0, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 201, 22, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, 22, 0, - 0, 0, 255, 255, 255, 0, 0, 82, 244, 255, 255, 255, 255, 255, - 206, 25, 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 201, 22, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 255, 255, 255, 0, 0, 82, 244, 255, 255, 255, 255, 255, 206, 25, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 109, 201, 243, - 255, 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 0, 0, 6, - 109, 201, 243, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, - 0, 0, 6, 109, 201, 243, 255, 255, 255, 243, 201, 108, 5, 0, - 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 243, 201, 108, 5, 0, 0, 0, 0, 255, 255, 255, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 143, 255, - 255, 255, 143, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 243, 201, - 108, 5, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 35, 165, - 241, 243, 202, 111, 7, 0, 0, 0, 0, 0, 0, 6, 109, 201, - 243, 255, 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 35, 165, 241, 243, - 202, 111, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 15, - 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 15, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, - 255, 255, 243, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 202, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, 202, 255, 255, - 110, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 113, 255, 255, 201, 0, 0, 202, 255, 255, 110, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 110, 255, 255, 249, 111, 16, 0, 0, 0, 0, - 0, 110, 255, 255, 249, 111, 16, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, 110, - 255, 255, 249, 111, 16, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 6, 203, 255, 255, 255, 255, - 255, 255, 255, 0, 0, 6, 203, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 135, 244, 135, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, - 5, 0, 0, 6, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, - 203, 255, 255, 255, 255, 255, 255, 0, 0, 0, 24, 203, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 244, 255, 243, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 201, 22, 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 6, 109, 201, 243, 255, 255, 255, 0, 0, 0, - 0, 6, 109, 201, 243, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 135, 243, 135, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 0, 0, 6, - 109, 201, 243, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 244, 135, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 244, 255, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 135, 243, 135, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, - 255, 255, 255, 255, 255, 255, 252, 235, 191, 114, 15, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 7, 111, 203, 244, 255, 255, 255, - 0, 0, 0, 0, 8, 114, 204, 244, 241, 167, 37, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, - 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 207, 247, 163, - 6, 0, 6, 164, 247, 206, 72, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 255, 255, 255, 255, 255, 255, 244, 203, 111, 7, 0, - 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 0, 0, 0, 0, 7, 111, 203, 244, 255, 255, - 255, 244, 202, 110, 6, 0, 0, 0, 0, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, - 0, 0, 0, 8, 114, 204, 244, 255, 255, 255, 244, 203, 113, 8, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0, 0, 0, 0, 7, 112, 203, 244, 255, - 255, 255, 244, 202, 110, 6, 0, 0, 0, 0, 0, 0, 7, 111, - 203, 244, 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, 0, 0, - 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 236, 80, 0, 0, 0, 0, 0, 0, 0, 0, 25, 206, 255, 255, - 255, 255, 255, 255, 0, 0, 0, 29, 210, 255, 255, 255, 255, 255, - 245, 87, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 192, 255, 255, 255, 192, 0, 0, 0, 0, 0, 0, 0, 0, 80, - 254, 255, 255, 255, 140, 0, 141, 255, 255, 255, 253, 77, 0, 0, - 0, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 143, 255, - 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 205, 25, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 25, 206, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 203, 24, 0, 0, 0, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 0, 0, 0, 29, 210, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 208, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 27, 208, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 24, 0, 0, 0, - 0, 25, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 24, - 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 253, 73, 0, 0, 0, 0, 0, 0, 7, - 206, 255, 255, 255, 255, 255, 255, 255, 0, 0, 8, 210, 255, 255, - 255, 255, 255, 255, 255, 254, 99, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 64, 255, 255, 255, 255, 255, 64, 0, 0, 0, 0, - 0, 0, 15, 236, 255, 255, 254, 255, 250, 56, 250, 255, 254, 255, - 255, 233, 13, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, - 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 205, 7, 0, 0, 143, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, - 7, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, - 6, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 0, 0, 143, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 0, 0, 8, 210, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 208, 7, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, - 0, 7, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 203, 6, 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 203, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 4, 25, 83, 195, 255, 255, 206, 0, 0, 0, - 0, 0, 0, 111, 255, 255, 248, 108, 15, 0, 0, 0, 0, 0, - 114, 255, 255, 248, 108, 15, 18, 104, 237, 255, 254, 96, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 192, 255, 255, 248, 255, 255, 192, - 0, 0, 0, 0, 0, 0, 111, 255, 255, 192, 12, 192, 255, 255, - 255, 191, 12, 191, 255, 255, 109, 0, 0, 0, 143, 255, 255, 255, - 143, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 16, 112, 250, 255, 255, 111, 0, - 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 255, 255, 250, 111, 16, 0, 0, 0, 15, - 110, 249, 255, 255, 109, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255, - 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, - 255, 255, 248, 108, 15, 0, 0, 0, 15, 110, 249, 255, 255, 112, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 113, 255, 255, 248, 108, 15, 0, 0, 0, - 15, 110, 249, 255, 255, 109, 0, 0, 111, 255, 255, 248, 108, 15, - 0, 0, 0, 15, 110, 249, 255, 255, 109, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 255, 255, - 247, 0, 0, 0, 0, 0, 0, 203, 255, 255, 108, 0, 0, 0, - 0, 0, 0, 0, 204, 255, 255, 108, 0, 0, 0, 0, 36, 224, - 255, 253, 255, 255, 255, 0, 0, 0, 0, 0, 64, 255, 255, 252, - 72, 252, 255, 255, 64, 0, 0, 0, 0, 0, 189, 255, 255, 82, - 0, 83, 255, 255, 255, 81, 0, 82, 255, 255, 187, 0, 0, 0, - 0, 143, 255, 255, 255, 143, 1, 143, 255, 255, 255, 143, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, - 255, 255, 202, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 203, 255, 255, 193, 2, 0, - 0, 0, 0, 0, 0, 111, 255, 255, 201, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 145, 255, 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 204, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, - 111, 255, 255, 202, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 204, 255, 255, 108, 0, - 0, 0, 0, 0, 0, 0, 111, 255, 255, 201, 0, 0, 203, 255, - 255, 108, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 201, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 25, - 83, 195, 255, 255, 206, 0, 0, 0, 0, 0, 0, 244, 255, 255, - 15, 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 15, 0, 0, - 0, 0, 0, 32, 224, 255, 255, 255, 255, 0, 0, 0, 0, 0, - 192, 255, 255, 160, 0, 160, 255, 255, 192, 0, 0, 0, 0, 0, - 233, 255, 255, 26, 0, 26, 255, 255, 255, 25, 0, 26, 255, 255, - 232, 0, 0, 0, 0, 0, 143, 255, 255, 255, 207, 255, 255, 255, - 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 17, 255, 255, 244, 0, 0, 0, 0, 0, 143, 255, - 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, - 255, 255, 160, 2, 0, 0, 0, 0, 0, 16, 255, 255, 243, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 145, 255, 255, 255, 145, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 15, 0, 0, 0, - 0, 0, 0, 0, 16, 255, 255, 243, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 244, - 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 243, - 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, - 255, 255, 243, 0, 0, 0, 0, 0, 0, 0, 0, 21, 122, 195, - 237, 253, 255, 255, 255, 255, 255, 254, 74, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 32, 224, 255, 255, 255, 0, - 0, 0, 0, 64, 255, 255, 252, 36, 0, 36, 252, 255, 255, 64, - 0, 0, 0, 0, 251, 255, 255, 5, 0, 5, 255, 255, 255, 5, - 0, 5, 255, 255, 251, 0, 0, 0, 0, 0, 0, 143, 255, 255, - 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 7, 111, - 203, 244, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, 255, 160, 2, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 255, - 255, 255, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 245, 0, 0, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 90, 241, 255, 255, 255, 255, 255, 255, 255, 255, 236, 81, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 32, - 255, 255, 255, 0, 0, 0, 0, 192, 255, 255, 160, 0, 0, 0, - 160, 255, 255, 192, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 1, 207, 255, 255, 255, 207, 1, 0, 0, 0, 0, 0, 0, - 0, 25, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 159, 2, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 147, 255, 255, 255, 148, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 255, - 255, 195, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 78, 255, 255, 255, 255, 255, 255, 253, 236, 193, 116, - 17, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 64, 255, 255, 252, - 36, 0, 0, 0, 36, 252, 255, 255, 64, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 143, 255, 255, 255, 255, 255, 143, 0, 0, - 0, 0, 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 143, - 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 255, 255, 255, 143, - 255, 255, 255, 159, 2, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 255, 149, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 16, 113, 250, 255, 255, 83, 0, 0, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 208, 255, 255, 193, 80, 24, 4, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 192, 255, 255, 160, 0, 0, 0, 0, 0, 160, 255, 255, 192, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 143, 255, 255, 255, 207, 255, - 255, 255, 143, 0, 0, 0, 0, 0, 111, 255, 255, 248, 108, 15, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 144, 255, 255, 255, 158, 2, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, - 255, 255, 255, 150, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 255, 255, 255, 255, 148, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, - 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 16, 255, 255, 243, 0, 0, 0, 0, 0, 0, 247, 255, 255, - 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 64, 255, 255, 252, 36, 0, 0, 0, 0, 0, 36, - 252, 255, 255, 64, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 143, 255, 255, - 255, 143, 1, 143, 255, 255, 255, 143, 0, 0, 0, 0, 203, 255, - 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 144, 255, 255, 255, 158, - 2, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 149, 255, 255, 255, 152, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 196, 1, 0, - 0, 0, 255, 255, 255, 143, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 113, 255, 255, 201, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 113, 255, 255, 201, 0, 0, 0, 0, 0, - 0, 207, 255, 255, 194, 82, 26, 5, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 192, 255, 255, 160, 0, 0, 0, - 0, 0, 0, 0, 160, 255, 255, 192, 0, 0, 255, 255, 255, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 143, 255, 255, 255, 143, 0, 0, 0, 143, 255, 255, 255, 143, 0, - 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 143, 255, 255, 255, 143, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 145, 255, 255, 255, 158, 2, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 149, 255, 255, 255, 153, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, - 255, 255, 149, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, - 0, 0, 0, 0, 0, 74, 254, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 128, 0, 0, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, - 36, 0, 0, 0, 0, 0, 0, 0, 36, 255, 255, 255, 0, 0, - 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, - 255, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 143, - 255, 255, 255, 143, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 145, 255, 255, 255, 158, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 150, 255, 255, 255, 113, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 15, 110, 249, 255, 255, 83, 0, 0, 0, 143, 255, 255, - 255, 143, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, 5, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 201, 5, 0, 0, 0, 0, 0, 0, 0, 81, 236, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 128, 0, 0, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 143, 0, 0, 0, - 0, 0, 0, 0, 143, 255, 255, 255, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 146, 255, 255, 255, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 151, 255, 255, 205, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 196, 0, 0, - 0, 0, 143, 255, 255, 255, 143, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 201, 22, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 201, 22, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 16, 114, 190, 234, 251, 255, 255, 255, 255, 255, 255, 255, 128, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 146, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 255, 255, 247, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, - 255, 245, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 15, - 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, 243, 0, 0, 0, - 128, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, - 255, 255, 243, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, - 0, 0, 16, 255, 255, 243, 0, 0, 0, 0, 0, 0, 143, 255, - 255, 255, 143, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 202, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, 180, 255, 255, - 201, 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 202, 255, 255, 110, 0, 0, 0, 0, - 0, 0, 0, 113, 255, 255, 202, 0, 0, 203, 255, 255, 110, 0, - 0, 0, 0, 0, 0, 0, 113, 255, 255, 202, 0, 0, 0, 0, - 0, 0, 0, 143, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 202, 255, 255, 110, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 110, 255, 255, 249, 111, 16, 0, 0, 0, 15, - 104, 247, 255, 255, 108, 0, 0, 0, 128, 255, 255, 255, 255, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 249, - 111, 16, 0, 0, 0, 16, 113, 250, 255, 255, 110, 0, 0, 113, - 255, 255, 249, 111, 16, 0, 0, 0, 16, 113, 250, 255, 255, 111, - 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 255, 255, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 249, 111, 16, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 6, 203, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 201, 5, 0, 0, 0, 0, 0, - 15, 110, 249, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 6, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 204, - 6, 0, 0, 8, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 206, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, - 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 6, 203, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, - 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, 22, 0, 0, - 0, 0, 0, 0, 0, 0, 111, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 203, 24, 0, 0, 0, 0, 27, 208, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 206, 25, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 143, 255, 255, 255, 0, 0, 0, 0, 0, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 0, 0, 0, 24, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 6, 109, 201, 243, 255, 255, 255, 243, 201, 108, - 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 109, 201, 243, - 255, 255, 255, 243, 201, 109, 6, 0, 0, 0, 0, 0, 0, 7, - 112, 202, 243, 255, 255, 255, 243, 202, 111, 7, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 0, 0, - 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 0, 0, 0, 0, 6, 109, 201, 243, 255, 255, - 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 114, 204, - 244, 255, 255, 255, 244, 203, 113, 8, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, - 0, 135, 244, 135, 0, 0, 0, 0, 7, 111, 203, 244, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 91, 212, 248, 212, 90, - 0, 0, 0, 0, 0, 9, 43, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 44, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 76, 0, 0, 0, 0, 0, - 0, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 99, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 29, 210, 255, 255, 255, 255, 255, 255, 255, 255, 255, 208, 27, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 203, 24, 0, 0, 0, 244, 255, 243, 0, 0, 0, 25, 206, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 115, 255, - 255, 255, 255, 255, 111, 0, 0, 0, 8, 186, 239, 52, 0, 0, - 0, 0, 0, 0, 0, 0, 49, 238, 197, 12, 0, 0, 0, 0, - 7, 112, 203, 244, 248, 206, 113, 1, 0, 0, 141, 253, 97, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 255, 137, - 0, 0, 0, 0, 138, 255, 139, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 26, 247, 253, 151, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 8, 210, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 208, 7, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 203, 6, 0, 0, 135, 243, 135, 0, 0, - 7, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 41, 251, 255, 255, 255, 255, 255, 249, 39, 0, 0, 167, 255, - 255, 239, 52, 0, 0, 0, 0, 0, 0, 49, 237, 255, 255, 174, - 0, 0, 0, 27, 208, 255, 255, 255, 255, 255, 255, 149, 1, 143, - 255, 255, 254, 58, 0, 0, 0, 54, 34, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 18, 44, 0, 0, 0, 0, 0, 0, 0, 0, - 141, 255, 255, 255, 79, 0, 0, 80, 255, 255, 255, 140, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 146, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 114, 255, 255, 248, 108, 15, 0, - 0, 0, 15, 107, 248, 255, 255, 112, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 15, 110, 249, 255, 255, 109, 0, 0, 0, - 0, 0, 0, 0, 111, 255, 255, 248, 108, 15, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 8, 114, - 204, 244, 255, 255, 255, 244, 203, 112, 7, 0, 0, 0, 0, 255, - 255, 255, 2, 0, 0, 150, 255, 255, 178, 15, 178, 255, 255, 148, - 0, 0, 50, 238, 255, 255, 239, 52, 0, 0, 0, 0, 49, 237, - 255, 255, 236, 47, 0, 0, 7, 208, 255, 255, 255, 255, 255, 255, - 255, 255, 207, 255, 255, 255, 147, 0, 0, 0, 5, 217, 248, 135, - 12, 0, 255, 255, 255, 0, 7, 117, 239, 213, 3, 0, 0, 0, - 0, 0, 0, 141, 255, 255, 255, 149, 0, 0, 0, 1, 149, 255, - 255, 255, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 249, 255, 247, - 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 204, 255, 255, - 108, 0, 0, 0, 0, 0, 0, 0, 109, 255, 255, 202, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, - 201, 0, 0, 0, 0, 0, 0, 0, 203, 255, 255, 108, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, - 0, 29, 210, 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, 25, - 0, 0, 0, 255, 255, 255, 158, 2, 0, 217, 255, 255, 58, 0, - 59, 255, 255, 215, 0, 0, 0, 49, 237, 255, 255, 239, 52, 0, - 0, 48, 237, 255, 255, 237, 48, 0, 0, 0, 113, 255, 255, 248, - 108, 15, 14, 150, 255, 255, 255, 255, 255, 149, 0, 0, 0, 0, - 116, 255, 255, 255, 230, 94, 255, 255, 255, 91, 224, 255, 255, 255, - 105, 0, 0, 0, 0, 0, 142, 255, 255, 255, 147, 0, 0, 0, - 0, 0, 0, 148, 255, 255, 255, 141, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 154, 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, 255, - 255, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 16, 255, 255, 243, 0, 0, 0, 0, 0, 0, 0, 244, 255, - 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 0, 0, 8, 210, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 205, 6, 0, 0, 158, 255, 255, 255, 158, 2, 247, - 255, 255, 12, 0, 12, 255, 255, 246, 0, 0, 0, 0, 49, 237, - 255, 255, 239, 52, 48, 236, 255, 255, 237, 48, 0, 0, 0, 0, - 204, 255, 255, 108, 0, 0, 0, 1, 208, 255, 255, 255, 213, 2, - 0, 0, 0, 0, 51, 197, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 220, 79, 0, 0, 0, 0, 142, 255, 255, 255, 146, - 0, 0, 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, 141, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 34, 251, 255, 247, 25, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 246, 255, 255, 15, 0, 0, 0, 0, 0, - 0, 0, 16, 255, 255, 245, 0, 0, 0, 0, 7, 111, 203, 244, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, - 0, 0, 255, 255, 255, 0, 0, 0, 5, 143, 239, 255, 255, 255, - 240, 147, 7, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 114, 255, 255, 249, 117, 24, - 255, 255, 255, 24, 118, 250, 255, 255, 110, 0, 0, 2, 158, 255, - 255, 255, 158, 250, 255, 255, 12, 0, 12, 255, 255, 247, 0, 0, - 0, 0, 0, 49, 237, 255, 255, 239, 236, 255, 255, 237, 49, 0, - 0, 0, 0, 0, 244, 255, 255, 15, 0, 0, 0, 146, 255, 255, - 255, 255, 255, 148, 0, 0, 0, 0, 0, 0, 89, 226, 255, 255, - 255, 255, 255, 255, 255, 233, 107, 5, 0, 0, 0, 0, 143, 255, - 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, - 255, 255, 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 160, 255, 255, 143, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 143, 0, 0, 0, 0, 0, 0, 0, 196, 255, 255, 110, 0, - 0, 0, 0, 0, 0, 0, 113, 255, 255, 195, 0, 0, 0, 25, - 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, - 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 140, 255, - 255, 255, 255, 255, 255, 255, 147, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 204, 255, - 255, 117, 0, 0, 255, 255, 255, 0, 0, 120, 255, 255, 201, 0, - 0, 0, 2, 158, 255, 255, 255, 255, 255, 255, 59, 0, 59, 255, - 255, 215, 0, 0, 0, 0, 0, 0, 48, 237, 255, 255, 255, 255, - 238, 50, 0, 0, 0, 0, 0, 0, 248, 255, 255, 14, 0, 0, - 148, 255, 255, 255, 207, 255, 255, 255, 146, 0, 0, 0, 0, 0, - 0, 22, 220, 255, 255, 255, 255, 255, 220, 22, 0, 0, 0, 0, - 0, 143, 255, 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 145, 255, 255, 255, 143, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 252, 255, 246, - 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 84, - 255, 255, 249, 111, 16, 0, 0, 0, 16, 113, 250, 255, 255, 83, - 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, - 0, 0, 238, 255, 255, 255, 255, 255, 255, 255, 239, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 244, 255, 255, 24, 0, 0, 255, 255, 255, 0, 0, 25, - 255, 255, 243, 0, 0, 0, 0, 2, 158, 255, 255, 255, 255, 255, - 180, 16, 180, 255, 255, 146, 0, 0, 0, 0, 0, 0, 0, 48, - 236, 255, 255, 238, 50, 0, 0, 0, 0, 0, 0, 0, 206, 255, - 255, 151, 2, 149, 255, 255, 255, 144, 1, 143, 255, 255, 255, 102, - 0, 0, 0, 5, 109, 233, 255, 255, 255, 255, 255, 255, 255, 225, - 87, 0, 0, 0, 0, 255, 255, 255, 143, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 166, 255, 255, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, - 0, 0, 0, 0, 149, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 148, 0, 0, 0, 111, 255, 255, 248, 108, 15, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, - 255, 255, 255, 0, 0, 0, 255, 255, 255, 27, 0, 27, 255, 255, - 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 25, 255, 255, 243, 0, 0, 0, 0, 85, 206, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 250, 37, 0, 0, 0, 0, - 0, 0, 0, 0, 48, 236, 238, 51, 0, 0, 0, 0, 0, 0, - 0, 0, 114, 255, 255, 255, 213, 255, 255, 255, 146, 0, 0, 0, - 143, 255, 184, 8, 0, 0, 79, 221, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 194, 48, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 43, 253, 255, 245, 22, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, - 255, 255, 143, 0, 0, 0, 0, 0, 1, 196, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 196, 1, 0, 0, 0, 203, 255, 255, 108, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, - 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 121, 255, 255, 201, 0, 0, 0, - 117, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 110, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 52, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 149, 255, 255, 255, 255, 255, 148, - 0, 0, 0, 0, 0, 101, 8, 0, 0, 0, 106, 255, 255, 255, - 219, 83, 255, 255, 255, 101, 233, 255, 255, 255, 107, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 140, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 150, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 149, 0, 0, 0, - 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, - 255, 255, 255, 27, 0, 27, 255, 255, 255, 0, 0, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 25, 121, 250, 255, 255, - 108, 0, 0, 43, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 214, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 214, 255, - 255, 255, 213, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 3, 213, 237, 112, 5, 0, 255, 255, 255, 0, 14, 140, 250, 210, - 3, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 254, 255, 245, - 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, - 0, 84, 255, 255, 248, 108, 15, 0, 0, 0, 15, 110, 249, 255, - 255, 83, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 255, 255, - 255, 0, 0, 0, 240, 255, 255, 255, 255, 255, 255, 255, 255, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 8, 114, 204, 244, 255, 255, 255, 255, - 255, 255, 255, 201, 5, 0, 0, 154, 255, 255, 178, 15, 178, 255, - 255, 255, 255, 255, 158, 2, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 156, 255, 255, 255, 255, 255, 149, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 44, 17, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 37, 50, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 177, 255, 255, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, - 255, 255, 255, 0, 0, 197, 255, 255, 108, 0, 0, 0, 0, 0, - 0, 0, 111, 255, 255, 196, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, - 0, 0, 255, 255, 255, 0, 0, 0, 145, 255, 255, 255, 255, 255, - 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 29, 210, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 201, 22, 0, 0, 0, 219, 255, 255, - 58, 0, 59, 255, 255, 255, 255, 255, 255, 158, 2, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 114, 255, 255, 255, 207, 255, 255, 255, 149, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 53, 255, 255, 244, 21, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 246, 255, 255, 15, 0, - 0, 0, 0, 0, 0, 0, 16, 255, 255, 245, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 6, 144, - 239, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 8, 210, - 255, 255, 255, 255, 255, 255, 255, 243, 201, 108, 5, 0, 0, 0, - 0, 249, 255, 255, 12, 0, 12, 255, 255, 253, 158, 255, 255, 255, - 158, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 206, 255, 255, 151, 1, 137, - 255, 255, 255, 149, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 183, 255, 255, 136, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 244, - 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 243, - 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, - 255, 255, 243, 0, 0, 255, 255, 255, 0, 0, 244, 255, 255, 15, - 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 243, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 114, 255, 255, 249, 117, 24, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 247, 255, 255, 12, 0, 12, 255, 255, 248, - 2, 158, 255, 255, 255, 158, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, - 255, 14, 0, 0, 138, 255, 255, 255, 148, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 255, 255, 243, - 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 203, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, - 113, 255, 255, 202, 0, 0, 202, 255, 255, 110, 0, 0, 0, 0, - 0, 0, 0, 113, 255, 255, 201, 0, 0, 255, 255, 255, 0, 0, - 202, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, 113, 255, 255, - 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 204, 255, 255, 117, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 217, 255, 255, 59, 0, - 59, 255, 255, 214, 0, 2, 158, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 244, 255, 255, 15, 0, 0, 0, 139, 255, 255, 255, 141, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 189, 255, 255, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 113, 255, 255, 248, 108, 16, 0, - 0, 0, 16, 113, 250, 255, 255, 111, 0, 0, 110, 255, 255, 249, - 111, 16, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, 255, - 255, 255, 0, 0, 110, 255, 255, 249, 111, 16, 0, 0, 0, 16, - 113, 250, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 24, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 149, - 255, 255, 180, 16, 180, 255, 255, 143, 0, 0, 2, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 203, 255, 255, 110, 0, 0, 0, 0, - 139, 255, 255, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 64, 255, 255, 242, 19, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 8, 208, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, 7, 0, 0, - 6, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, - 5, 0, 0, 255, 255, 255, 0, 0, 6, 203, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 201, 5, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 244, 255, 255, 24, 0, 0, 255, 255, 255, 0, 0, 25, 255, 255, - 243, 0, 0, 40, 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 255, 255, 249, - 111, 16, 0, 0, 27, 255, 255, 238, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 195, 255, 255, 133, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, - 0, 0, 27, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, - 25, 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 201, 22, 0, 0, 0, 255, 255, 255, 0, 0, 0, 24, - 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, 22, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 203, 255, 255, 118, 0, 0, 255, 255, 255, 0, - 0, 121, 255, 255, 201, 0, 0, 0, 112, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 8, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 163, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 255, 255, 241, - 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 0, 0, 0, 0, 7, 112, 202, 243, 255, 255, 255, - 243, 202, 111, 7, 0, 0, 0, 0, 0, 0, 6, 109, 201, 243, - 255, 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 6, 109, 201, 243, 255, 255, 255, 243, 201, 108, - 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 113, 255, 255, 250, 120, 25, - 255, 255, 255, 25, 121, 250, 255, 255, 109, 0, 0, 0, 0, 90, - 210, 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 27, 208, 255, 255, 255, 255, 255, 255, 255, - 228, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 143, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 200, 255, 255, 131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 208, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 6, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 112, 202, 243, - 255, 255, 237, 163, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 75, 255, 255, 240, 17, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 27, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 204, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 205, 255, 255, 129, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 7, 112, 202, 243, 255, 255, - 255, 243, 202, 110, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, - 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, - 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 255, 255, 240, - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 143, - 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 210, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, - 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 87, 255, 255, 239, 15, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, - 255, 255, 80, 0, 0, 80, 255, 255, 255, 143, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3, 215, 255, 255, 126, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 143, 255, 143, 0, 0, 0, 0, 143, 255, 143, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 255, 255, 238, - 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, - 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 186, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 5, 103, 209, 14, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 50, 167, 8, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 11, 140, 226, 250, 223, 132, 9, 0, 0, 0, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 20, 131, 210, 246, - 0, 0, 246, 210, 130, 19, 0, 0, 0, 0, 0, 0, 0, 0, - 21, 111, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 7, - 147, 240, 240, 145, 6, 0, 0, 255, 255, 255, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 111, 21, 0, 0, 0, 0, 0, 76, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 123, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 135, 244, 135, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 21, 111, 0, 0, 0, 0, 0, 0, 6, 143, - 238, 243, 157, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 50, 238, 255, 184, 8, 0, 0, 253, 255, - 255, 6, 0, 0, 22, 205, 255, 255, 255, 255, 255, 198, 9, 0, - 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, - 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 59, - 233, 255, 255, 255, 0, 0, 255, 255, 255, 231, 57, 0, 0, 0, - 0, 0, 0, 21, 210, 255, 136, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 147, 255, 255, 255, 255, 144, 0, 0, 255, 255, 255, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 138, 255, 212, 23, 0, 0, 0, 142, 255, 140, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 145, 250, - 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 243, 0, - 0, 0, 0, 0, 0, 0, 0, 21, 210, 255, 136, 0, 0, 0, - 0, 0, 145, 255, 255, 255, 255, 153, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 50, 238, 255, 255, 220, 25, - 0, 0, 223, 255, 255, 120, 12, 80, 227, 255, 255, 255, 255, 255, - 255, 255, 131, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 34, 242, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, - 240, 33, 0, 0, 0, 0, 0, 121, 255, 255, 255, 137, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 240, 255, 255, 255, 255, 239, 0, - 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 140, 255, 255, 255, 119, 0, 0, - 80, 255, 255, 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 146, 255, 255, 202, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 135, 243, 135, 0, 0, 0, 0, 0, 0, 0, 0, 121, 255, 255, - 255, 137, 0, 0, 0, 0, 239, 255, 255, 255, 255, 240, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 238, 255, - 255, 220, 28, 0, 0, 0, 132, 255, 255, 255, 255, 255, 255, 255, - 230, 82, 12, 120, 255, 255, 221, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 163, 255, 255, 249, 117, 24, 0, 0, - 24, 118, 250, 255, 255, 162, 0, 0, 0, 0, 0, 1, 150, 255, - 255, 255, 138, 0, 0, 0, 255, 255, 255, 0, 0, 240, 255, 255, - 255, 255, 239, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 142, 255, 255, 255, - 144, 0, 0, 0, 0, 145, 255, 255, 255, 144, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 25, 246, 255, 255, 78, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 150, 255, 255, 255, 138, 0, 0, 0, 239, 255, 255, 255, - 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 49, 238, 255, 255, 221, 29, 0, 0, 0, 0, 9, 198, 255, 255, - 255, 255, 255, 210, 25, 0, 0, 6, 255, 255, 252, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 234, 255, 255, 116, - 0, 0, 0, 0, 0, 0, 119, 255, 255, 233, 0, 0, 0, 0, - 0, 0, 1, 150, 255, 255, 255, 139, 0, 0, 255, 255, 255, 0, - 0, 145, 255, 255, 255, 255, 142, 0, 0, 255, 255, 255, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, - 255, 255, 255, 146, 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, - 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 255, 255, - 207, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 150, 255, 255, 255, 139, 0, 0, - 143, 255, 255, 255, 255, 151, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 32, 224, 255, 222, 30, 0, 0, 0, 0, 0, - 0, 9, 131, 221, 250, 226, 144, 13, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 255, 255, 255, 23, 0, 0, 0, 0, 0, 0, 24, 255, 255, 254, - 0, 0, 0, 0, 0, 0, 0, 1, 150, 255, 255, 255, 0, 0, - 255, 255, 255, 0, 0, 6, 144, 239, 239, 142, 5, 0, 0, 255, - 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 147, 255, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, - 0, 149, 255, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 21, 244, 255, 255, 83, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 150, 255, - 255, 255, 0, 0, 5, 140, 236, 242, 155, 9, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 191, 31, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, - 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 1, 149, 255, 255, 255, 149, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 150, 255, 255, 255, 149, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 136, 255, 255, 211, 2, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 7, 147, 240, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 1, 150, 255, 255, 255, 151, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 151, 255, 255, - 255, 150, 1, 0, 0, 0, 0, 0, 0, 0, 19, 242, 255, 255, - 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 145, 0, 0, 0, 0, - 0, 0, 0, 0, 7, 147, 240, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 147, 255, 255, 255, 255, 255, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 1, 152, 255, 255, - 255, 152, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 153, 255, 255, 255, 152, 1, 0, 0, 0, 0, 0, 0, - 0, 131, 255, 255, 216, 3, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255, 255, - 147, 0, 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 7, 147, 240, 240, 145, 6, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 154, 255, 255, 255, 154, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 155, 255, 255, 255, 154, 1, 0, - 0, 0, 0, 0, 0, 16, 239, 255, 255, 93, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 145, 255, 255, 255, 149, 1, 0, 0, 0, 0, 0, 240, 255, - 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 240, 255, - 255, 255, 255, 239, 0, 0, 0, 0, 0, 0, 0, 147, 255, 255, - 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 156, 255, 255, 255, 156, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 157, 255, - 255, 255, 156, 1, 0, 0, 0, 0, 0, 0, 126, 255, 255, 219, - 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, 150, 1, 0, 0, - 0, 0, 240, 255, 255, 255, 255, 239, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 145, 255, 255, 255, 255, 142, 0, 0, 0, 0, 0, 0, - 0, 240, 255, 255, 255, 255, 239, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 95, 255, 255, 255, 219, 3, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 4, 220, 255, 255, 255, 95, 0, 0, 0, 0, 0, 0, - 14, 237, 255, 255, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, - 255, 152, 1, 0, 0, 0, 145, 255, 255, 255, 255, 142, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 25, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 24, 0, 0, 0, 0, 0, 6, 144, 239, 239, 142, 5, 0, 0, - 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 239, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 156, 255, 255, - 255, 156, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 157, 255, 255, 255, 155, 1, 0, 0, - 0, 0, 0, 0, 0, 121, 255, 255, 223, 5, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 149, 255, 255, 255, 113, 0, 0, 0, 6, 144, 239, 239, - 142, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 121, 255, 255, 248, 0, 0, 0, 0, 0, 0, 0, - 0, 248, 255, 255, 118, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 145, 255, 255, 255, 255, - 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 155, 255, 255, 255, 154, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 155, 255, 255, 255, 154, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 11, 234, 255, 255, 104, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 151, 255, 255, 205, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 25, 121, 250, 255, 255, 157, 0, 0, 0, - 0, 0, 0, 0, 0, 158, 255, 255, 250, 120, 25, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, - 144, 239, 239, 142, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 154, 255, 255, 255, 153, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 154, 255, - 255, 255, 153, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 116, 255, 255, 227, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 255, 255, - 247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 248, 135, - 5, 0, 0, 0, 0, 0, 0, 0, 0, 5, 135, 248, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 152, 255, - 255, 255, 151, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 152, 255, 255, 255, 152, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 9, 231, 255, 255, 109, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, - 0, 16, 255, 255, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, - 255, 255, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 51, 255, 255, 255, 0, 0, 0, 7, 147, 240, 240, 145, 6, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 151, 255, 255, 255, 150, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 151, 255, 255, 255, 150, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 230, 9, - 0, 0, 0, 0, 0, 0, 0, 0, 202, 255, 255, 110, 0, 0, - 0, 0, 0, 0, 0, 113, 255, 255, 202, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 255, 255, 255, 248, 136, 5, 0, 0, 0, 0, 0, - 0, 0, 0, 5, 136, 248, 255, 255, 255, 0, 0, 0, 147, 255, - 255, 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 150, 255, 255, 255, 148, 0, - 0, 0, 0, 0, 0, 0, 0, 149, 255, 255, 255, 149, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, - 228, 255, 255, 114, 0, 0, 0, 0, 0, 0, 0, 0, 110, 255, - 255, 249, 111, 16, 0, 0, 0, 16, 113, 250, 255, 255, 110, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 24, 118, 250, 255, 255, 157, 0, - 0, 0, 0, 0, 0, 0, 0, 158, 255, 255, 249, 117, 24, 0, - 0, 0, 240, 255, 255, 255, 255, 239, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, - 255, 255, 255, 146, 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, - 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 105, 255, 255, 233, 11, 0, 0, 0, 0, 0, - 0, 0, 6, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 204, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 120, - 255, 255, 248, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 255, - 117, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 239, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 147, 255, 255, 255, 144, 0, 0, 0, 0, 146, - 255, 255, 255, 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 6, 224, 255, 255, 119, 0, - 0, 0, 0, 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 203, 24, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 25, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 24, 0, 0, 0, 0, 0, 145, 255, 255, 255, - 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 255, 255, 255, 80, - 0, 0, 119, 255, 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, - 255, 255, 236, 13, 0, 0, 0, 0, 0, 0, 0, 0, 6, 109, - 201, 243, 255, 255, 255, 243, 201, 109, 6, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 6, 144, 239, 239, 142, 5, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 144, 255, 143, 0, 0, 0, 24, 215, 255, 144, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 4, 221, 255, 255, 125, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 24, 119, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 239, 16, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 217, - 255, 255, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, - 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 90, 255, 255, 241, 18, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 255, - 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2, 213, 255, 255, 135, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, - 255, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, - 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 255, - 255, 244, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 24, 0, 0, 0, 0, 0, 0, - 25, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 209, 255, 255, 140, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 234, 255, 255, 118, 0, 0, - 0, 0, 0, 0, 121, 255, 255, 233, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 80, 255, 255, 222, 4, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 255, - 255, 250, 120, 25, 0, 0, 25, 121, 250, 255, 255, 162, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 184, 117, - 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 35, 241, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, - 240, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 59, 232, 255, 255, 255, 0, 0, - 255, 255, 255, 231, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 130, - 209, 245, 0, 0, 245, 209, 129, 19, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -} /* namespace profont_36 */ +/** + * Parrot Drones Awesome Video Viewer + * OpenGL ES 2.0 HUD rendering library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 "pdraw_gles2hud_priv.h" + + +/** + * ProFont by Andrew Welch, Carl Osterwald and Stephen C. Gilardi + * http://tobiasjung.name/profont/ + */ + +namespace profont_36 { + +file_header font = { + 256, + 202, + {27, -9, 18}, + {0.134f, -0.045f, 0.089f}, + { + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {218, 136, 0, 0, 18, 0, 0}, + {0.852f, + 0.673f, + 0.000f, + 0.000f, + 0.070f, + 0.000f, + 0.000f}, + }, + { + /*!*/ + {51, 103, 3, 21, 18, 6, -21}, + {0.199f, + 0.510f, + 0.012f, + 0.104f, + 0.070f, + 0.023f, + -0.104f}, + }, + { + /*"*/ + {121, 136, 9, 9, 18, 3, -24}, + {0.473f, + 0.673f, + 0.035f, + 0.045f, + 0.070f, + 0.012f, + -0.119f}, + }, + { + /*#*/ + {73, 103, 15, 15, 18, 0, -21}, + {0.285f, + 0.510f, + 0.059f, + 0.074f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*$*/ + {90, 103, 15, 27, 18, 0, -24}, + {0.352f, + 0.510f, + 0.059f, + 0.134f, + 0.070f, + 0.000f, + -0.119f}, + }, + { + /*%*/ + {107, 103, 15, 21, 18, 0, -21}, + {0.418f, + 0.510f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*&*/ + {142, 103, 16, 22, 18, 0, -21}, + {0.555f, + 0.510f, + 0.063f, + 0.109f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*'*/ + {108, 136, 3, 9, 18, 6, -24}, + {0.422f, + 0.673f, + 0.012f, + 0.045f, + 0.070f, + 0.023f, + -0.119f}, + }, + { + /*(*/ + {177, 103, 10, 29, 18, 3, -25}, + {0.691f, + 0.510f, + 0.039f, + 0.144f, + 0.070f, + 0.012f, + -0.124f}, + }, + { + /*)*/ + {189, 103, 10, 29, 18, 2, -25}, + {0.738f, + 0.510f, + 0.039f, + 0.144f, + 0.070f, + 0.008f, + -0.124f}, + }, + { + /***/ + {160, 103, 15, 15, 18, 0, -21}, + {0.625f, + 0.510f, + 0.059f, + 0.074f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*+*/ + {44, 136, 15, 15, 18, 0, -18}, + {0.172f, + 0.673f, + 0.059f, + 0.074f, + 0.070f, + 0.000f, + -0.089f}, + }, + { + /*,*/ + {201, 136, 7, 13, 18, 3, -7}, + {0.785f, + 0.673f, + 0.027f, + 0.064f, + 0.070f, + 0.012f, + -0.035f}, + }, + { + /*-*/ + {201, 103, 9, 3, 18, 3, -12}, + {0.785f, + 0.510f, + 0.035f, + 0.015f, + 0.070f, + 0.012f, + -0.059f}, + }, + { + /*.*/ + {210, 136, 6, 6, 18, 4, -7}, + {0.820f, + 0.673f, + 0.023f, + 0.030f, + 0.070f, + 0.016f, + -0.035f}, + }, + { + /*/*/ + {164, 136, 18, 30, 18, 0, -24}, + {0.641f, + 0.673f, + 0.070f, + 0.149f, + 0.070f, + 0.000f, + -0.119f}, + }, + { + /*0*/ + {134, 80, 15, 21, 18, 0, -21}, + {0.523f, + 0.396f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*1*/ + {151, 80, 15, 21, 18, 0, -21}, + {0.590f, + 0.396f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*2*/ + {168, 80, 15, 21, 18, 0, -21}, + {0.656f, + 0.396f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*3*/ + {185, 80, 15, 21, 18, 0, -21}, + {0.723f, + 0.396f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*4*/ + {202, 80, 15, 21, 18, 0, -21}, + {0.789f, + 0.396f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*5*/ + {219, 80, 15, 21, 18, 0, -21}, + {0.855f, + 0.396f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*6*/ + {236, 80, 15, 21, 18, 0, -21}, + {0.922f, + 0.396f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*7*/ + {0, 103, 15, 21, 18, 0, -21}, + {0.000f, + 0.510f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*8*/ + {17, 103, 15, 21, 18, 0, -21}, + {0.066f, + 0.510f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*9*/ + {34, 103, 15, 21, 18, 0, -21}, + {0.133f, + 0.510f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*:*/ + {113, 136, 6, 15, 18, 4, -16}, + {0.441f, + 0.673f, + 0.023f, + 0.074f, + 0.070f, + 0.016f, + -0.079f}, + }, + { + /*;*/ + {99, 136, 7, 22, 18, 3, -16}, + {0.387f, + 0.673f, + 0.027f, + 0.109f, + 0.070f, + 0.012f, + -0.079f}, + }, + { + /*<*/ + {132, 136, 14, 23, 18, 2, -22}, + {0.516f, + 0.673f, + 0.055f, + 0.114f, + 0.070f, + 0.008f, + -0.109f}, + }, + { + /*=*/ + {27, 136, 15, 9, 18, 0, -15}, + {0.105f, + 0.673f, + 0.059f, + 0.045f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /*>*/ + {148, 136, 14, 23, 18, 2, -22}, + {0.578f, + 0.673f, + 0.055f, + 0.114f, + 0.070f, + 0.008f, + -0.109f}, + }, + { + /*?*/ + {184, 136, 15, 21, 18, 0, -21}, + {0.719f, + 0.673f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*@*/ + {56, 103, 15, 21, 18, 0, -21}, + {0.219f, + 0.510f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*A*/ + {1, 1, 15, 21, 18, 0, -21}, + {0.004f, + 0.005f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*B*/ + {18, 1, 15, 21, 18, 0, -21}, + {0.070f, + 0.005f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*C*/ + {35, 1, 15, 21, 18, 0, -21}, + {0.137f, + 0.005f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*D*/ + {52, 1, 15, 21, 18, 0, -21}, + {0.203f, + 0.005f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*E*/ + {69, 1, 15, 21, 18, 0, -21}, + {0.270f, + 0.005f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*F*/ + {86, 1, 15, 21, 18, 0, -21}, + {0.336f, + 0.005f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*G*/ + {103, 1, 15, 21, 18, 0, -21}, + {0.402f, + 0.005f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*H*/ + {120, 1, 15, 21, 18, 0, -21}, + {0.469f, + 0.005f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*I*/ + {137, 1, 15, 21, 18, 0, -21}, + {0.535f, + 0.005f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*J*/ + {154, 1, 15, 21, 18, 0, -21}, + {0.602f, + 0.005f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*K*/ + {171, 1, 15, 21, 18, 0, -21}, + {0.668f, + 0.005f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*L*/ + {188, 1, 15, 21, 18, 0, -21}, + {0.734f, + 0.005f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*M*/ + {205, 1, 15, 21, 18, 0, -21}, + {0.801f, + 0.005f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*N*/ + {222, 1, 15, 21, 18, 0, -21}, + {0.867f, + 0.005f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*O*/ + {0, 24, 15, 21, 18, 0, -21}, + {0.000f, + 0.119f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*P*/ + {17, 24, 15, 21, 18, 0, -21}, + {0.066f, + 0.119f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*Q*/ + {34, 24, 16, 25, 18, 0, -21}, + {0.133f, + 0.119f, + 0.063f, + 0.124f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*R*/ + {52, 24, 15, 21, 18, 0, -21}, + {0.203f, + 0.119f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*S*/ + {69, 24, 15, 21, 18, 0, -21}, + {0.270f, + 0.119f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*T*/ + {86, 24, 15, 21, 18, 0, -21}, + {0.336f, + 0.119f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*U*/ + {103, 24, 15, 21, 18, 0, -21}, + {0.402f, + 0.119f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*V*/ + {120, 24, 15, 21, 18, 0, -21}, + {0.469f, + 0.119f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*W*/ + {137, 24, 15, 21, 18, 0, -21}, + {0.535f, + 0.119f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*X*/ + {154, 24, 15, 21, 18, 0, -21}, + {0.602f, + 0.119f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*Y*/ + {171, 24, 15, 21, 18, 0, -21}, + {0.668f, + 0.119f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*Z*/ + {188, 24, 15, 21, 18, 0, -21}, + {0.734f, + 0.119f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*[*/ + {61, 136, 6, 27, 18, 6, -24}, + {0.238f, + 0.673f, + 0.023f, + 0.134f, + 0.070f, + 0.023f, + -0.119f}, + }, + { + /*\*/ + {232, 103, 17, 31, 18, 1, -25}, + {0.906f, + 0.510f, + 0.066f, + 0.153f, + 0.070f, + 0.004f, + -0.124f}, + }, + { + /*]*/ + {69, 136, 6, 27, 18, 3, -24}, + {0.270f, + 0.673f, + 0.023f, + 0.134f, + 0.070f, + 0.012f, + -0.119f}, + }, + { + /*^*/ + {124, 103, 16, 11, 18, 0, -22}, + {0.484f, + 0.510f, + 0.063f, + 0.054f, + 0.070f, + 0.000f, + -0.109f}, + }, + { + /*_*/ + {212, 103, 18, 3, 18, 0, 3}, + {0.828f, + 0.510f, + 0.070f, + 0.015f, + 0.070f, + 0.000f, + 0.015f}, + }, + { + /*`*/ + {0, 136, 8, 7, 18, 2, -24}, + {0.000f, + 0.673f, + 0.031f, + 0.035f, + 0.070f, + 0.008f, + -0.119f}, + }, + { + /*a*/ + {205, 24, 15, 15, 18, 0, -15}, + {0.801f, + 0.119f, + 0.059f, + 0.074f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /*b*/ + {222, 24, 15, 21, 18, 0, -21}, + {0.867f, + 0.119f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*c*/ + {0, 51, 15, 15, 18, 0, -15}, + {0.000f, + 0.252f, + 0.059f, + 0.074f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /*d*/ + {17, 51, 15, 21, 18, 0, -21}, + {0.066f, + 0.252f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*e*/ + {34, 51, 15, 15, 18, 0, -15}, + {0.133f, + 0.252f, + 0.059f, + 0.074f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /*f*/ + {51, 51, 12, 21, 18, 3, -21}, + {0.199f, + 0.252f, + 0.047f, + 0.104f, + 0.070f, + 0.012f, + -0.104f}, + }, + { + /*g*/ + {65, 51, 15, 21, 18, 0, -15}, + {0.254f, + 0.252f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /*h*/ + {82, 51, 15, 21, 18, 0, -21}, + {0.320f, + 0.252f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*i*/ + {99, 51, 9, 21, 18, 3, -21}, + {0.387f, + 0.252f, + 0.035f, + 0.104f, + 0.070f, + 0.012f, + -0.104f}, + }, + { + /*j*/ + {110, 51, 9, 27, 18, 0, -21}, + {0.430f, + 0.252f, + 0.035f, + 0.134f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*k*/ + {121, 51, 15, 21, 18, 0, -21}, + {0.473f, + 0.252f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.104f}, + }, + { + /*l*/ + {138, 51, 9, 21, 18, 3, -21}, + {0.539f, + 0.252f, + 0.035f, + 0.104f, + 0.070f, + 0.012f, + -0.104f}, + }, + { + /*m*/ + {149, 51, 15, 15, 18, 0, -15}, + {0.582f, + 0.252f, + 0.059f, + 0.074f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /*n*/ + {166, 51, 15, 15, 18, 0, -15}, + {0.648f, + 0.252f, + 0.059f, + 0.074f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /*o*/ + {183, 51, 15, 15, 18, 0, -15}, + {0.715f, + 0.252f, + 0.059f, + 0.074f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /*p*/ + {200, 51, 15, 21, 18, 0, -15}, + {0.781f, + 0.252f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /*q*/ + {217, 51, 15, 21, 18, 0, -15}, + {0.848f, + 0.252f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /*r*/ + {234, 51, 15, 15, 18, 0, -15}, + {0.914f, + 0.252f, + 0.059f, + 0.074f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /*s*/ + {0, 80, 16, 15, 18, 0, -15}, + {0.000f, + 0.396f, + 0.063f, + 0.074f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /*t*/ + {18, 80, 12, 21, 18, 3, -21}, + {0.070f, + 0.396f, + 0.047f, + 0.104f, + 0.070f, + 0.012f, + -0.104f}, + }, + { + /*u*/ + {32, 80, 15, 15, 18, 0, -15}, + {0.125f, + 0.396f, + 0.059f, + 0.074f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /*v*/ + {49, 80, 15, 15, 18, 0, -15}, + {0.191f, + 0.396f, + 0.059f, + 0.074f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /*w*/ + {66, 80, 15, 15, 18, 0, -15}, + {0.258f, + 0.396f, + 0.059f, + 0.074f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /*x*/ + {83, 80, 15, 15, 18, 0, -15}, + {0.324f, + 0.396f, + 0.059f, + 0.074f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /*y*/ + {100, 80, 15, 21, 18, 0, -15}, + {0.391f, + 0.396f, + 0.059f, + 0.104f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /*z*/ + {117, 80, 15, 15, 18, 0, -15}, + {0.457f, + 0.396f, + 0.059f, + 0.074f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /*{*/ + {77, 136, 9, 33, 18, 3, -27}, + {0.301f, + 0.673f, + 0.035f, + 0.163f, + 0.070f, + 0.012f, + -0.134f}, + }, + { + /*|*/ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /*}*/ + {88, 136, 9, 33, 18, 3, -27}, + {0.344f, + 0.673f, + 0.035f, + 0.163f, + 0.070f, + 0.012f, + -0.134f}, + }, + { + /*~*/ + {10, 136, 15, 6, 18, 0, -15}, + {0.039f, + 0.673f, + 0.059f, + 0.030f, + 0.070f, + 0.000f, + -0.074f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + { + /* */ + {0, 0, 0, 0, 0, 0, 0}, + {0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f, + 0.000f}, + }, + }, +}; + +int image_width = 256; +int image_height = 202; +unsigned char image[] = { + // gray-scale image + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 244, 203, 113, 8, 0, 0, 0, 0, 0, 0, 7, + 111, 203, 244, 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 244, 202, 110, 6, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 111, 203, + 244, 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 0, 0, 0, 0, 7, 111, 203, 244, 255, 255, 255, 244, + 203, 111, 7, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 255, 255, 255, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 208, 27, 0, 0, + 0, 0, 25, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, + 24, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 203, 24, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 24, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 0, 0, 25, 206, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 205, 25, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 161, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 208, 7, 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 203, 6, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 203, 6, 0, 0, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 203, 6, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 7, 206, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 205, 7, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, + 159, 2, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 15, 110, 249, 255, 255, 112, 0, 0, 111, 255, 255, 248, 108, + 15, 0, 0, 0, 15, 110, 249, 255, 255, 109, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 15, 110, 249, 255, 255, 109, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 248, 108, 15, 0, + 0, 0, 15, 110, 249, 255, 255, 109, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 111, 255, 255, 248, 108, 15, 0, 0, 0, 15, 110, 249, + 255, 255, 111, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 128, + 255, 255, 255, 158, 2, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 202, 0, 0, 203, + 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 201, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 111, + 255, 255, 201, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, 255, 255, + 108, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 201, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 203, 255, 255, 108, 0, 0, 0, 0, + 0, 0, 0, 111, 255, 255, 202, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 128, 255, 255, 255, 158, 2, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, + 243, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, + 16, 255, 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 16, 255, 255, 243, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, 255, + 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 15, + 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 243, 0, 0, 255, + 255, 255, 0, 0, 0, 128, 255, 255, 255, 157, 1, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 16, 255, 255, 245, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 255, 255, 255, 0, 0, 128, 255, 255, 255, 156, 1, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 113, 255, 255, 195, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 128, 255, 255, + 255, 156, 1, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 143, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 16, 113, 250, 255, 255, 83, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, + 128, 255, 255, 255, 155, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 143, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, + 255, 255, 64, 0, 0, 0, 0, 0, 0, 0, 64, 255, 255, 252, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 148, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 255, 154, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 143, 255, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 160, 255, 255, 192, 0, 0, 0, 0, 0, 0, 0, + 192, 255, 255, 160, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 196, 1, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 216, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 143, 255, 255, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 252, 255, 255, 64, 0, 0, + 0, 0, 0, 64, 255, 255, 252, 36, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 149, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, + 255, 154, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, + 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 143, 255, 255, + 255, 143, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, + 255, 192, 0, 0, 0, 0, 0, 192, 255, 255, 160, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 15, 110, 249, 255, 255, + 83, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, + 255, 255, 128, 255, 255, 255, 154, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 8, 255, 255, 255, + 8, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, + 143, 255, 255, 255, 143, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 35, 251, 255, 255, 64, 0, 0, 0, 64, 255, 255, 252, + 36, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 255, 255, 196, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 255, 255, 255, 0, 128, 255, 255, 255, 154, 1, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 8, + 184, 255, 255, 255, 184, 8, 0, 255, 255, 255, 0, 0, 255, 255, + 255, 0, 0, 143, 255, 255, 255, 143, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 192, 0, 0, 0, + 192, 255, 255, 161, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 16, 255, 255, 245, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 128, 255, + 255, 255, 154, 1, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 8, 184, 255, 255, 255, 255, 255, 184, 8, 255, 255, 255, + 0, 0, 255, 255, 255, 0, 143, 255, 255, 255, 143, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 251, 255, + 255, 64, 0, 64, 255, 255, 252, 36, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 243, 0, + 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, 255, + 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 16, 255, 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, + 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 243, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, + 0, 0, 0, 128, 255, 255, 255, 154, 1, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 184, 255, 255, 255, 240, 255, 255, 255, + 184, 255, 255, 255, 0, 0, 255, 255, 255, 143, 255, 255, 255, 143, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 158, 255, 255, 192, 0, 192, 255, 255, 162, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 113, + 255, 255, 202, 0, 0, 202, 255, 255, 110, 0, 0, 0, 0, 0, + 0, 0, 113, 255, 255, 201, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 113, 255, 255, 201, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 202, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, + 113, 255, 255, 201, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 128, 255, 255, 255, 154, 1, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 184, + 16, 184, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, + 255, 255, 143, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 34, 251, 255, 255, 128, 255, 255, 252, + 37, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 16, 113, 250, 255, 255, 111, 0, 0, 110, 255, 255, 249, 111, + 16, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 249, 111, 16, 0, + 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 128, + 255, 255, 255, 154, 1, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 255, 184, 8, 0, 8, 184, 255, 255, 255, 255, 255, 0, 0, + 255, 255, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 157, 255, 255, + 255, 255, 255, 162, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, 7, 0, 0, 6, + 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, 5, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 201, 5, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 6, 203, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, 5, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 128, 255, 255, 255, 154, 1, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 184, 8, 0, 0, 0, 8, 184, 255, 255, + 255, 255, 0, 0, 255, 255, 255, 255, 143, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 33, 251, 255, 255, 255, 252, 38, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, 25, + 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 201, 22, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 201, 22, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 0, 0, 24, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, + 22, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 154, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 184, 8, 0, 0, 0, 0, + 0, 8, 184, 255, 255, 255, 0, 0, 255, 255, 255, 143, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 156, 255, 255, 255, 163, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 243, + 202, 111, 7, 0, 0, 0, 0, 0, 0, 6, 109, 201, 243, 255, + 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 0, 6, 109, 201, 243, 255, 255, 255, + 243, 201, 108, 5, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 128, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 184, 8, 0, + 0, 0, 0, 0, 0, 0, 8, 184, 255, 255, 0, 0, 255, 255, + 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 111, 203, 244, 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 21, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 7, + 112, 203, 244, 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 7, 112, 203, 244, 255, 255, 255, 244, 203, + 111, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 184, 8, 0, + 0, 0, 0, 0, 0, 0, 8, 184, 255, 255, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 8, 114, 204, 244, 241, + 167, 37, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 25, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 203, 24, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 150, 222, 30, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 27, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, + 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 27, 208, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 205, 25, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 64, 255, 64, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 184, 8, 0, 0, 0, 0, 0, 8, 184, 255, 255, 255, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 29, 210, + 255, 255, 255, 255, 255, 245, 87, 0, 0, 255, 255, 255, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 24, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 203, 6, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 150, 255, 255, 221, 23, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 7, 208, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 203, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 7, 208, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 205, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 192, 255, 192, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 184, 8, 0, 0, 0, 8, 184, + 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 0, 8, 210, 255, 255, 255, 255, 255, 255, 255, 254, 99, 0, 255, + 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 203, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 248, + 108, 15, 0, 0, 0, 15, 110, 249, 255, 255, 109, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 150, 255, + 255, 255, 166, 4, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 113, 255, 255, 248, 108, + 15, 0, 0, 0, 15, 110, 249, 255, 255, 109, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 113, 255, 255, 248, 108, 15, 0, 0, 0, 15, 110, 249, 255, + 255, 111, 0, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 64, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 184, 8, + 0, 8, 184, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 114, 255, 255, 248, 108, 15, 18, 104, 237, + 255, 254, 96, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 15, 110, 249, 255, 255, 109, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 203, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, + 201, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 7, 111, 203, 244, 255, 255, + 255, 249, 255, 255, 255, 161, 3, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 204, + 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 201, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 204, 255, 255, 108, 0, 0, 0, 0, 0, + 0, 0, 111, 255, 255, 202, 0, 0, 0, 0, 0, 0, 0, 192, + 255, 255, 255, 192, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 255, 255, 184, 16, 184, 255, 255, 255, 255, 255, 255, 0, 0, + 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 94, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 255, 255, 108, 0, + 0, 0, 0, 36, 224, 255, 253, 255, 255, 255, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 201, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, + 0, 16, 255, 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 206, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 243, 27, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, + 16, 255, 255, 243, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 15, 0, + 0, 0, 0, 0, 0, 0, 16, 255, 255, 243, 0, 0, 0, 0, + 0, 0, 64, 255, 255, 255, 255, 255, 64, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 184, 255, 255, 255, 240, 255, 255, 255, 184, 255, + 255, 255, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, + 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 206, 255, 255, 253, + 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, + 255, 255, 15, 0, 0, 0, 0, 0, 32, 224, 255, 255, 255, 255, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, + 255, 255, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 212, + 7, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 16, 255, 255, 245, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 16, 255, 255, 243, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 192, 255, 255, 247, 255, 255, 192, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 8, 184, 255, 255, 255, 255, + 255, 184, 8, 255, 255, 255, 0, 0, 0, 143, 255, 255, 255, 143, + 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 17, 205, 255, 255, 253, 94, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 32, + 224, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 111, 255, 255, 249, 117, 25, 155, 255, 255, 255, + 188, 247, 255, 255, 116, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 113, 255, 255, 195, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 113, 255, 255, 201, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 64, 255, 255, 251, 70, + 252, 255, 255, 64, 0, 0, 0, 0, 0, 255, 255, 255, 0, 8, + 184, 255, 255, 255, 184, 8, 0, 255, 255, 255, 0, 0, 0, 0, + 143, 255, 255, 255, 143, 1, 143, 255, 255, 255, 143, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, + 0, 0, 0, 0, 0, 17, 205, 255, 255, 253, 94, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 32, 255, 255, 255, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 203, 255, 255, 118, 0, 21, + 222, 255, 255, 159, 2, 105, 255, 255, 204, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 16, 113, 250, 255, 255, 83, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 113, 250, 255, + 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 192, + 255, 255, 157, 0, 160, 255, 255, 192, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 8, 255, 255, 255, 8, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 143, 255, 255, 255, 207, 255, 255, 255, 143, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 255, + 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 17, 204, 255, 255, + 253, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, 0, 244, 255, + 255, 25, 0, 0, 30, 221, 158, 2, 0, 16, 255, 255, 242, 0, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 148, 0, 0, 0, 0, 0, 7, 111, 203, 244, 255, 255, 255, + 255, 255, 255, 255, 201, 5, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 64, 255, 255, 251, 33, 0, 36, 252, 255, 255, 64, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, + 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, + 255, 255, 207, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, + 0, 17, 204, 255, 255, 253, 94, 0, 0, 0, 0, 0, 0, 0, + 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 16, 255, 255, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 24, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 20, 2, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 196, 1, 0, 0, 0, 0, 25, 206, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 201, 22, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 192, 255, 255, 156, 0, 0, 0, 160, + 255, 255, 192, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 1, 207, 255, 255, 255, 207, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 143, 255, 255, 255, 143, 1, 143, 255, 255, 255, 143, 0, 0, + 0, 0, 0, 0, 0, 0, 16, 204, 255, 255, 253, 94, 0, 0, + 0, 0, 0, 0, 0, 203, 255, 255, 110, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 113, 255, 255, 201, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 203, 6, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 149, 0, 0, 0, 7, + 206, 255, 255, 255, 255, 255, 255, 255, 243, 201, 108, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 64, 255, 255, 250, 32, + 0, 0, 0, 36, 252, 255, 255, 64, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 143, 255, 255, 255, 255, 255, 143, 0, 0, 0, + 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 143, 255, + 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 16, 203, 255, + 255, 253, 94, 0, 0, 0, 0, 0, 0, 113, 255, 255, 249, 111, + 16, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 15, 110, 249, 255, 255, 109, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 15, 110, 249, 255, 255, + 83, 0, 0, 111, 255, 255, 248, 108, 15, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 192, + 255, 255, 154, 0, 0, 0, 0, 0, 160, 255, 255, 192, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 143, 255, 255, 255, 207, 255, 255, + 255, 143, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, + 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, + 0, 0, 16, 202, 255, 255, 253, 94, 0, 0, 0, 0, 0, 8, + 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 201, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 201, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 255, 255, 196, 0, 0, 203, 255, 255, 108, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 64, 255, 255, 250, 31, 0, 0, 0, 0, 0, 36, 252, + 255, 255, 64, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 143, 255, 255, 255, + 143, 1, 143, 255, 255, 255, 143, 0, 0, 0, 0, 255, 255, 255, + 143, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 16, 202, 255, 255, 253, 94, 0, + 0, 0, 0, 0, 27, 208, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 201, 22, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, + 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 16, 255, 255, 245, 0, 0, 244, 255, 255, + 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 192, 255, 255, 152, 0, 0, 0, 0, + 0, 0, 0, 160, 255, 255, 192, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 143, + 255, 255, 255, 143, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 202, + 255, 255, 253, 94, 0, 0, 0, 0, 0, 7, 112, 202, 243, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, + 255, 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 16, 255, 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 243, 0, + 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, 255, + 255, 243, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 30, + 0, 0, 0, 0, 0, 0, 0, 36, 255, 255, 255, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 143, 255, + 255, 255, 143, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 15, 201, 255, 255, 253, 94, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 202, 255, 255, 110, 0, 0, 0, 0, + 0, 0, 0, 113, 255, 255, 201, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 113, 255, 255, 202, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 113, + 255, 255, 202, 0, 0, 202, 255, 255, 110, 0, 0, 0, 0, 0, + 0, 0, 113, 255, 255, 201, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 255, 255, 255, 143, 0, 0, 0, 0, + 0, 0, 0, 143, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 15, 201, 255, 255, 253, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 249, + 111, 16, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 16, 113, 250, 255, 255, 111, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 16, 113, 250, 255, 255, 111, 0, 0, 110, 255, 255, 249, 111, + 16, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, + 5, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 206, 7, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, 7, 0, 0, 6, + 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, 5, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 201, 22, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 206, 25, 0, 0, 0, 244, 255, 255, 24, + 0, 0, 0, 0, 0, 0, 0, 25, 255, 255, 243, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, 25, + 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 201, 22, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 109, 201, 243, + 255, 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 243, 202, 111, 7, 0, 0, 0, 0, + 202, 255, 255, 118, 0, 0, 0, 0, 0, 0, 0, 121, 255, 255, + 201, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 243, + 202, 111, 7, 0, 0, 0, 0, 0, 0, 6, 109, 201, 243, 255, + 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 110, 255, 255, 250, 120, 25, 0, 0, 0, 25, + 121, 250, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 203, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 201, 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, + 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, 22, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 109, 201, 243, 255, 255, 255, 243, 201, 108, + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 111, 203, 244, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 7, + 111, 203, 244, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 7, 111, 203, 244, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 244, 203, + 111, 7, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 255, 255, 255, 244, 203, 111, 7, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 143, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 7, 111, 203, + 244, 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, + 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 0, 0, 25, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 0, 0, 0, 25, 206, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 205, 25, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, + 255, 255, 205, 25, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 143, 255, 255, 255, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 25, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 24, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 0, 7, 206, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 205, 7, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 205, 7, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 203, 6, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 111, 255, 255, 248, 108, 15, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 248, 108, + 15, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 111, 255, + 255, 248, 108, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 112, 250, 255, + 255, 111, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 24, 118, 250, 255, 255, 111, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, + 143, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, + 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 111, 255, 255, 248, 108, 15, 0, + 0, 0, 15, 110, 249, 255, 255, 109, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, 255, 255, 108, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, + 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 203, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 113, 255, 255, 202, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, + 255, 255, 202, 0, 0, 255, 255, 255, 143, 0, 0, 0, 0, 143, + 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 203, 255, 255, + 108, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 201, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 17, 255, 255, 244, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 24, 255, 255, 244, 0, 0, 255, 255, 255, 255, 143, + 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, 255, + 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 7, 111, 203, 244, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, + 255, 255, 255, 255, 143, 143, 255, 255, 255, 143, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, 0, 0, 0, 7, + 111, 203, 244, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 25, 206, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 255, 255, 255, 0, 0, 255, 255, 255, 32, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 24, 0, 0, + 0, 0, 25, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 0, 0, 255, 255, 255, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 7, 206, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 143, 255, 255, 255, + 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 224, + 32, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 203, 6, 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 224, 32, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, + 255, 255, 243, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 244, 255, 255, 15, 0, 0, + 0, 0, 0, 0, 0, 16, 255, 255, 243, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, + 248, 108, 15, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, + 243, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, + 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 255, 255, 255, 0, 0, 16, 255, 255, 243, 0, 0, + 255, 255, 255, 255, 224, 32, 0, 0, 0, 0, 0, 16, 255, 255, + 243, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, + 16, 255, 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 15, 110, 249, 255, 255, 109, 0, 0, 111, 255, 255, 248, 108, + 15, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, + 255, 255, 224, 32, 0, 0, 0, 0, 0, 16, 255, 255, 243, 0, + 0, 0, 0, 0, 0, 0, 202, 255, 255, 110, 0, 0, 0, 0, + 0, 0, 0, 113, 255, 255, 201, 0, 0, 202, 255, 255, 110, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 202, 255, + 255, 110, 0, 0, 0, 0, 0, 0, 0, 113, 255, 255, 201, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 203, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 113, 255, 255, 201, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 255, 255, 255, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 113, 255, + 255, 201, 0, 0, 255, 255, 255, 254, 255, 224, 36, 0, 0, 0, + 0, 113, 255, 255, 202, 0, 0, 202, 255, 255, 110, 0, 0, 0, + 0, 0, 0, 0, 113, 255, 255, 201, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 201, 0, 0, 203, + 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 255, 255, 255, 254, 255, 224, 36, 0, 0, 0, 0, 113, + 255, 255, 202, 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 249, + 111, 16, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, 110, + 255, 255, 249, 111, 16, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 110, 255, 255, 249, 111, 16, 0, 0, 0, 16, 113, 250, + 255, 255, 108, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 143, 255, 255, + 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, + 16, 113, 250, 255, 255, 108, 0, 0, 255, 255, 255, 99, 254, 255, + 237, 104, 18, 16, 113, 250, 255, 255, 111, 0, 0, 110, 255, 255, + 249, 111, 16, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, + 243, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 255, 255, 255, 99, 254, 255, 237, 104, + 18, 16, 113, 250, 255, 255, 111, 0, 0, 0, 0, 0, 0, 0, + 6, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, + 5, 0, 0, 6, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 6, 203, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 201, 5, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, 5, 0, + 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 201, 5, 0, 0, 255, 255, + 255, 0, 95, 253, 255, 255, 255, 255, 255, 255, 255, 206, 7, 0, + 0, 6, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 201, 5, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, + 95, 253, 255, 255, 255, 255, 255, 255, 255, 206, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 201, 22, 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 24, 203, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 201, 22, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 201, 22, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, 22, 0, + 0, 0, 255, 255, 255, 0, 0, 82, 244, 255, 255, 255, 255, 255, + 206, 25, 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 201, 22, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 255, 255, 255, 0, 0, 82, 244, 255, 255, 255, 255, 255, 206, 25, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 109, 201, 243, + 255, 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 0, 0, 6, + 109, 201, 243, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 6, 109, 201, 243, 255, 255, 255, 243, 201, 108, 5, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 243, 201, 108, 5, 0, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 143, 255, + 255, 255, 143, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 243, 201, + 108, 5, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 35, 165, + 241, 243, 202, 111, 7, 0, 0, 0, 0, 0, 0, 6, 109, 201, + 243, 255, 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 35, 165, 241, 243, + 202, 111, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 15, + 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 15, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, + 255, 255, 243, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 202, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, 202, 255, 255, + 110, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 113, 255, 255, 201, 0, 0, 202, 255, 255, 110, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 110, 255, 255, 249, 111, 16, 0, 0, 0, 0, + 0, 110, 255, 255, 249, 111, 16, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, 110, + 255, 255, 249, 111, 16, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 203, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 6, 203, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 135, 244, 135, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, + 5, 0, 0, 6, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, + 203, 255, 255, 255, 255, 255, 255, 0, 0, 0, 24, 203, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 244, 255, 243, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 201, 22, 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 109, 201, 243, 255, 255, 255, 0, 0, 0, + 0, 6, 109, 201, 243, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 135, 243, 135, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 0, 0, 6, + 109, 201, 243, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 244, 135, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 244, 255, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 135, 243, 135, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, + 255, 255, 255, 255, 255, 255, 252, 235, 191, 114, 15, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 7, 111, 203, 244, 255, 255, 255, + 0, 0, 0, 0, 8, 114, 204, 244, 241, 167, 37, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, + 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 207, 247, 163, + 6, 0, 6, 164, 247, 206, 72, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 255, 244, 203, 111, 7, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 7, 111, 203, 244, 255, 255, + 255, 244, 202, 110, 6, 0, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 0, 0, 0, 8, 114, 204, 244, 255, 255, 255, 244, 203, 113, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 7, 112, 203, 244, 255, + 255, 255, 244, 202, 110, 6, 0, 0, 0, 0, 0, 0, 7, 111, + 203, 244, 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, 0, 0, + 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 236, 80, 0, 0, 0, 0, 0, 0, 0, 0, 25, 206, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 29, 210, 255, 255, 255, 255, 255, + 245, 87, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 192, 255, 255, 255, 192, 0, 0, 0, 0, 0, 0, 0, 0, 80, + 254, 255, 255, 255, 140, 0, 141, 255, 255, 255, 253, 77, 0, 0, + 0, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 143, 255, + 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 205, 25, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 25, 206, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 203, 24, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 29, 210, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 208, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 27, 208, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 24, 0, 0, 0, + 0, 25, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 24, + 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 253, 73, 0, 0, 0, 0, 0, 0, 7, + 206, 255, 255, 255, 255, 255, 255, 255, 0, 0, 8, 210, 255, 255, + 255, 255, 255, 255, 255, 254, 99, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 64, 255, 255, 255, 255, 255, 64, 0, 0, 0, 0, + 0, 0, 15, 236, 255, 255, 254, 255, 250, 56, 250, 255, 254, 255, + 255, 233, 13, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, + 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 205, 7, 0, 0, 143, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 7, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, + 6, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 143, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 0, 8, 210, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 208, 7, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 0, 7, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 203, 6, 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 203, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 25, 83, 195, 255, 255, 206, 0, 0, 0, + 0, 0, 0, 111, 255, 255, 248, 108, 15, 0, 0, 0, 0, 0, + 114, 255, 255, 248, 108, 15, 18, 104, 237, 255, 254, 96, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 192, 255, 255, 248, 255, 255, 192, + 0, 0, 0, 0, 0, 0, 111, 255, 255, 192, 12, 192, 255, 255, + 255, 191, 12, 191, 255, 255, 109, 0, 0, 0, 143, 255, 255, 255, + 143, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 16, 112, 250, 255, 255, 111, 0, + 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 111, 255, 255, 250, 111, 16, 0, 0, 0, 15, + 110, 249, 255, 255, 109, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255, + 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, + 255, 255, 248, 108, 15, 0, 0, 0, 15, 110, 249, 255, 255, 112, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 113, 255, 255, 248, 108, 15, 0, 0, 0, + 15, 110, 249, 255, 255, 109, 0, 0, 111, 255, 255, 248, 108, 15, + 0, 0, 0, 15, 110, 249, 255, 255, 109, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 255, 255, + 247, 0, 0, 0, 0, 0, 0, 203, 255, 255, 108, 0, 0, 0, + 0, 0, 0, 0, 204, 255, 255, 108, 0, 0, 0, 0, 36, 224, + 255, 253, 255, 255, 255, 0, 0, 0, 0, 0, 64, 255, 255, 252, + 72, 252, 255, 255, 64, 0, 0, 0, 0, 0, 189, 255, 255, 82, + 0, 83, 255, 255, 255, 81, 0, 82, 255, 255, 187, 0, 0, 0, + 0, 143, 255, 255, 255, 143, 1, 143, 255, 255, 255, 143, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, + 255, 255, 202, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 203, 255, 255, 193, 2, 0, + 0, 0, 0, 0, 0, 111, 255, 255, 201, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 145, 255, 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 204, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, + 111, 255, 255, 202, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 204, 255, 255, 108, 0, + 0, 0, 0, 0, 0, 0, 111, 255, 255, 201, 0, 0, 203, 255, + 255, 108, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 201, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 25, + 83, 195, 255, 255, 206, 0, 0, 0, 0, 0, 0, 244, 255, 255, + 15, 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 15, 0, 0, + 0, 0, 0, 32, 224, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 192, 255, 255, 160, 0, 160, 255, 255, 192, 0, 0, 0, 0, 0, + 233, 255, 255, 26, 0, 26, 255, 255, 255, 25, 0, 26, 255, 255, + 232, 0, 0, 0, 0, 0, 143, 255, 255, 255, 207, 255, 255, 255, + 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 17, 255, 255, 244, 0, 0, 0, 0, 0, 143, 255, + 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, + 255, 255, 160, 2, 0, 0, 0, 0, 0, 16, 255, 255, 243, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 145, 255, 255, 255, 145, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 15, 0, 0, 0, + 0, 0, 0, 0, 16, 255, 255, 243, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 244, + 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 243, + 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, + 255, 255, 243, 0, 0, 0, 0, 0, 0, 0, 0, 21, 122, 195, + 237, 253, 255, 255, 255, 255, 255, 254, 74, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 32, 224, 255, 255, 255, 0, + 0, 0, 0, 64, 255, 255, 252, 36, 0, 36, 252, 255, 255, 64, + 0, 0, 0, 0, 251, 255, 255, 5, 0, 5, 255, 255, 255, 5, + 0, 5, 255, 255, 251, 0, 0, 0, 0, 0, 0, 143, 255, 255, + 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 7, 111, + 203, 244, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 255, 160, 2, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 255, + 255, 255, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 245, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 90, 241, 255, 255, 255, 255, 255, 255, 255, 255, 236, 81, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 32, + 255, 255, 255, 0, 0, 0, 0, 192, 255, 255, 160, 0, 0, 0, + 160, 255, 255, 192, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 1, 207, 255, 255, 255, 207, 1, 0, 0, 0, 0, 0, 0, + 0, 25, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 159, 2, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 147, 255, 255, 255, 148, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 255, + 255, 195, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 78, 255, 255, 255, 255, 255, 255, 253, 236, 193, 116, + 17, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 64, 255, 255, 252, + 36, 0, 0, 0, 36, 252, 255, 255, 64, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 143, 255, 255, 255, 255, 255, 143, 0, 0, + 0, 0, 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 143, + 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 255, 255, 255, 143, + 255, 255, 255, 159, 2, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 255, 149, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16, 113, 250, 255, 255, 83, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 208, 255, 255, 193, 80, 24, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 192, 255, 255, 160, 0, 0, 0, 0, 0, 160, 255, 255, 192, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 143, 255, 255, 255, 207, 255, + 255, 255, 143, 0, 0, 0, 0, 0, 111, 255, 255, 248, 108, 15, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 144, 255, 255, 255, 158, 2, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 255, 255, 255, 150, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 255, 255, 255, 148, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, + 255, 243, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 16, 255, 255, 243, 0, 0, 0, 0, 0, 0, 247, 255, 255, + 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 64, 255, 255, 252, 36, 0, 0, 0, 0, 0, 36, + 252, 255, 255, 64, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 143, 255, 255, + 255, 143, 1, 143, 255, 255, 255, 143, 0, 0, 0, 0, 203, 255, + 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 144, 255, 255, 255, 158, + 2, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 149, 255, 255, 255, 152, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 196, 1, 0, + 0, 0, 255, 255, 255, 143, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 113, 255, 255, 201, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 113, 255, 255, 201, 0, 0, 0, 0, 0, + 0, 207, 255, 255, 194, 82, 26, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 192, 255, 255, 160, 0, 0, 0, + 0, 0, 0, 0, 160, 255, 255, 192, 0, 0, 255, 255, 255, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 143, 255, 255, 255, 143, 0, 0, 0, 143, 255, 255, 255, 143, 0, + 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 143, 255, 255, 255, 143, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 145, 255, 255, 255, 158, 2, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 149, 255, 255, 255, 153, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 149, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, + 0, 0, 0, 0, 0, 74, 254, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 128, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, + 36, 0, 0, 0, 0, 0, 0, 0, 36, 255, 255, 255, 0, 0, + 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, + 255, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 143, + 255, 255, 255, 143, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 145, 255, 255, 255, 158, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 150, 255, 255, 255, 113, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 15, 110, 249, 255, 255, 83, 0, 0, 0, 143, 255, 255, + 255, 143, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, 5, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 201, 5, 0, 0, 0, 0, 0, 0, 0, 81, 236, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 128, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 143, 0, 0, 0, + 0, 0, 0, 0, 143, 255, 255, 255, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 146, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 151, 255, 255, 205, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 196, 0, 0, + 0, 0, 143, 255, 255, 255, 143, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 201, 22, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 201, 22, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 16, 114, 190, 234, 251, 255, 255, 255, 255, 255, 255, 255, 128, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 146, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 255, 255, 247, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, + 255, 245, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 15, + 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, 243, 0, 0, 0, + 128, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, + 255, 255, 243, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, + 0, 0, 16, 255, 255, 243, 0, 0, 0, 0, 0, 0, 143, 255, + 255, 255, 143, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 202, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, 180, 255, 255, + 201, 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 202, 255, 255, 110, 0, 0, 0, 0, + 0, 0, 0, 113, 255, 255, 202, 0, 0, 203, 255, 255, 110, 0, + 0, 0, 0, 0, 0, 0, 113, 255, 255, 202, 0, 0, 0, 0, + 0, 0, 0, 143, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 202, 255, 255, 110, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 110, 255, 255, 249, 111, 16, 0, 0, 0, 15, + 104, 247, 255, 255, 108, 0, 0, 0, 128, 255, 255, 255, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 249, + 111, 16, 0, 0, 0, 16, 113, 250, 255, 255, 110, 0, 0, 113, + 255, 255, 249, 111, 16, 0, 0, 0, 16, 113, 250, 255, 255, 111, + 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 249, 111, 16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 203, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 201, 5, 0, 0, 0, 0, 0, + 15, 110, 249, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 204, + 6, 0, 0, 8, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 206, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 6, 203, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, + 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, 22, 0, 0, + 0, 0, 0, 0, 0, 0, 111, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 203, 24, 0, 0, 0, 0, 27, 208, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 206, 25, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 143, 255, 255, 255, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 0, 0, 24, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 109, 201, 243, 255, 255, 255, 243, 201, 108, + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 109, 201, 243, + 255, 255, 255, 243, 201, 109, 6, 0, 0, 0, 0, 0, 0, 7, + 112, 202, 243, 255, 255, 255, 243, 202, 111, 7, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 6, 109, 201, 243, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 114, 204, + 244, 255, 255, 255, 244, 203, 113, 8, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 255, 255, 244, 202, 110, 6, 0, 0, 0, + 0, 135, 244, 135, 0, 0, 0, 0, 7, 111, 203, 244, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 91, 212, 248, 212, 90, + 0, 0, 0, 0, 0, 9, 43, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 44, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 76, 0, 0, 0, 0, 0, + 0, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 99, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 29, 210, 255, 255, 255, 255, 255, 255, 255, 255, 255, 208, 27, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 203, 24, 0, 0, 0, 244, 255, 243, 0, 0, 0, 25, 206, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 115, 255, + 255, 255, 255, 255, 111, 0, 0, 0, 8, 186, 239, 52, 0, 0, + 0, 0, 0, 0, 0, 0, 49, 238, 197, 12, 0, 0, 0, 0, + 7, 112, 203, 244, 248, 206, 113, 1, 0, 0, 141, 253, 97, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 255, 137, + 0, 0, 0, 0, 138, 255, 139, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 247, 253, 151, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 210, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 208, 7, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 203, 6, 0, 0, 135, 243, 135, 0, 0, + 7, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 41, 251, 255, 255, 255, 255, 255, 249, 39, 0, 0, 167, 255, + 255, 239, 52, 0, 0, 0, 0, 0, 0, 49, 237, 255, 255, 174, + 0, 0, 0, 27, 208, 255, 255, 255, 255, 255, 255, 149, 1, 143, + 255, 255, 254, 58, 0, 0, 0, 54, 34, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 18, 44, 0, 0, 0, 0, 0, 0, 0, 0, + 141, 255, 255, 255, 79, 0, 0, 80, 255, 255, 255, 140, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 146, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 114, 255, 255, 248, 108, 15, 0, + 0, 0, 15, 107, 248, 255, 255, 112, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 15, 110, 249, 255, 255, 109, 0, 0, 0, + 0, 0, 0, 0, 111, 255, 255, 248, 108, 15, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 8, 114, + 204, 244, 255, 255, 255, 244, 203, 112, 7, 0, 0, 0, 0, 255, + 255, 255, 2, 0, 0, 150, 255, 255, 178, 15, 178, 255, 255, 148, + 0, 0, 50, 238, 255, 255, 239, 52, 0, 0, 0, 0, 49, 237, + 255, 255, 236, 47, 0, 0, 7, 208, 255, 255, 255, 255, 255, 255, + 255, 255, 207, 255, 255, 255, 147, 0, 0, 0, 5, 217, 248, 135, + 12, 0, 255, 255, 255, 0, 7, 117, 239, 213, 3, 0, 0, 0, + 0, 0, 0, 141, 255, 255, 255, 149, 0, 0, 0, 1, 149, 255, + 255, 255, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 249, 255, 247, + 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 204, 255, 255, + 108, 0, 0, 0, 0, 0, 0, 0, 109, 255, 255, 202, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, + 201, 0, 0, 0, 0, 0, 0, 0, 203, 255, 255, 108, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 29, 210, 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, 25, + 0, 0, 0, 255, 255, 255, 158, 2, 0, 217, 255, 255, 58, 0, + 59, 255, 255, 215, 0, 0, 0, 49, 237, 255, 255, 239, 52, 0, + 0, 48, 237, 255, 255, 237, 48, 0, 0, 0, 113, 255, 255, 248, + 108, 15, 14, 150, 255, 255, 255, 255, 255, 149, 0, 0, 0, 0, + 116, 255, 255, 255, 230, 94, 255, 255, 255, 91, 224, 255, 255, 255, + 105, 0, 0, 0, 0, 0, 142, 255, 255, 255, 147, 0, 0, 0, + 0, 0, 0, 148, 255, 255, 255, 141, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 154, 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, 255, + 255, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 16, 255, 255, 243, 0, 0, 0, 0, 0, 0, 0, 244, 255, + 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 0, 0, 8, 210, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 205, 6, 0, 0, 158, 255, 255, 255, 158, 2, 247, + 255, 255, 12, 0, 12, 255, 255, 246, 0, 0, 0, 0, 49, 237, + 255, 255, 239, 52, 48, 236, 255, 255, 237, 48, 0, 0, 0, 0, + 204, 255, 255, 108, 0, 0, 0, 1, 208, 255, 255, 255, 213, 2, + 0, 0, 0, 0, 51, 197, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 220, 79, 0, 0, 0, 0, 142, 255, 255, 255, 146, + 0, 0, 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, 141, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 34, 251, 255, 247, 25, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 246, 255, 255, 15, 0, 0, 0, 0, 0, + 0, 0, 16, 255, 255, 245, 0, 0, 0, 0, 7, 111, 203, 244, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, + 0, 0, 255, 255, 255, 0, 0, 0, 5, 143, 239, 255, 255, 255, + 240, 147, 7, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 114, 255, 255, 249, 117, 24, + 255, 255, 255, 24, 118, 250, 255, 255, 110, 0, 0, 2, 158, 255, + 255, 255, 158, 250, 255, 255, 12, 0, 12, 255, 255, 247, 0, 0, + 0, 0, 0, 49, 237, 255, 255, 239, 236, 255, 255, 237, 49, 0, + 0, 0, 0, 0, 244, 255, 255, 15, 0, 0, 0, 146, 255, 255, + 255, 255, 255, 148, 0, 0, 0, 0, 0, 0, 89, 226, 255, 255, + 255, 255, 255, 255, 255, 233, 107, 5, 0, 0, 0, 0, 143, 255, + 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, + 255, 255, 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 160, 255, 255, 143, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 143, 0, 0, 0, 0, 0, 0, 0, 196, 255, 255, 110, 0, + 0, 0, 0, 0, 0, 0, 113, 255, 255, 195, 0, 0, 0, 25, + 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 140, 255, + 255, 255, 255, 255, 255, 255, 147, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 204, 255, + 255, 117, 0, 0, 255, 255, 255, 0, 0, 120, 255, 255, 201, 0, + 0, 0, 2, 158, 255, 255, 255, 255, 255, 255, 59, 0, 59, 255, + 255, 215, 0, 0, 0, 0, 0, 0, 48, 237, 255, 255, 255, 255, + 238, 50, 0, 0, 0, 0, 0, 0, 248, 255, 255, 14, 0, 0, + 148, 255, 255, 255, 207, 255, 255, 255, 146, 0, 0, 0, 0, 0, + 0, 22, 220, 255, 255, 255, 255, 255, 220, 22, 0, 0, 0, 0, + 0, 143, 255, 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 145, 255, 255, 255, 143, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 252, 255, 246, + 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 84, + 255, 255, 249, 111, 16, 0, 0, 0, 16, 113, 250, 255, 255, 83, + 0, 0, 7, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, + 0, 0, 238, 255, 255, 255, 255, 255, 255, 255, 239, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 244, 255, 255, 24, 0, 0, 255, 255, 255, 0, 0, 25, + 255, 255, 243, 0, 0, 0, 0, 2, 158, 255, 255, 255, 255, 255, + 180, 16, 180, 255, 255, 146, 0, 0, 0, 0, 0, 0, 0, 48, + 236, 255, 255, 238, 50, 0, 0, 0, 0, 0, 0, 0, 206, 255, + 255, 151, 2, 149, 255, 255, 255, 144, 1, 143, 255, 255, 255, 102, + 0, 0, 0, 5, 109, 233, 255, 255, 255, 255, 255, 255, 255, 225, + 87, 0, 0, 0, 0, 255, 255, 255, 143, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 166, 255, 255, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, + 0, 0, 0, 0, 149, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 148, 0, 0, 0, 111, 255, 255, 248, 108, 15, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, + 255, 255, 255, 0, 0, 0, 255, 255, 255, 27, 0, 27, 255, 255, + 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 25, 255, 255, 243, 0, 0, 0, 0, 85, 206, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 250, 37, 0, 0, 0, 0, + 0, 0, 0, 0, 48, 236, 238, 51, 0, 0, 0, 0, 0, 0, + 0, 0, 114, 255, 255, 255, 213, 255, 255, 255, 146, 0, 0, 0, + 143, 255, 184, 8, 0, 0, 79, 221, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 194, 48, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 43, 253, 255, 245, 22, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, + 255, 255, 143, 0, 0, 0, 0, 0, 1, 196, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 196, 1, 0, 0, 0, 203, 255, 255, 108, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, + 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 121, 255, 255, 201, 0, 0, 0, + 117, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 110, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 52, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 149, 255, 255, 255, 255, 255, 148, + 0, 0, 0, 0, 0, 101, 8, 0, 0, 0, 106, 255, 255, 255, + 219, 83, 255, 255, 255, 101, 233, 255, 255, 255, 107, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 140, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 150, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 149, 0, 0, 0, + 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, + 255, 255, 255, 27, 0, 27, 255, 255, 255, 0, 0, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 25, 121, 250, 255, 255, + 108, 0, 0, 43, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 214, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 214, 255, + 255, 255, 213, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 213, 237, 112, 5, 0, 255, 255, 255, 0, 14, 140, 250, 210, + 3, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 254, 255, 245, + 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, + 0, 84, 255, 255, 248, 108, 15, 0, 0, 0, 15, 110, 249, 255, + 255, 83, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 255, 255, + 255, 0, 0, 0, 240, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 8, 114, 204, 244, 255, 255, 255, 255, + 255, 255, 255, 201, 5, 0, 0, 154, 255, 255, 178, 15, 178, 255, + 255, 255, 255, 255, 158, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 156, 255, 255, 255, 255, 255, 149, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 44, 17, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 37, 50, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 177, 255, 255, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, + 255, 255, 255, 0, 0, 197, 255, 255, 108, 0, 0, 0, 0, 0, + 0, 0, 111, 255, 255, 196, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, + 0, 0, 255, 255, 255, 0, 0, 0, 145, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 29, 210, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 201, 22, 0, 0, 0, 219, 255, 255, + 58, 0, 59, 255, 255, 255, 255, 255, 255, 158, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 114, 255, 255, 255, 207, 255, 255, 255, 149, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 53, 255, 255, 244, 21, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 246, 255, 255, 15, 0, + 0, 0, 0, 0, 0, 0, 16, 255, 255, 245, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 6, 144, + 239, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 8, 210, + 255, 255, 255, 255, 255, 255, 255, 243, 201, 108, 5, 0, 0, 0, + 0, 249, 255, 255, 12, 0, 12, 255, 255, 253, 158, 255, 255, 255, + 158, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 206, 255, 255, 151, 1, 137, + 255, 255, 255, 149, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 183, 255, 255, 136, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 244, + 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 243, + 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 16, + 255, 255, 243, 0, 0, 255, 255, 255, 0, 0, 244, 255, 255, 15, + 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 243, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 114, 255, 255, 249, 117, 24, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 247, 255, 255, 12, 0, 12, 255, 255, 248, + 2, 158, 255, 255, 255, 158, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, + 255, 14, 0, 0, 138, 255, 255, 255, 148, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 255, 255, 243, + 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 203, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, + 113, 255, 255, 202, 0, 0, 202, 255, 255, 110, 0, 0, 0, 0, + 0, 0, 0, 113, 255, 255, 201, 0, 0, 255, 255, 255, 0, 0, + 202, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, 113, 255, 255, + 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 204, 255, 255, 117, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 217, 255, 255, 59, 0, + 59, 255, 255, 214, 0, 2, 158, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 244, 255, 255, 15, 0, 0, 0, 139, 255, 255, 255, 141, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 189, 255, 255, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 113, 255, 255, 248, 108, 16, 0, + 0, 0, 16, 113, 250, 255, 255, 111, 0, 0, 110, 255, 255, 249, + 111, 16, 0, 0, 0, 16, 113, 250, 255, 255, 108, 0, 0, 255, + 255, 255, 0, 0, 110, 255, 255, 249, 111, 16, 0, 0, 0, 16, + 113, 250, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 24, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 149, + 255, 255, 180, 16, 180, 255, 255, 143, 0, 0, 2, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 203, 255, 255, 110, 0, 0, 0, 0, + 139, 255, 255, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 64, 255, 255, 242, 19, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 8, 208, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, 7, 0, 0, + 6, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, + 5, 0, 0, 255, 255, 255, 0, 0, 6, 203, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 201, 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 244, 255, 255, 24, 0, 0, 255, 255, 255, 0, 0, 25, 255, 255, + 243, 0, 0, 40, 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 255, 255, 249, + 111, 16, 0, 0, 27, 255, 255, 238, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 195, 255, 255, 133, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, + 0, 0, 27, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, + 25, 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 201, 22, 0, 0, 0, 255, 255, 255, 0, 0, 0, 24, + 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 201, 22, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 203, 255, 255, 118, 0, 0, 255, 255, 255, 0, + 0, 121, 255, 255, 201, 0, 0, 0, 112, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 163, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 255, 255, 241, + 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 0, 7, 112, 202, 243, 255, 255, 255, + 243, 202, 111, 7, 0, 0, 0, 0, 0, 0, 6, 109, 201, 243, + 255, 255, 255, 243, 201, 108, 5, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 6, 109, 201, 243, 255, 255, 255, 243, 201, 108, + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 113, 255, 255, 250, 120, 25, + 255, 255, 255, 25, 121, 250, 255, 255, 109, 0, 0, 0, 0, 90, + 210, 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 27, 208, 255, 255, 255, 255, 255, 255, 255, + 228, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 143, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 200, 255, 255, 131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 208, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 203, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 112, 202, 243, + 255, 255, 237, 163, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 75, 255, 255, 240, 17, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 27, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 204, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 205, 255, 255, 129, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 7, 112, 202, 243, 255, 255, + 255, 243, 202, 110, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, + 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, + 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 255, 255, 240, + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 143, + 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 210, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 255, 143, 0, 0, + 0, 0, 143, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 87, 255, 255, 239, 15, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, + 255, 255, 80, 0, 0, 80, 255, 255, 255, 143, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3, 215, 255, 255, 126, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 143, 255, 143, 0, 0, 0, 0, 143, 255, 143, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 255, 255, 238, + 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, + 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 186, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 103, 209, 14, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 50, 167, 8, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 11, 140, 226, 250, 223, 132, 9, 0, 0, 0, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 20, 131, 210, 246, + 0, 0, 246, 210, 130, 19, 0, 0, 0, 0, 0, 0, 0, 0, + 21, 111, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 7, + 147, 240, 240, 145, 6, 0, 0, 255, 255, 255, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 21, 0, 0, 0, 0, 0, 76, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 123, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 135, 244, 135, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 21, 111, 0, 0, 0, 0, 0, 0, 6, 143, + 238, 243, 157, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 50, 238, 255, 184, 8, 0, 0, 253, 255, + 255, 6, 0, 0, 22, 205, 255, 255, 255, 255, 255, 198, 9, 0, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, + 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 59, + 233, 255, 255, 255, 0, 0, 255, 255, 255, 231, 57, 0, 0, 0, + 0, 0, 0, 21, 210, 255, 136, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 147, 255, 255, 255, 255, 144, 0, 0, 255, 255, 255, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 138, 255, 212, 23, 0, 0, 0, 142, 255, 140, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 145, 250, + 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 243, 0, + 0, 0, 0, 0, 0, 0, 0, 21, 210, 255, 136, 0, 0, 0, + 0, 0, 145, 255, 255, 255, 255, 153, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 50, 238, 255, 255, 220, 25, + 0, 0, 223, 255, 255, 120, 12, 80, 227, 255, 255, 255, 255, 255, + 255, 255, 131, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 34, 242, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, + 240, 33, 0, 0, 0, 0, 0, 121, 255, 255, 255, 137, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 240, 255, 255, 255, 255, 239, 0, + 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 140, 255, 255, 255, 119, 0, 0, + 80, 255, 255, 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 146, 255, 255, 202, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 135, 243, 135, 0, 0, 0, 0, 0, 0, 0, 0, 121, 255, 255, + 255, 137, 0, 0, 0, 0, 239, 255, 255, 255, 255, 240, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 238, 255, + 255, 220, 28, 0, 0, 0, 132, 255, 255, 255, 255, 255, 255, 255, + 230, 82, 12, 120, 255, 255, 221, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 163, 255, 255, 249, 117, 24, 0, 0, + 24, 118, 250, 255, 255, 162, 0, 0, 0, 0, 0, 1, 150, 255, + 255, 255, 138, 0, 0, 0, 255, 255, 255, 0, 0, 240, 255, 255, + 255, 255, 239, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 142, 255, 255, 255, + 144, 0, 0, 0, 0, 145, 255, 255, 255, 144, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 25, 246, 255, 255, 78, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 150, 255, 255, 255, 138, 0, 0, 0, 239, 255, 255, 255, + 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 49, 238, 255, 255, 221, 29, 0, 0, 0, 0, 9, 198, 255, 255, + 255, 255, 255, 210, 25, 0, 0, 6, 255, 255, 252, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 234, 255, 255, 116, + 0, 0, 0, 0, 0, 0, 119, 255, 255, 233, 0, 0, 0, 0, + 0, 0, 1, 150, 255, 255, 255, 139, 0, 0, 255, 255, 255, 0, + 0, 145, 255, 255, 255, 255, 142, 0, 0, 255, 255, 255, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, + 255, 255, 255, 146, 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, + 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 255, 255, + 207, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 150, 255, 255, 255, 139, 0, 0, + 143, 255, 255, 255, 255, 151, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 32, 224, 255, 222, 30, 0, 0, 0, 0, 0, + 0, 9, 131, 221, 250, 226, 144, 13, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 255, 255, 255, 23, 0, 0, 0, 0, 0, 0, 24, 255, 255, 254, + 0, 0, 0, 0, 0, 0, 0, 1, 150, 255, 255, 255, 0, 0, + 255, 255, 255, 0, 0, 6, 144, 239, 239, 142, 5, 0, 0, 255, + 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 147, 255, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, + 0, 149, 255, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 21, 244, 255, 255, 83, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 150, 255, + 255, 255, 0, 0, 5, 140, 236, 242, 155, 9, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 191, 31, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, + 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 1, 149, 255, 255, 255, 149, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 150, 255, 255, 255, 149, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 136, 255, 255, 211, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 7, 147, 240, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 1, 150, 255, 255, 255, 151, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 151, 255, 255, + 255, 150, 1, 0, 0, 0, 0, 0, 0, 0, 19, 242, 255, 255, + 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 145, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 147, 240, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 147, 255, 255, 255, 255, 255, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 1, 152, 255, 255, + 255, 152, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 153, 255, 255, 255, 152, 1, 0, 0, 0, 0, 0, 0, + 0, 131, 255, 255, 216, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255, 255, + 147, 0, 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 7, 147, 240, 240, 145, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 154, 255, 255, 255, 154, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 155, 255, 255, 255, 154, 1, 0, + 0, 0, 0, 0, 0, 16, 239, 255, 255, 93, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 145, 255, 255, 255, 149, 1, 0, 0, 0, 0, 0, 240, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 240, 255, + 255, 255, 255, 239, 0, 0, 0, 0, 0, 0, 0, 147, 255, 255, + 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 156, 255, 255, 255, 156, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 157, 255, + 255, 255, 156, 1, 0, 0, 0, 0, 0, 0, 126, 255, 255, 219, + 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, 150, 1, 0, 0, + 0, 0, 240, 255, 255, 255, 255, 239, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 145, 255, 255, 255, 255, 142, 0, 0, 0, 0, 0, 0, + 0, 240, 255, 255, 255, 255, 239, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 95, 255, 255, 255, 219, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 220, 255, 255, 255, 95, 0, 0, 0, 0, 0, 0, + 14, 237, 255, 255, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, + 255, 152, 1, 0, 0, 0, 145, 255, 255, 255, 255, 142, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 25, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 24, 0, 0, 0, 0, 0, 6, 144, 239, 239, 142, 5, 0, 0, + 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 239, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 156, 255, 255, + 255, 156, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 157, 255, 255, 255, 155, 1, 0, 0, + 0, 0, 0, 0, 0, 121, 255, 255, 223, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 149, 255, 255, 255, 113, 0, 0, 0, 6, 144, 239, 239, + 142, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 121, 255, 255, 248, 0, 0, 0, 0, 0, 0, 0, + 0, 248, 255, 255, 118, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 145, 255, 255, 255, 255, + 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 155, 255, 255, 255, 154, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 155, 255, 255, 255, 154, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 11, 234, 255, 255, 104, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 151, 255, 255, 205, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 25, 121, 250, 255, 255, 157, 0, 0, 0, + 0, 0, 0, 0, 0, 158, 255, 255, 250, 120, 25, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, + 144, 239, 239, 142, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 154, 255, 255, 255, 153, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 154, 255, + 255, 255, 153, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 116, 255, 255, 227, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 255, 255, + 247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 255, 248, 135, + 5, 0, 0, 0, 0, 0, 0, 0, 0, 5, 135, 248, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 152, 255, + 255, 255, 151, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 152, 255, 255, 255, 152, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 9, 231, 255, 255, 109, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 244, 255, 255, 15, 0, 0, 0, 0, 0, 0, + 0, 16, 255, 255, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, + 255, 255, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 51, 255, 255, 255, 0, 0, 0, 7, 147, 240, 240, 145, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 151, 255, 255, 255, 150, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 151, 255, 255, 255, 150, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 230, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 202, 255, 255, 110, 0, 0, + 0, 0, 0, 0, 0, 113, 255, 255, 202, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 255, 255, 255, 248, 136, 5, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 136, 248, 255, 255, 255, 0, 0, 0, 147, 255, + 255, 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 150, 255, 255, 255, 148, 0, + 0, 0, 0, 0, 0, 0, 0, 149, 255, 255, 255, 149, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, + 228, 255, 255, 114, 0, 0, 0, 0, 0, 0, 0, 0, 110, 255, + 255, 249, 111, 16, 0, 0, 0, 16, 113, 250, 255, 255, 110, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 24, 118, 250, 255, 255, 157, 0, + 0, 0, 0, 0, 0, 0, 0, 158, 255, 255, 249, 117, 24, 0, + 0, 0, 240, 255, 255, 255, 255, 239, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, + 255, 255, 255, 146, 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, + 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 105, 255, 255, 233, 11, 0, 0, 0, 0, 0, + 0, 0, 6, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 204, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 120, + 255, 255, 248, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 255, + 117, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 239, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 147, 255, 255, 255, 144, 0, 0, 0, 0, 146, + 255, 255, 255, 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 224, 255, 255, 119, 0, + 0, 0, 0, 0, 0, 0, 0, 24, 203, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 203, 24, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 25, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 24, 0, 0, 0, 0, 0, 145, 255, 255, 255, + 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 255, 255, 255, 80, + 0, 0, 119, 255, 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, + 255, 255, 236, 13, 0, 0, 0, 0, 0, 0, 0, 0, 6, 109, + 201, 243, 255, 255, 255, 243, 201, 109, 6, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 6, 144, 239, 239, 142, 5, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 144, 255, 143, 0, 0, 0, 24, 215, 255, 144, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4, 221, 255, 255, 125, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 24, 119, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 239, 16, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, + 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 217, + 255, 255, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 90, 255, 255, 241, 18, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2, 213, 255, 255, 135, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 255, + 255, 244, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 24, 0, 0, 0, 0, 0, 0, + 25, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 209, 255, 255, 140, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 234, 255, 255, 118, 0, 0, + 0, 0, 0, 0, 121, 255, 255, 233, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 80, 255, 255, 222, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 255, + 255, 250, 120, 25, 0, 0, 25, 121, 250, 255, 255, 162, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 184, 117, + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 35, 241, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, + 240, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 59, 232, 255, 255, 255, 0, 0, + 255, 255, 255, 231, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 130, + 209, 245, 0, 0, 245, 209, 129, 19, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +} /* namespace profont_36 */ diff --git a/libpdraw-vsink/CMakeLists.txt b/libpdraw-vsink/CMakeLists.txt new file mode 100644 index 0000000..d300057 --- /dev/null +++ b/libpdraw-vsink/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 3.15) +project(pdraw-vsink VERSION 1.0) + +add_library(${PROJECT_NAME} SHARED src/pdraw_vsink.c) + +# checks if set up rpath exists for install +if(COMMAND set_up_rpath) + set_up_rpath() +else() + message("Set up rpath not defined!") +endif() + +option(BUILD_SHARED_LIBS "Build using shared libraries" ON) +set_target_properties(${PROJECT_NAME} PROPERTIES + POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS} +) + +target_include_directories(${PROJECT_NAME} PUBLIC + $ + $ +) + +target_compile_definitions(${PROJECT_NAME} PRIVATE "PDRAW_VSINK_API_EXPORTS") +target_compile_options(${PROJECT_NAME} PRIVATE "-fvisibility=hidden") +target_compile_options(${PROJECT_NAME} PRIVATE "-std=gnu99") + +target_link_libraries(${PROJECT_NAME} + + PRIVATE + futils + media-buffers-memory + pomp + ulog + pdraw + media-buffers + media-buffers-memory-generic + # PUBLIC +) + +set(${PROJECT_NAME}-headers + ${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/ +) + +install(TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}-targets + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) + +# to get all header files correctly +install( + DIRECTORY ${${PROJECT_NAME}-headers} DESTINATION include/${PROJECT_NAME} +) + diff --git a/libpdraw-vsink/atom.mk b/libpdraw-vsink/atom.mk index e08d25f..f33bd00 100644 --- a/libpdraw-vsink/atom.mk +++ b/libpdraw-vsink/atom.mk @@ -1,38 +1,38 @@ - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := libpdraw-vsink -LOCAL_CATEGORY_PATH := libs -LOCAL_DESCRIPTION := Parrot Drones Awesome Video Viewer video sink wrapper library -LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include -LOCAL_CFLAGS := -DPDRAW_VSINK_API_EXPORTS -fvisibility=hidden -std=gnu99 -LOCAL_SRC_FILES := \ - src/pdraw_vsink.c -LOCAL_LIBRARIES := \ - libfutils \ - libmedia-buffers \ - libmedia-buffers-memory \ - libmedia-buffers-memory-generic \ - libpdraw \ - libpomp \ - libulog - -include $(BUILD_LIBRARY) - - -include $(CLEAR_VARS) - -LOCAL_MODULE := pdraw-vsink-test -LOCAL_DESCRIPTION := Parrot Drones Awesome Video Viewer video sink wrapper library test program -LOCAL_CATEGORY_PATH := multimedia -LOCAL_SRC_FILES := tests/pdraw_vsink_test.c -LOCAL_LIBRARIES := \ - libmedia-buffers \ - libpdraw-vsink \ - libulog \ - libvideo-defs \ - libvideo-metadata - -include $(BUILD_EXECUTABLE) + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libpdraw-vsink +LOCAL_CATEGORY_PATH := libs +LOCAL_DESCRIPTION := Parrot Drones Awesome Video Viewer video sink wrapper library +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_CFLAGS := -DPDRAW_VSINK_API_EXPORTS -fvisibility=hidden -std=gnu99 +LOCAL_SRC_FILES := \ + src/pdraw_vsink.c +LOCAL_LIBRARIES := \ + libfutils \ + libmedia-buffers \ + libmedia-buffers-memory \ + libmedia-buffers-memory-generic \ + libpdraw \ + libpomp \ + libulog + +include $(BUILD_LIBRARY) + + +include $(CLEAR_VARS) + +LOCAL_MODULE := pdraw-vsink-test +LOCAL_DESCRIPTION := Parrot Drones Awesome Video Viewer video sink wrapper library test program +LOCAL_CATEGORY_PATH := multimedia +LOCAL_SRC_FILES := tests/pdraw_vsink_test.c +LOCAL_LIBRARIES := \ + libmedia-buffers \ + libpdraw-vsink \ + libulog \ + libvideo-defs \ + libvideo-metadata + +include $(BUILD_EXECUTABLE) diff --git a/libpdraw-vsink/include/pdraw-vsink/pdraw_vsink.h b/libpdraw-vsink/include/pdraw-vsink/pdraw_vsink.h index e567c7b..58c8660 100644 --- a/libpdraw-vsink/include/pdraw-vsink/pdraw_vsink.h +++ b/libpdraw-vsink/include/pdraw-vsink/pdraw_vsink.h @@ -1,103 +1,103 @@ -/** - * Parrot Drones Awesome Video Viewer - * Video sink wrapper library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_VSINK_H_ -#define _PDRAW_VSINK_H_ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* To be used for all public API */ -#ifdef PDRAW_VSINK_API_EXPORTS -# ifdef _WIN32 -# define PDRAW_VSINK_API __declspec(dllexport) -# else /* !_WIN32 */ -# define PDRAW_VSINK_API __attribute__((visibility("default"))) -# endif /* !_WIN32 */ -#else /* !PDRAW_VSINK_API_EXPORTS */ -# define PDRAW_VSINK_API -#endif /* !PDRAW_VSINK_API_EXPORTS */ - - -/* Forward declarations */ -struct pdraw_vsink; - - -/** - * Create a pdraw_vsink instance and connect to a URL. - * The instance handle is returned through the ret_obj parameter. - * When no longer needed, the instance must be freed using the - * pdraw_vsink_stop() function. - * @param url: URL to open (network URL or local file) - * @param media_info: media info pointer to fill if not NULL. - * Note: mdeia_info will be freed in pdraw_vsink_stop(). - * @param ret_obj: pdraw_vsink instance handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_VSINK_API int pdraw_vsink_start(const char *url, - struct pdraw_media_info **media_info, - struct pdraw_vsink **ret_obj); - - -/** - * Stop and destroy a pdraw_vsink instance. - * @param self: pdraw_vsink instance handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_VSINK_API int pdraw_vsink_stop(struct pdraw_vsink *self); - - -/** - * Get a frame. - * The caller can pass a mbuf_mem object to hold the frame. If not given, this - * call will allocate a memory internally. - * @param self: pdraw_vsink instance handle - * @param frame_memory: memory used by the frame (optional) - * @param frame_info: frame information (output) - * @param ret_frame: frame (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_VSINK_API int -pdraw_vsink_get_frame(struct pdraw_vsink *self, - struct mbuf_mem *frame_memory, - struct pdraw_video_frame *frame_info, - struct mbuf_raw_video_frame **ret_frame); -/* TODO: Add timeout !!! */ - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* !_LIBPDRAW_VSINK_H_ */ +/** + * Parrot Drones Awesome Video Viewer + * Video sink wrapper library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_VSINK_H_ +#define _PDRAW_VSINK_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* To be used for all public API */ +#ifdef PDRAW_VSINK_API_EXPORTS +# ifdef _WIN32 +# define PDRAW_VSINK_API __declspec(dllexport) +# else /* !_WIN32 */ +# define PDRAW_VSINK_API __attribute__((visibility("default"))) +# endif /* !_WIN32 */ +#else /* !PDRAW_VSINK_API_EXPORTS */ +# define PDRAW_VSINK_API +#endif /* !PDRAW_VSINK_API_EXPORTS */ + + +/* Forward declarations */ +struct pdraw_vsink; + + +/** + * Create a pdraw_vsink instance and connect to a URL. + * The instance handle is returned through the ret_obj parameter. + * When no longer needed, the instance must be freed using the + * pdraw_vsink_stop() function. + * @param url: URL to open (network URL or local file) + * @param media_info: media info pointer to fill if not NULL. + * Note: mdeia_info will be freed in pdraw_vsink_stop(). + * @param ret_obj: pdraw_vsink instance handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_VSINK_API int pdraw_vsink_start(const char *url, + struct pdraw_media_info **media_info, + struct pdraw_vsink **ret_obj); + + +/** + * Stop and destroy a pdraw_vsink instance. + * @param self: pdraw_vsink instance handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_VSINK_API int pdraw_vsink_stop(struct pdraw_vsink *self); + + +/** + * Get a frame. + * The caller can pass a mbuf_mem object to hold the frame. If not given, this + * call will allocate a memory internally. + * @param self: pdraw_vsink instance handle + * @param frame_memory: memory used by the frame (optional) + * @param frame_info: frame information (output) + * @param ret_frame: frame (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_VSINK_API int +pdraw_vsink_get_frame(struct pdraw_vsink *self, + struct mbuf_mem *frame_memory, + struct pdraw_video_frame *frame_info, + struct mbuf_raw_video_frame **ret_frame); +/* TODO: Add timeout !!! */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* !_LIBPDRAW_VSINK_H_ */ diff --git a/libpdraw-vsink/tests/pdraw_vsink_test.c b/libpdraw-vsink/tests/pdraw_vsink_test.c index 8ec2678..2306001 100644 --- a/libpdraw-vsink/tests/pdraw_vsink_test.c +++ b/libpdraw-vsink/tests/pdraw_vsink_test.c @@ -1,142 +1,142 @@ -/** - * Parrot Drones Awesome Video Viewer - * Video sink wrapper library test program - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include - -#define ULOG_TAG pdraw_vsink_test -#include -ULOG_DECLARE_TAG(pdraw_vsink_test); - -#include -#include -#include -#include -#include - - -int main(int argc, char **argv) -{ - int status = EXIT_SUCCESS, res, i; - struct pdraw_vsink *vsink = NULL; - struct pdraw_media_info *media_info = NULL; - - if (argc < 2) { - ULOGE("usage: %s ", argv[0]); - exit(EXIT_FAILURE); - } - - res = pdraw_vsink_start(argv[1], &media_info, &vsink); - if (res < 0 || media_info == NULL) { - ULOG_ERRNO("pdraw_vsink_start", -res); - exit(EXIT_FAILURE); - } - ULOGI("started"); - - ULOGI("media_info: name=%s, path=%s", - media_info->name, - media_info->path); - ULOGI("media_info: duration=%.3fs, res=%ux%u, framerate=%u/%u", - media_info->duration / 1000000.0, - media_info->video.raw.info.resolution.width, - media_info->video.raw.info.resolution.height, - media_info->video.raw.info.framerate.num, - media_info->video.raw.info.framerate.den); - - for (i = 0; i < 20; i++) { - struct pdraw_video_frame frame_info = {0}; - struct mbuf_raw_video_frame *frame = NULL; - struct vmeta_frame *frame_meta = NULL; - unsigned int plane_count; - - /* Get a new frame */ - res = pdraw_vsink_get_frame(vsink, NULL, &frame_info, &frame); - if (res < 0) { - ULOG_ERRNO("pdraw_vsink_get_frame", -res); - continue; - } - - /* Get frame information */ - ULOGI("frame #%d (width=%d height=%d)", - i + 1, - frame_info.raw.info.resolution.width, - frame_info.raw.info.resolution.height); - - /* Get the video metadata */ - res = mbuf_raw_video_frame_get_metadata(frame, &frame_meta); - if (res < 0 && res != -ENOENT) - ULOG_ERRNO("mbuf_raw_video_frame_get_metadata", -res); - if (frame_meta != NULL) { - uint8_t battery_percentage; - vmeta_frame_get_battery_percentage(frame_meta, - &battery_percentage); - ULOGI("metadata: battery_percentage=%d%%", - battery_percentage); - } - - /* Get the frame planes */ - plane_count = - vdef_get_raw_frame_plane_count(&frame_info.raw.format); - for (unsigned int k = 0; k < plane_count; k++) { - const void *plane = NULL; - size_t plane_len; - res = mbuf_raw_video_frame_get_plane( - frame, k, &plane, &plane_len); - if (res < 0) { - ULOG_ERRNO("mbuf_raw_video_frame_get_plane(%u)", - -res, - k); - continue; - } - ULOGI("plane[%d]: addr=%p stride=%zu", - k, - plane, - frame_info.raw.plane_stride[k]); - res = mbuf_raw_video_frame_release_plane( - frame, k, plane); - if (res < 0) - ULOG_ERRNO( - "mbuf_raw_video_frame_release_plane(%u)", - -res, - k); - } - - vmeta_frame_unref(frame_meta); - mbuf_raw_video_frame_unref(frame); - } - - res = pdraw_vsink_stop(vsink); - if (res < 0) - ULOG_ERRNO("pdraw_vsink_stop", -res); - /* media_info won't be valid after pdraw_vsink_stop() */ - - ULOGI("%s", (status == EXIT_SUCCESS) ? "success!" : "failed!"); - exit(status); -} +/** + * Parrot Drones Awesome Video Viewer + * Video sink wrapper library test program + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#define ULOG_TAG pdraw_vsink_test +#include +ULOG_DECLARE_TAG(pdraw_vsink_test); + +#include +#include +#include +#include +#include + + +int main(int argc, char **argv) +{ + int status = EXIT_SUCCESS, res, i; + struct pdraw_vsink *vsink = NULL; + struct pdraw_media_info *media_info = NULL; + + if (argc < 2) { + ULOGE("usage: %s ", argv[0]); + exit(EXIT_FAILURE); + } + + res = pdraw_vsink_start(argv[1], &media_info, &vsink); + if (res < 0 || media_info == NULL) { + ULOG_ERRNO("pdraw_vsink_start", -res); + exit(EXIT_FAILURE); + } + ULOGI("started"); + + ULOGI("media_info: name=%s, path=%s", + media_info->name, + media_info->path); + ULOGI("media_info: duration=%.3fs, res=%ux%u, framerate=%u/%u", + media_info->duration / 1000000.0, + media_info->video.raw.info.resolution.width, + media_info->video.raw.info.resolution.height, + media_info->video.raw.info.framerate.num, + media_info->video.raw.info.framerate.den); + + for (i = 0; i < 20; i++) { + struct pdraw_video_frame frame_info = {0}; + struct mbuf_raw_video_frame *frame = NULL; + struct vmeta_frame *frame_meta = NULL; + unsigned int plane_count; + + /* Get a new frame */ + res = pdraw_vsink_get_frame(vsink, NULL, &frame_info, &frame); + if (res < 0) { + ULOG_ERRNO("pdraw_vsink_get_frame", -res); + continue; + } + + /* Get frame information */ + ULOGI("frame #%d (width=%d height=%d)", + i + 1, + frame_info.raw.info.resolution.width, + frame_info.raw.info.resolution.height); + + /* Get the video metadata */ + res = mbuf_raw_video_frame_get_metadata(frame, &frame_meta); + if (res < 0 && res != -ENOENT) + ULOG_ERRNO("mbuf_raw_video_frame_get_metadata", -res); + if (frame_meta != NULL) { + uint8_t battery_percentage; + vmeta_frame_get_battery_percentage(frame_meta, + &battery_percentage); + ULOGI("metadata: battery_percentage=%d%%", + battery_percentage); + } + + /* Get the frame planes */ + plane_count = + vdef_get_raw_frame_plane_count(&frame_info.raw.format); + for (unsigned int k = 0; k < plane_count; k++) { + const void *plane = NULL; + size_t plane_len; + res = mbuf_raw_video_frame_get_plane( + frame, k, &plane, &plane_len); + if (res < 0) { + ULOG_ERRNO("mbuf_raw_video_frame_get_plane(%u)", + -res, + k); + continue; + } + ULOGI("plane[%d]: addr=%p stride=%zu", + k, + plane, + frame_info.raw.plane_stride[k]); + res = mbuf_raw_video_frame_release_plane( + frame, k, plane); + if (res < 0) + ULOG_ERRNO( + "mbuf_raw_video_frame_release_plane(%u)", + -res, + k); + } + + vmeta_frame_unref(frame_meta); + mbuf_raw_video_frame_unref(frame); + } + + res = pdraw_vsink_stop(vsink); + if (res < 0) + ULOG_ERRNO("pdraw_vsink_stop", -res); + /* media_info won't be valid after pdraw_vsink_stop() */ + + ULOGI("%s", (status == EXIT_SUCCESS) ? "success!" : "failed!"); + exit(status); +} diff --git a/libpdraw/CMakeLists.txt b/libpdraw/CMakeLists.txt new file mode 100644 index 0000000..21dcbf9 --- /dev/null +++ b/libpdraw/CMakeLists.txt @@ -0,0 +1,126 @@ +cmake_minimum_required(VERSION 3.15) +project(pdraw VERSION 1.0) + + + +set(LIB_SOURCES + src/pdraw_channel_coded_video.cpp + src/pdraw_channel_raw_video.cpp + src/pdraw_decoder_video.cpp + src/pdraw_demuxer.cpp + src/pdraw_demuxer_record.cpp + src/pdraw_demuxer_stream_mux.cpp + src/pdraw_demuxer_stream_net.cpp + src/pdraw_demuxer_stream.cpp + src/pdraw_element.cpp + src/pdraw_encoder_video.cpp + src/pdraw_external_coded_video_sink.cpp + src/pdraw_external_raw_video_sink.cpp + src/pdraw_gles2_hmd_colors.cpp + src/pdraw_gles2_hmd_indices.cpp + src/pdraw_gles2_hmd_positions_cockpitglasses.cpp + src/pdraw_gles2_hmd_positions_cockpitglasses2.cpp + src/pdraw_gles2_hmd_shaders.cpp + src/pdraw_gles2_hmd_texcoords_cockpitglasses_blue.cpp + src/pdraw_gles2_hmd_texcoords_cockpitglasses_red.cpp + src/pdraw_gles2_hmd_texcoords.cpp + src/pdraw_gles2_hmd.cpp + src/pdraw_gles2_video.cpp + src/pdraw_media.cpp + src/pdraw_muxer_record.cpp + src/pdraw_muxer_stream_rtmp.cpp + src/pdraw_muxer.cpp + src/pdraw_renderer_gles2.cpp + src/pdraw_renderer_videocoreegl.cpp + src/pdraw_renderer.cpp + src/pdraw_scaler_video.cpp + src/pdraw_session.cpp + src/pdraw_settings.cpp + src/pdraw_sink_coded_video.cpp + src/pdraw_sink_raw_video.cpp + src/pdraw_source_coded_video.cpp + src/pdraw_source_raw_video.cpp + src/pdraw_utils.cpp + src/pdraw_video_pres_stats.cpp + src/pdraw_wrapper.cpp +) + +add_library(${PROJECT_NAME} SHARED ${LIB_SOURCES}) +set_target_properties(${PROJECT_NAME} PROPERTIES + POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS} +) + +# checks if set up rpath exists for install +if(COMMAND set_up_rpath) + set_up_rpath() +else() + message("Set up rpath not defined!") +endif() + +target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_11) + +option(BUILD_SHARED_LIBS "Build using shared libraries" ON) + +target_include_directories(${PROJECT_NAME} PUBLIC + $ + $ +) + +target_compile_definitions(${PROJECT_NAME} PRIVATE "PDRAW_API_EXPORTS") +target_compile_definitions(${PROJECT_NAME} PRIVATE "_USE_MATH_DEFINES") +target_compile_definitions(${PROJECT_NAME} PRIVATE "_GNU_SOURCE") +target_compile_options(${PROJECT_NAME} PRIVATE "-fvisibility=hidden") +target_compile_options(${PROJECT_NAME} PRIVATE "-std=c++11") + +find_package(Eigen3 CONFIG REQUIRED) + +target_link_libraries(${PROJECT_NAME} + + PRIVATE + Eigen3::Eigen + futils + h264 + h265 + + media-buffers + media-buffers-memory + media-buffers-memory-generic + + mp4 + pomp + rtp + rtsp + sdp + transport-packet + transport-socket + ulog + + video-decode + video-decode-core + video-defs + + video-encode + video-encode-core + + video-metadata + video-scale + video-scale-core + video-streaming + +) + +set(${PROJECT_NAME}-headers + ${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/ +) + +install(TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}-targets + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) + +# to get all header files correctly +install( + DIRECTORY ${${PROJECT_NAME}-headers} DESTINATION include/${PROJECT_NAME} +) diff --git a/libpdraw/atom.mk b/libpdraw/atom.mk index 95d827c..532e95b 100644 --- a/libpdraw/atom.mk +++ b/libpdraw/atom.mk @@ -1,115 +1,115 @@ - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := libpdraw -LOCAL_DESCRIPTION := Parrot Drones Awesome Video Viewer library -LOCAL_CATEGORY_PATH := libs - -LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include -# Public API headers - top level headers first -# This header list is currently used to generate a python binding -LOCAL_EXPORT_CUSTOM_VARIABLES := LIBPDRAW_HEADERS=$\ - $(LOCAL_PATH)/include/pdraw/pdraw.h:$\ - $(LOCAL_PATH)/include/pdraw/pdraw_defs.h; -LOCAL_EXPORT_CXXFLAGS := -std=c++11 -LOCAL_CFLAGS := -DPDRAW_API_EXPORTS -fvisibility=hidden -D_USE_MATH_DEFINES -D_GNU_SOURCE - -LOCAL_SRC_FILES := \ - src/pdraw_channel_coded_video.cpp \ - src/pdraw_channel_raw_video.cpp \ - src/pdraw_decoder_video.cpp \ - src/pdraw_demuxer.cpp \ - src/pdraw_demuxer_record.cpp \ - src/pdraw_demuxer_stream_mux.cpp \ - src/pdraw_demuxer_stream_net.cpp \ - src/pdraw_demuxer_stream.cpp \ - src/pdraw_element.cpp \ - src/pdraw_encoder_video.cpp \ - src/pdraw_external_coded_video_sink.cpp \ - src/pdraw_external_raw_video_sink.cpp \ - src/pdraw_gles2_hmd_colors.cpp \ - src/pdraw_gles2_hmd_indices.cpp \ - src/pdraw_gles2_hmd_positions_cockpitglasses.cpp \ - src/pdraw_gles2_hmd_positions_cockpitglasses2.cpp \ - src/pdraw_gles2_hmd_shaders.cpp \ - src/pdraw_gles2_hmd_texcoords_cockpitglasses_blue.cpp \ - src/pdraw_gles2_hmd_texcoords_cockpitglasses_red.cpp \ - src/pdraw_gles2_hmd_texcoords.cpp \ - src/pdraw_gles2_hmd.cpp \ - src/pdraw_gles2_video.cpp \ - src/pdraw_media.cpp \ - src/pdraw_muxer_record.cpp \ - src/pdraw_muxer_stream_rtmp.cpp \ - src/pdraw_muxer.cpp \ - src/pdraw_renderer_gles2.cpp \ - src/pdraw_renderer_videocoreegl.cpp \ - src/pdraw_renderer.cpp \ - src/pdraw_scaler_video.cpp \ - src/pdraw_session.cpp \ - src/pdraw_settings.cpp \ - src/pdraw_sink_coded_video.cpp \ - src/pdraw_sink_raw_video.cpp \ - src/pdraw_source_coded_video.cpp \ - src/pdraw_source_raw_video.cpp \ - src/pdraw_utils.cpp \ - src/pdraw_video_pres_stats.cpp \ - src/pdraw_wrapper.cpp - -LOCAL_LIBRARIES := \ - eigen \ - libfutils \ - libh264 \ - libh265 \ - libmedia-buffers \ - libmedia-buffers-memory \ - libmedia-buffers-memory-generic \ - libmp4 \ - libpomp \ - librtp \ - librtsp \ - libsdp \ - libtransport-packet \ - libtransport-socket \ - libulog \ - libvideo-decode \ - libvideo-defs \ - libvideo-encode \ - libvideo-metadata \ - libvideo-scale \ - libvideo-streaming -LOCAL_CONDITIONAL_LIBRARIES := \ - OPTIONAL:json \ - OPTIONAL:libmux \ - OPTIONAL:librtmp - -ifeq ($(TARGET_CPU),$(filter %$(TARGET_CPU),s905d3 s905x3)) - LOCAL_CFLAGS += -DUSE_GLES2 - LOCAL_LDLIBS += -lGLESv2 - LOCAL_LIBRARIES += \ - am-gpu -else ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","linux-native") - LOCAL_CFLAGS += -DUSE_GLES2 -DUSE_GLFW3 - LOCAL_LIBRARIES += \ - glfw3 \ - opengl -else ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","linux-android") - LOCAL_CFLAGS += -DUSE_GLES2 - LOCAL_LDLIBS += -lEGL -lGLESv2 -landroid -else ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","darwin-native") - LOCAL_CFLAGS += -DUSE_GLES2 -DUSE_GLFW3 - LOCAL_LDLIBS += \ - -framework OpenGL - LOCAL_LIBRARIES += \ - glfw3 -else ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","darwin-iphoneos") - LOCAL_CFLAGS += -DUSE_GLES2 - LOCAL_LDLIBS += \ - -framework OpenGLES -else ifeq ("$(TARGET_OS)","windows") - LOCAL_CFLAGS += -DUSE_GLES2 -D_WIN32_WINNT=0x0600 -DEPOXY_SHARED - LOCAL_LDLIBS += -lws2_32 -lepoxy -endif - -include $(BUILD_LIBRARY) + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libpdraw +LOCAL_DESCRIPTION := Parrot Drones Awesome Video Viewer library +LOCAL_CATEGORY_PATH := libs + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include +# Public API headers - top level headers first +# This header list is currently used to generate a python binding +LOCAL_EXPORT_CUSTOM_VARIABLES := LIBPDRAW_HEADERS=$\ + $(LOCAL_PATH)/include/pdraw/pdraw.h:$\ + $(LOCAL_PATH)/include/pdraw/pdraw_defs.h; +LOCAL_EXPORT_CXXFLAGS := -std=c++11 +LOCAL_CFLAGS := -DPDRAW_API_EXPORTS -fvisibility=hidden -D_USE_MATH_DEFINES -D_GNU_SOURCE + +LOCAL_SRC_FILES := \ + src/pdraw_channel_coded_video.cpp \ + src/pdraw_channel_raw_video.cpp \ + src/pdraw_decoder_video.cpp \ + src/pdraw_demuxer.cpp \ + src/pdraw_demuxer_record.cpp \ + src/pdraw_demuxer_stream_mux.cpp \ + src/pdraw_demuxer_stream_net.cpp \ + src/pdraw_demuxer_stream.cpp \ + src/pdraw_element.cpp \ + src/pdraw_encoder_video.cpp \ + src/pdraw_external_coded_video_sink.cpp \ + src/pdraw_external_raw_video_sink.cpp \ + src/pdraw_gles2_hmd_colors.cpp \ + src/pdraw_gles2_hmd_indices.cpp \ + src/pdraw_gles2_hmd_positions_cockpitglasses.cpp \ + src/pdraw_gles2_hmd_positions_cockpitglasses2.cpp \ + src/pdraw_gles2_hmd_shaders.cpp \ + src/pdraw_gles2_hmd_texcoords_cockpitglasses_blue.cpp \ + src/pdraw_gles2_hmd_texcoords_cockpitglasses_red.cpp \ + src/pdraw_gles2_hmd_texcoords.cpp \ + src/pdraw_gles2_hmd.cpp \ + src/pdraw_gles2_video.cpp \ + src/pdraw_media.cpp \ + src/pdraw_muxer_record.cpp \ + src/pdraw_muxer_stream_rtmp.cpp \ + src/pdraw_muxer.cpp \ + src/pdraw_renderer_gles2.cpp \ + src/pdraw_renderer_videocoreegl.cpp \ + src/pdraw_renderer.cpp \ + src/pdraw_scaler_video.cpp \ + src/pdraw_session.cpp \ + src/pdraw_settings.cpp \ + src/pdraw_sink_coded_video.cpp \ + src/pdraw_sink_raw_video.cpp \ + src/pdraw_source_coded_video.cpp \ + src/pdraw_source_raw_video.cpp \ + src/pdraw_utils.cpp \ + src/pdraw_video_pres_stats.cpp \ + src/pdraw_wrapper.cpp + +LOCAL_LIBRARIES := \ + eigen \ + libfutils \ + libh264 \ + libh265 \ + libmedia-buffers \ + libmedia-buffers-memory \ + libmedia-buffers-memory-generic \ + libmp4 \ + libpomp \ + librtp \ + librtsp \ + libsdp \ + libtransport-packet \ + libtransport-socket \ + libulog \ + libvideo-decode \ + libvideo-defs \ + libvideo-encode \ + libvideo-metadata \ + libvideo-scale \ + libvideo-streaming +LOCAL_CONDITIONAL_LIBRARIES := \ + OPTIONAL:json \ + OPTIONAL:libmux \ + OPTIONAL:librtmp + +ifeq ($(TARGET_CPU),$(filter %$(TARGET_CPU),s905d3 s905x3)) + LOCAL_CFLAGS += -DUSE_GLES2 + LOCAL_LDLIBS += -lGLESv2 + LOCAL_LIBRARIES += \ + am-gpu +else ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","linux-native") + LOCAL_CFLAGS += -DUSE_GLES2 -DUSE_GLFW3 + LOCAL_LIBRARIES += \ + glfw3 \ + opengl +else ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","linux-android") + LOCAL_CFLAGS += -DUSE_GLES2 + LOCAL_LDLIBS += -lEGL -lGLESv2 -landroid +else ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","darwin-native") + LOCAL_CFLAGS += -DUSE_GLES2 -DUSE_GLFW3 + LOCAL_LDLIBS += \ + -framework OpenGL + LOCAL_LIBRARIES += \ + glfw3 +else ifeq ("$(TARGET_OS)-$(TARGET_OS_FLAVOUR)","darwin-iphoneos") + LOCAL_CFLAGS += -DUSE_GLES2 + LOCAL_LDLIBS += \ + -framework OpenGLES +else ifeq ("$(TARGET_OS)","windows") + LOCAL_CFLAGS += -DUSE_GLES2 -D_WIN32_WINNT=0x0600 -DEPOXY_SHARED + LOCAL_LDLIBS += -lws2_32 -lepoxy +endif + +include $(BUILD_LIBRARY) diff --git a/libpdraw/include/pdraw/pdraw.h b/libpdraw/include/pdraw/pdraw.h index f58a3bb..a17ab59 100644 --- a/libpdraw/include/pdraw/pdraw.h +++ b/libpdraw/include/pdraw/pdraw.h @@ -1,1702 +1,1702 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_H_ -#define _PDRAW_H_ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#include - -#include "pdraw_defs.h" - - -/* Forward declarations */ -struct pdraw; -struct pdraw_demuxer; -struct pdraw_muxer; -struct pdraw_video_renderer; -struct pdraw_coded_video_sink; -struct pdraw_raw_video_sink; - - -/* General callback functions */ -struct pdraw_cbs { - /* Stop response callback function, called when a stop operation is - * complete or has failed (optional, can be null, but highly recommended - * for correct PDrAW session management). - * The status parameter is the stop operation status: 0 on success, - * or a negative errno value in case of error. - * @param pdraw: PDrAW instance handle - * @param status: 0 on success, negative errno value in case of error - * @param userdata: user data pointer */ - void (*stop_resp)(struct pdraw *pdraw, int status, void *userdata); - - /* Media added callback function, called when a media has been added - * internally in the PDrAW pipeline. Medias are for example raw or coded - * video medias. The info structure gives the media identifier that can - * be used for example to create a video sink on this media. - * @param pdraw: PDrAW instance handle - * @param info: pointer on the media information - * @param userdata: user data pointer */ - void (*media_added)(struct pdraw *pdraw, - const struct pdraw_media_info *info, - void *userdata); - - /* Media removed callback function, called when a media has been removed - * internally from the PDrAW pipeline. Medias are for example raw or - * coded video medias. When a media is removed, any video sink created - * on this media must then be stopped. - * @param pdraw: PDrAW instance handle - * @param info: pointer on the media information - * @param userdata: user data pointer */ - void (*media_removed)(struct pdraw *pdraw, - const struct pdraw_media_info *info, - void *userdata); - - /* Socket creation callback function, called immediately after a - * socket creation with its file descriptor as parameter (optional, - * can be null). - * @param pdraw: PDrAW instance handle - * @param fd: socket file descriptor - * @param userdata: user data pointer */ - void (*socket_created)(struct pdraw *pdraw, int fd, void *userdata); -}; - - -/* Demuxer callback functions */ -struct pdraw_demuxer_cbs { - /* Open response callback function, called when an open operation is - * complete or has failed (optional, can be null, but highly recommended - * for correct PDrAW session management). - * The status parameter is the open operation status: 0 on success, - * or a negative errno value in case of error. - * If this function reports an error, the demuxer still needs to be - * closed: pdraw_demuxer_close() must be called and one must wait for - * the close_resp callback function to be issued prior to calling - * pdraw_demuxer_destroy(). - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param status: 0 on success, negative errno value in case of error - * @param userdata: user data pointer */ - void (*open_resp)(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - int status, - void *userdata); - - /* Close response callback function, called when a close operation is - * complete or has failed (optional, can be null, but highly recommended - * for correct PDrAW session management). - * The status parameter is the close operation status: 0 on success, - * or a negative errno value in case of error. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param status: 0 on success, negative errno value in case of error - * @param userdata: user data pointer */ - void (*close_resp)(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - int status, - void *userdata); - - /* Unrecoverable error callback function, called when a previously - * opened session is no longer running. When this function is called, - * the demuxer session is no longer running; pdraw_demuxer_close() must - * be called and one must wait for the close_resp callback function to - * be issued prior to calling pdraw_demuxer_destroy(). - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param userdata: user data pointer */ - void (*unrecoverable_error)(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - void *userdata); - - /* Demuxer media selection callback function, called with a list of - * video medias found from which the application must choose one or more - * to process in the pipeline. The return value of the callback function - * must be a bitfield of the identifiers of the chosen medias (from the - * pdraw_demuxer_media structure), or 0 to choose the default medias. If - * the return value is -ENOSYS, the callback is considered not - * implemented and the default medias are chosen. If the return value is - * -ECANCELED no media is chosen and the open operation is aborted. If - * the return value is another negative errno or an invalid bitfield the - * open_resp callback function will be called if an open operation is in - * progress, or the unrecoverable_error callback function otherwise. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param medias: array of demuxer media - * @param count: demuxer media array element count - * @param userdata: user data pointer - * @return a bitfield of the identifiers of the chosen medias, - * 0 or -ENOSYS to choose the default medias, - * -ECANCELED to choose no media and abort the open operation, - * or another negative errno value in case of error */ - int (*select_media)(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - const struct pdraw_demuxer_media *medias, - size_t count, - void *userdata); - - /* Ready to play callback function, called when the playback is ready - * to start (optional, can be null). This function is called to indicate - * that the PDrAW session is ready to process play operations. - * Generally the session is ready to play as soon as the open_resp - * callback function has been called with a success status. One case - * when the open operation was successful and the playback is not ready - * is when connected to a SkyController's RTSP server but the - * SkyController itself is not yet connected to a drone. Similarly, - * when connected to a drone's stream through a SkyController's RTSP - * server, if the drone is disconnected from the SkyController, this - * function will be called with a 0 value in the ready parameter. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param ready: 1 if the session is ready to play, 0 otherwise - * @param userdata: user data pointer */ - void (*ready_to_play)(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - int ready, - void *userdata); - - /* End of range callback function, called when the playback is suspended - * after having reached the end of the playback duration (optional, - * can be null). This function is only called for replays (either local - * or streamed), not for live streams. The timestamp parameter is the - * current play time in microseconds at the moment the playback is - * suspended. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param timestamp: current playback time in microseconds - * @param userdata: user data pointer */ - void (*end_of_range)(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - uint64_t timestamp, - void *userdata); - - /* Play response callback function, called when a play operation is - * complete (the playback has started) or has failed (optional, can be - * null). The status parameter is the play operation status: 0 on - * success, or a negative errno value in case of error. The timestamp - * parameter is the current play time in microseconds at the moment the - * playback is started. The speed parameter is the current playback - * speed; a negative value means playing backward. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param status: 0 on success, negative errno value in case of error - * @param timestamp: current playback time in microseconds - * @param speed: current playback speed, negative means backward - * @param userdata: user data pointer */ - void (*play_resp)(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - int status, - uint64_t timestamp, - float speed, - void *userdata); - - /* Pause response callback function, called when a pause operation is - * complete (the playback is suspended) or has failed (optional, can be - * null). The status parameter is the pause operation status: 0 on - * success, or a negative errno value in case of error. The timestamp - * parameter is the current play time in microseconds at the moment the - * playback is paused. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param status: 0 on success, negative errno value in case of error - * @param timestamp: current playback time in microseconds - * @param userdata: user data pointer */ - void (*pause_resp)(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - int status, - uint64_t timestamp, - void *userdata); - - /* Seek response callback function, called when a seek operation is - * complete or has failed (optional, can be null). - * The status parameter is the seek operation status: 0 on success, - * or a negative errno value in case of error. The timestamp parameter - * is the current play time in microseconds after seeking. The speed - * parameter is the current playback speed; a negative value means - * playing backward. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param status: 0 on success, negative errno value in case of error - * @param timestamp: current playback time in microseconds - * @param speed: current playback speed, negative means backward - * @param userdata: user data pointer */ - void (*seek_resp)(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - int status, - uint64_t timestamp, - float speed, - void *userdata); -}; - - -/* Video renderer callback functions */ -struct pdraw_video_renderer_cbs { - /* Media added callback function, called when a media has been added - * internally to the renderer. Medias are raw video medias. - * This function is called from the pomp_loop thread. - * @param pdraw: PDrAW instance handle - * @param renderer: renderer handle - * @param info: pointer on the media information - * @param userdata: user data pointer */ - void (*media_added)(struct pdraw *pdraw, - struct pdraw_video_renderer *renderer, - const struct pdraw_media_info *info, - void *userdata); - - /* Media removed callback function, called when a media has been removed - * internally from the renderer. Medias are raw video medias. - * This function is called from the pomp_loop thread. - * @param pdraw: PDrAW instance handle - * @param renderer: renderer handle - * @param info: pointer on the media information - * @param userdata: user data pointer */ - void (*media_removed)(struct pdraw *pdraw, - struct pdraw_video_renderer *renderer, - const struct pdraw_media_info *info, - void *userdata); - - /* Render ready callback function, called both when a new frame is - * ready for rendering and periodically (optional, can be null). - * This function is called from the pomp_loop thread. - * @param pdraw: PDrAW instance handle - * @param renderer: renderer handle - * @param userdata: user data pointer */ - void (*render_ready)(struct pdraw *pdraw, - struct pdraw_video_renderer *renderer, - void *userdata); - - /* External texture loading callback function (optional, can be null). - * This function is called before the rendering of the video frame in - * order to override the frame loading as a texture. This can be used - * to transform the video frames outside of PDrAW before resuming the - * rendering. This function is called from the rendering thread. - * @param pdraw: PDrAW instance handle - * @param renderer: renderer handle - * @param texture_width: texture width in pixels - * @param texture_height: texture height in pixels - * @param media_info: media information - * @param frame: frame information - * @param frame_userdata: frame user data buffer - * @param frame_userdata_len: frame user data buffer size in bytes - * @param userdata: user data pointer - * @return 0 on success, negative errno value in case of error */ - int (*load_texture)(struct pdraw *pdraw, - struct pdraw_video_renderer *renderer, - unsigned int texture_width, - unsigned int texture_height, - const struct pdraw_media_info *media_info, - struct mbuf_raw_video_frame *frame, - const void *frame_userdata, - size_t frame_userdata_len, - void *userdata); - - /* Overlay rendering callback function (optional, can be null). - * This function is called after the rendering of the video frame - * (if one is available) in order to render an application overlay - * on top of the video. When HMD distorsion correction is enabled - * in the renderer, it is applied after the overlay rendering. - * When no frame is available for the rendering, the frame_meta - * and frame_extra parameters are NULL. This function is called - * from the rendering thread. - * @param pdraw: PDrAW instance handle - * @param renderer: renderer handle - * @param render_pos: rendering position - * @param content_pos: video content position - * @param view_mat: 4x4 view matrix - * @param proj_mat: 4x4 projection matrix - * @param media_info: media information - * @param frame_meta: frame metadata (optional, can be NULL) - * @param frame_extra: frame extra information (optional, can be NULL) - * @param userdata: user data pointer */ - void (*render_overlay)( - struct pdraw *pdraw, - struct pdraw_video_renderer *renderer, - const struct pdraw_rect *render_pos, - const struct pdraw_rect *content_pos, - const float *view_mat, - const float *proj_mat, - const struct pdraw_media_info *media_info, - struct vmeta_frame *frame_meta, - const struct pdraw_video_frame_extra *frame_extra, - void *userdata); -}; - - -/* Coded video sink callback functions */ -struct pdraw_coded_video_sink_cbs { - /* Coded video sink flush callback function, called when flushing is - * required (mandatory). When this function is called, the application - * must flush the sink queue by calling - * mbuf_coded_video_frame_queue_flush() and must return all frames - * outside of the queue by calling mbuf_coded_video_frame_unref(); once - * the flushing is done, the pdraw_coded_video_sink_queue_flushed() - * function must be called. - * @param pdraw: PDrAW instance handle - * @param sink: coded video sink handle - * @param userdata: user data pointer */ - void (*flush)(struct pdraw *pdraw, - struct pdraw_coded_video_sink *sink, - void *userdata); -}; - - -/* Raw video sink callback functions */ -struct pdraw_raw_video_sink_cbs { - /* Raw video sink flush callback function, called when flushing is - * required (mandatory). When this function is called, the application - * must flush the sink queue by calling - * mbuf_raw_video_frame_queue_flush() and must return all frames outside - * of the queue by calling mbuf_raw_video_frame_unref(); once the - * flushing is done, the pdraw_raw_video_sink_queue_flushed() function - * must be called. - * @param pdraw: PDrAW instance handle - * @param sink: raw video sink handle - * @param userdata: user data pointer */ - void (*flush)(struct pdraw *pdraw, - struct pdraw_raw_video_sink *sink, - void *userdata); -}; - - -/** - * Instance management API - */ - -/** - * Create a PDrAW instance. - * A running pomp_loop must be provided; all functions (with the exception of - * rendering functions) must be called from the pomp_loop thread; all callback - * functions (with the exception of rendering callbacks) are issued from the - * pomp_loop thread. The callbacks structure must be provided but all callback - * functions are optional. However, for correct management of the session it is - * highly recommended to implement at least the open_resp and close_resp - * functions. The instance handle is returned through the ret_obj parameter. - * When no longer needed, the instance must be freed using the pdraw_destroy() - * function. The pdraw_stop() function must be called and one must wait for the - * stop_resp callback function to be issued prior to calling pdraw_destroy(). - * @param loop: pomp_loop to use - * @param cbs: PDrAW callback functions - * @param userdata: callback functions user data (optional, can be null) - * @param ret_obj: PDrAW instance handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_new(struct pomp_loop *loop, - const struct pdraw_cbs *cbs, - void *userdata, - struct pdraw **ret_obj); - - -/** - * Free a PDrAW instance. - * This function frees all resources associated with a PDrAW instance. - * If a successful pdraw_open_*() was made, the pdraw_close() function must - * be called and one must wait for the close_resp callback function to be - * issued prior to calling pdraw_destroy(). - * @param pdraw: PDrAW instance handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_destroy(struct pdraw *pdraw); - - -/** - * Stop a PDrAW instance. - * This function stops a PDrAW instance and all the associated objects. - * The function returns before the actual stopping is done. If the function - * returns 0, the stop_resp callback function will be called once the stopping - * is successful (0 status) or has failed (negative errno status). If the - * function returns a negative errno value (immediate failure), the stop_resp - * callback function will not be called. After a successful stop, the PDrAW - * instance must be destroyed by calling pdraw_destroy(). - * @param pdraw: PDrAW instance handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_stop(struct pdraw *pdraw); - - -/** - * Demuxer API - */ - -/** - * Create a demuxer on a URL (stream or local file). - * The URL can be either an RTSP URL (starting with "rtsp://") or a local file - * path (either absolute or relative). - * The function returns before the actual opening is done. If the function - * returns 0, the open_resp callback function will be issued once the open - * operation is successful (0 status) or has failed (negative errno status). - * If the function returns a negative errno value (immediate failure), the - * open_resp callback function will not be issued. - * Once a demuxer is no longer used, it must be closed and then destroyed - * (@see the pdraw_demuxer_close() function). - * @param pdraw: PDrAW instance handle - * @param url: URL of the resource to open - * @param cbs: demuxer callback functions - * @param userdata: callback functions user data (optional, can be null) - * @param ret_obj: demuxer handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_demuxer_new_from_url(struct pdraw *pdraw, - const char *url, - const struct pdraw_demuxer_cbs *cbs, - void *userdata, - struct pdraw_demuxer **ret_obj); - - -/** - * Create a demuxer on a single stream. - * This function opens an RTP/AVP stream. No session management is done: it - * is the application's responsibility to handle the ports negociation with - * the sender. If null local ports are given as parameter, the effective port - * numbers used can be retrieved using the - * pdraw_get_single_stream_local_stream_port() for the RTP port and - * pdraw_get_single_stream_local_control_port() for the RTCP functions. - * If the local_addr parameter is left null, any local network interface will - * be used. The remote_addr, remote_stream_port and remote_control_port - * parameters can be left null if unknown; they will be known once the stream - * is being received. - * The function returns before the actual opening is done. If the function - * returns 0, the open_resp callback function will be issued once the open - * operation is successful (0 status) or has failed (negative errno status). - * If the function returns a negative errno value (immediate failure), the - * open_resp callback function will not be issued. - * Once a demuxer is no longer used, it must be closed and then destroyed - * (@see the pdraw_demuxer_close() function). - * @param pdraw: PDrAW instance handle - * @param local_addr: local IP address (optional, can be NULL) - * @param local_stream_port: local stream (RTP) port (optional, can be 0) - * @param local_control_port: local control (RTCP) port (optional, can be 0) - * @param remote_addr: remote IP address (optional, can be NULL) - * @param remote_stream_port: remote stream (RTP) port (optional, can be 0) - * @param remote_control_port: remote control (RTCP) port (optional, can be 0) - * @param cbs: demuxer callback functions - * @param userdata: callback functions user data (optional, can be null) - * @param ret_obj: demuxer handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_demuxer_new_single_stream(struct pdraw *pdraw, - const char *local_addr, - uint16_t local_stream_port, - uint16_t local_control_port, - const char *remote_addr, - uint16_t remote_stream_port, - uint16_t remote_control_port, - const struct pdraw_demuxer_cbs *cbs, - void *userdata, - struct pdraw_demuxer **ret_obj); - - -/** - * Create a demuxer on a stream URL through a mux channel. - * The URL must be an RTSP URL (starting with "rtsp://"). Mux channels are used - * to transfer data between a SkyController remote and a smartphone through USB; - * see Parrot's libmux for more information. - * No concurrent sessions can run on the mux channel; therefore the user must - * take care of limiting the number of PDrAW instances and demuxer objects - * running on the mux channel to only one. - * The function returns before the actual opening is done. If the function - * returns 0, the open_resp callback function will be issued once the open - * operation is successful (0 status) or has failed (negative errno status). - * If the function returns a negative errno value (immediate failure), the - * open_resp callback function will not be issued. - * Once a demuxer is no longer used, it must be closed and then destroyed - * (@see the pdraw_demuxer_close() function). - * @param pdraw: PDrAW instance handle - * @param url: URL of the resource to open - * @param mux: mux instance handle - * @param cbs: demuxer callback functions - * @param userdata: callback functions user data (optional, can be null) - * @param ret_obj: demuxer handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_demuxer_new_from_url_on_mux(struct pdraw *pdraw, - const char *url, - struct mux_ctx *mux, - const struct pdraw_demuxer_cbs *cbs, - void *userdata, - struct pdraw_demuxer **ret_obj); - - -/** - * Destroy a demuxer. - * This function stops a running demuxer and frees the associated resources. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_demuxer_destroy(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer); - - -/** - * Close a demuxer. - * This function closes a previously opened demuxer (either record or stream). - * The function returns before the actual closing is done. If the function - * returns 0, the close_resp callback function will be issued once the close is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the close_resp callback - * function will not be issued. After a successful close, the demuxer must be - * destroyed by calling pdraw_demuxer_destroy(). - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_demuxer_close(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer); - - -/** - * Get the single stream local stream port. - * This function returns the local stream (RTP) port currently in use after - * a succesful open operation on a single stream (RTP/AVP) or an RTSP URL. - * If no open operation has been done, or if an open operation has been done - * on a mux channel, 0 is returned. - * This is useful when calling pdraw_open_single_stream() with null local - * ports to let PDrAW open sockets on any available port. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @return the stream port on success, 0 in case of error - */ -PDRAW_API uint16_t pdraw_demuxer_get_single_stream_local_stream_port( - struct pdraw *pdraw, - struct pdraw_demuxer *demuxer); - - -/** - * Get the single stream local control port. - * This function returns the local control (RTCP) port currently in use after - * a succesful open operation on a single stream (RTP/AVP) or an RTSP URL. - * If no open operation has been done, or if an open operation has been done - * on a mux channel, 0 is returned. - * This is useful when calling pdraw_open_single_stream() with null local - * ports to let PDrAW open sockets on any available port. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @return the stream port on success, 0 in case of error - */ -PDRAW_API uint16_t pdraw_demuxer_get_single_stream_local_control_port( - struct pdraw *pdraw, - struct pdraw_demuxer *demuxer); - - -/** - * Get the ready to play status. - * This function returns 1 if a successful open operation has been completed - * and if the playback is ready to start, 0 otherwise. One case when a - * successful open operation is complete but the playback is not ready is when - * connected to a SkyController's RTSP server but the SkyController itself is - * not yet connected to a drone. - * The value returned by this function is identical to the ready parameter - * passed to the ready_to_play callback function when it is issued. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @return the ready to play status on success, 0 in case of error - */ -PDRAW_API int pdraw_demuxer_is_ready_to_play(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer); - - -/** - * Get the pause status. - * This function returns 1 if the playback is currently paused, 0 otherwise. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @return the pause status on success, 0 in case of error - */ -PDRAW_API int pdraw_demuxer_is_paused(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer); - - -/** - * Play at normal speed (x1.0). - * This function starts the playback of the video. - * The function returns before the actual operation is done. If the function - * returns 0, the play_resp callback function will be issued once the play is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the play_resp callback - * function will not be issued. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_demuxer_play(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer); - - -/** - * Play at the given speed. - * This function starts the playback of the video. If the speed parameter is - * negative, the video is played backward. If the speed is greater than or - * equal to PDRAW_PLAY_SPEED_MAX, the speed is ignored and the video is played - * at the maximum speed achievable. If the speed is less than or equal to - * -PDRAW_PLAY_SPEED_MAX, the speed is ignored and the video is played backward - * at the maximum speed achievable. A 0.0 speed has the same effet as calling - * the pdraw_pause() function. On a live stream, the speed parameter has - * no effect. - * The function returns before the actual operation is done. If the function - * returns 0, the play_resp callback function will be issued once the play is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the play_resp callback - * function will not be issued. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param speed: playback speed (0.0 means pause, negative value means - * play backward) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_demuxer_play_with_speed(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - float speed); - - -/** - * Pause the playback. - * This function suspends the playback of the video. The session is not closed - * and the playback can be resumed using the play functions. - * The function returns before the actual operation is done. If the function - * returns 0, the pause_resp callback function will be issued once the pause is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the pause_resp callback - * function will not be issued. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_demuxer_pause(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer); - - -/** - * Go to previous frame in frame-by-frame playback. - * This function plays the previous frame while the playback is paused. If the - * playback is not currently paused an error is returned. Frame-by-frame is - * only available on local replays (MP4 records). - * The function returns before the actual operation is done. If the function - * returns 0, the seek_resp callback function will be issued once the seek is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the seek_resp callback - * function will not be issued. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_demuxer_previous_frame(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer); - - -/** - * Go to next frame in frame-by-frame playback. - * This function plays the next frame while the playback is paused. If the - * playback is not currently paused an error is returned. Frame-by-frame is - * only available on local replays (MP4 records). - * The function returns before the actual operation is done. If the function - * returns 0, the seek_resp callback function will be issued once the seek is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the seek_resp callback - * function will not be issued. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_demuxer_next_frame(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer); - - -/** - * Seek forward or backward. - * This function seeks forward (positive delta) or backward (negative delta). - * The delta parameter is in microseconds. The exact parameter is a boolean - * value; when exact is 0 the seek is done to the nearest synchronization sample - * preceeding the delta, otherwise the seek is done to the sample nearest to the - * delta. Seeking is only available on replays (either local or streamed), - * not on live streams. - * The function returns before the actual operation is done. If the function - * returns 0, the seek_resp callback function will be issued once the seek is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the seek_resp callback - * function will not be issued. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param delta: time delta in microseconds (positive or negative) - * @param exact: 1 means seek to the sample closest to the delta, 0 means seek - * to the nearest synchronization sample preceeding the delta - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_demuxer_seek(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - int64_t delta, - int exact); - - -/** - * Seek forward. - * This function seeks forward by delta microseconds. It has the same - * behavior as calling pdraw_seek() with a positive delta. The exact parameter - * is a boolean value; when exact is 0 the seek is done to the nearest - * synchronization sample preceeding the delta, otherwise the seek is done to - * the sample nearest to the delta. Seeking is only available on replays - * (either local or streamed), not on live streams. - * The function returns before the actual operation is done. If the function - * returns 0, the seek_resp callback function will be issued once the seek is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the seek_resp callback - * function will not be issued. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param delta: positive time delta forward in microseconds - * @param exact: 1 means seek to the sample closest to the delta, 0 means seek - * to the nearest synchronization sample preceeding the delta - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_demuxer_seek_forward(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - uint64_t delta, - int exact); - - -/** - * Seek backward. - * This function seeks backward by delta microseconds (postive). It has the same - * behavior as calling pdraw_seek() with a negative delta. The exact parameter - * is a boolean value; when exact is 0 the seek is done to the nearest - * synchronization sample preceeding the delta, otherwise the seek is done to - * the sample nearest to the delta. Seeking is only available on replays - * (either local or streamed), not on live streams. - * The function returns before the actual operation is done. If the function - * returns 0, the seek_resp callback function will be issued once the seek is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the seek_resp callback - * function will not be issued. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param delta: positive time delta backward in microseconds - * @param exact: 1 means seek to the sample closest to the delta, 0 means seek - * to the nearest synchronization sample preceeding the delta - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_demuxer_seek_back(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - uint64_t delta, - int exact); - - -/** - * Seek to a given time. - * This function seeks to the given play timestamp in microseconds. The exact - * parameter is a boolean value; when exact is 0 the seek is done to the nearest - * synchronization sample preceeding the timestamp, otherwise the seek is done - * to the sample nearest to the timestamp. Seeking is only available on replays - * (either local or streamed), not on live streams. - * The function returns before the actual operation is done. If the function - * returns 0, the seek_resp callback function will be issued once the seek is - * successful (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the seek_resp callback - * function will not be issued. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param timestamp: play timestamp in microseconds - * @param exact: 1 means seek to the sample closest to the timestamp, - * 0 means seek to the nearest synchronization sample - * preceeding the timestamp - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_demuxer_seek_to(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - uint64_t timestamp, - int exact); - - -/** - * Get the playback duration. - * This function returns the playback duration in microseconds. The duration is - * only available on replays (either local or streamed), not on live streams. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @return the duration in microseconds on success, 0 in case of error - */ -PDRAW_API uint64_t pdraw_demuxer_get_duration(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer); - - -/** - * Get the playback current time. - * This function returns the current playback position in microseconds. - * On replays (either local or streamed) this is the position between 0 and - * the duration; on live streams this is the time since the start of the - * stream session. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @return the current time in microseconds on success, 0 in case of error - */ -PDRAW_API uint64_t -pdraw_demuxer_get_current_time(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer); - - -/** - * Muxer API - */ - -/** - * Create a muxer (experimental). - * This function creates a muxer with a given URL. - * Once the muxer is created medias can be added by id using the - * pdraw_muxer_add_media() function. Once a muxer is no longer used it must be - * destroyed by calling the pdraw_muxer_destroy() function. If writing to an - * MP4 file, the file is finalized in the pdraw_muxer_destroy() function. - * @note: experimental only, the function returns -ENOSYS - * @param pdraw: PDrAW instance handle - * @param url: destination URL - * @param ret_obj: muxer handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_muxer_new(struct pdraw *pdraw, - const char *url, - struct pdraw_muxer **ret_obj); - - -/** - * Destroy a muxer. - * This function stops a running muxer and frees the associated resources. - * @param pdraw: PDrAW instance handle - * @param muxer: muxer handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_muxer_destroy(struct pdraw *pdraw, - struct pdraw_muxer *muxer); - - -/** - * Add a media to a muxer. - * This function adds a media to the muxer by its media_id. The media - * idenfifiers are known when the media_added or media_removed general - * callback functions are called. - * The params structure is only relevant for video medias; the structure must - * then be provided but all parameters are optional and can be left null. - * @param pdraw: PDrAW instance handle - * @param muxer: muxer handle - * @param media_id: identifier of the media to add to the muxer - * @param params: muxer video media parameters - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_muxer_add_media(struct pdraw *pdraw, - struct pdraw_muxer *muxer, - unsigned int media_id, - const struct pdraw_muxer_video_media_params *params); - - -/** - * Video renderer API - * @warning: all functions must be called from the application's - * rendering thread - */ - -/** - * Create a video renderer. - * This function creates a video renderer on a media of the given media id; - * if the media id is zero the first raw media encountered is used. - * Once the renderer is created, the rendering is done by calling the - * pdraw_video_renderer_render*() functions. Once a renderer is no longer used - * it must be destroyed by calling the pdraw_video_renderer_destroy() function. - * The render_pos parameter sets the position and size of the rendering in the - * window/view; these coordinates are in pixels from the bottom-left corner - * (OpenGL coordinates). The params structure must be provided but all - * parameters are optional and can be left null. - * The callbacks structure must be provided but all callback functions are - * optional; all callback functions are called from the rendering thread, - * except the render_ready function which is called from the pomp_loop thread. - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW instance handle - * @param media_id: identifier of the raw media to render (from a - * pdraw_media_info structure); if zero the first - * raw media found is used for rendering - * @param render_pos: rendering position and size - * @param params: renderer parameters - * @param cbs: renderer callback functions - * @param userdata: callback functions user data (optional, can be null) - * @param ret_obj: renderer handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_video_renderer_new(struct pdraw *pdraw, - unsigned int media_id, - const struct pdraw_rect *render_pos, - const struct pdraw_video_renderer_params *params, - const struct pdraw_video_renderer_cbs *cbs, - void *userdata, - struct pdraw_video_renderer **ret_obj); - - -/** - * Create a video renderer on an EGL display. - * This function creates a video renderer on an active EGL display context on a - * media of the given media id; if the media id is zero the first raw - * media encountered is used. Once the renderer is created, the rendering is - * done by calling the pdraw_video_renderer_render*() functions. Once a renderer - * is no longer used it must be destroyed by calling the - * pdraw_video_renderer_destroy() function. The render_pos parameter sets the - * position and size of the rendering in the window/view; these coordinates are - * in pixels from the bottom-left corner (OpenGL coordinates). The params - * structure must be provided but all parameters are optional and can be left - * null. The callbacks structure must be provided but all callback functions are - * optional; all callback functions are called from the rendering thread, - * except the render_ready function which is called from the pomp_loop thread. - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW instance handle - * @param media_id: identifier of the raw media to render (from a - * pdraw_media_info structure); if zero the first - * raw media found is used for rendering - * @param render_pos: rendering position and size - * @param params: renderer parameters - * @param cbs: renderer callback functions - * @param userdata: callback functions user data (optional, can be null) - * @param egl_display: EGL display context - * @param ret_obj: renderer handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_video_renderer_new_egl(struct pdraw *pdraw, - unsigned int media_id, - const struct pdraw_rect *render_pos, - const struct pdraw_video_renderer_params *params, - const struct pdraw_video_renderer_cbs *cbs, - void *userdata, - struct egl_display *egl_display, - struct pdraw_video_renderer **ret_obj); - - -/** - * Destroy a video renderer. - * This function stops a running video renderer and frees the associated - * resources. - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW instance handle - * @param renderer: renderer handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_video_renderer_destroy(struct pdraw *pdraw, - struct pdraw_video_renderer *renderer); - - -/** - * Resize a video renderer. - * This function updates the rendering position on a running video renderer. - * The render_pos parameter sets the position and size of the rendering in the - * window/view; these coordinates are in pixels from the bottom-left corner - * (OpenGL coordinates). - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW instance handle - * @param renderer: renderer handle - * @param render_pos: rendering position and size - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_video_renderer_resize(struct pdraw *pdraw, - struct pdraw_video_renderer *renderer, - const struct pdraw_rect *render_pos); - - -/** - * Set the video renderer media identifier. - * This function updates the identifier of the media on which the rendering is - * done; if the media id is zero the first raw media encountered is used. - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW instance handle - * @param renderer: renderer handle - * @param media_id: identifier of the raw media to render (from a - * pdraw_media_info structure); if zero the first raw media - * found is used for rendering - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_video_renderer_set_media_id(struct pdraw *pdraw, - struct pdraw_video_renderer *renderer, - unsigned int media_id); - - -/** - * Get the video renderer media identifier. - * This function retrieves the identifier of the media on which the rendering - * is done. - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW instance handle - * @param renderer: renderer handle - * @return the identifier of the media on success, 0 if no media is being - * renderered or in case of error - */ -PDRAW_API unsigned int -pdraw_video_renderer_get_media_id(struct pdraw *pdraw, - struct pdraw_video_renderer *renderer); - - -/** - * Set the video renderer parameters. - * This function updates the rendering parameters on a running video renderer. - * The params structure must be provided but all parameters are optional and - * can be left null. - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW instance handle - * @param renderer: renderer handle - * @param params: renderer parameters - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_video_renderer_set_params( - struct pdraw *pdraw, - struct pdraw_video_renderer *renderer, - const struct pdraw_video_renderer_params *params); - - -/** - * Get the video renderer parameters. - * This function retrieves the rendering parameters on a running video renderer. - * The provided params structure is filled by the function. - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW instance handle - * @param renderer: renderer handle - * @param params: renderer parameters (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_video_renderer_get_params(struct pdraw *pdraw, - struct pdraw_video_renderer *renderer, - struct pdraw_video_renderer_params *params); - - -/** - * Render the video. - * This function renders the video with the current rendering position and - * rendering parameters. - * For render-on-demand, consider using the render_ready callback function - * which is called both when a new frame is ready for rendering and - * periodically. Note that the render_ready callback function is called from - * the pomp_loop thread, not the rendering thread; the synchronization is up - * to the caller. - * If a content_pos structure is provided, it is filled with the actual position - * and size of the video within the rendering position; these coordinates are - * in pixels from the bottom-left corner (OpenGL coordinates). - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW instance handle - * @param renderer: renderer handle - * @param content_pos: video content position (output; optional, can be null) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_video_renderer_render(struct pdraw *pdraw, - struct pdraw_video_renderer *renderer, - struct pdraw_rect *content_pos); - - -/** - * Render the video with provided matrices. - * This function renders the video with the current rendering position and - * rendering parameters, and the provided view and projection matrices. - * The view_mat and proj_mat matrices are 4x4 OpenGL matrices. - * For render-on-demand, consider using the render_ready callback function - * which is called both when a new frame is ready for rendering and - * periodically. Note that the render_ready callback function is called from - * the pomp_loop thread, not the rendering thread; the synchronization is up - * to the caller. - * If a content_pos structure is provided, it is filled with the actual position - * and size of the video within the rendering position; these coordinates are - * in pixels from the bottom-left corner (OpenGL coordinates). - * @warning: this function must be called from the application's rendering - * thread. - * @param pdraw: PDrAW instance handle - * @param renderer: renderer handle - * @param content_pos: video content position (output; optional, can be null) - * @param view_mat: 4x4 view matrix - * @param proj_mat: 4x4 projection matrix - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_video_renderer_render_mat(struct pdraw *pdraw, - struct pdraw_video_renderer *renderer, - struct pdraw_rect *content_pos, - const float *view_mat, - const float *proj_mat); - - -/** - * Video sink API - */ - -/** - * Create a coded video sink. - * This function creates a coded video sink on a media of the given media_id. - * The media idenfifiers are known when the media_added or media_removed - * general callback functions are called. - * Once the sink is created, video frames are retrieved by getting them - * from the queue returned by the pdraw_coded_video_sink_get_queue() function. - * Once a video sink is no longer used, it must be destroyed by calling the - * pdraw_coded_video_sink_destroy() function. - * The params structure must be provided but all parameters are optional and - * can be left null. - * The callbacks structure must be provided and the flush callback function is - * required to be implemented; all callback functions are called from the - * pomp_loop thread. When the flush callback function is called, the - * application must flush the sink queue by calling - * mbuf_coded_video_frame_queue_flush() and must return all frames outside of - * the queue by calling mbuf_coded_video_frame_unref(); once the flushing is - * complete, the pdraw_coded_video_sink_queue_flushed() function must be called. - * - * @note media_id must refer to a coded video media. - * - * @param pdraw: PDrAW instance handle - * @param media_id: identifier of the media on which to create the sink - * @param params: video sink parameters - * @param cbs: video sink callback functions - * @param userdata: callback functions user data (optional, can be null) - * @param ret_obj: video sink handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_coded_video_sink_new(struct pdraw *pdraw, - unsigned int media_id, - const struct pdraw_video_sink_params *params, - const struct pdraw_coded_video_sink_cbs *cbs, - void *userdata, - struct pdraw_coded_video_sink **ret_obj); - - -/** - * Destroy a coded video sink. - * This function stops a running video sink and frees the associated resources. - * A video sink must not be destroyed unless all frames outside of the queue - * have been returned by calling mbuf_coded_video_frame_unref(). Once a video - * sink is destroyed the queue returned by pdraw_coded_video_sink_get_queue() - * must no longer be used. - * @param pdraw: PDrAW instance handle - * @param sink: video sink handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_coded_video_sink_destroy(struct pdraw *pdraw, - struct pdraw_coded_video_sink *sink); - - -/** - * Resynchronize a coded video sink. - * This function schedules the output of a synchronization frame (IDR) for a - * running video sink. It can be used for example in case of unrecoverable video - * decoder errors to restart decoding. After a video sink creation, the first - * frame that is output is always a synchronization frame; therefore it is not - * necessary to call this function immediately after a video sink creation. - * @param pdraw: PDrAW instance handle - * @param sink: video sink handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_coded_video_sink_resync(struct pdraw *pdraw, - struct pdraw_coded_video_sink *sink); - - -/** - * Get the coded video sink frame queue. - * This function returns the frame queue to use in order to retrieve frames - * from a running video sink. Frames are retrieved from the queue by using the - * mbuf_coded_video_frame_queue_pop() function. - * @param pdraw: PDrAW instance handle - * @param sink: video sink handle - * @return a pointer on a mbuf_coded_video_frame_queue object on success, NULL - * in case of error - */ -PDRAW_API struct mbuf_coded_video_frame_queue * -pdraw_coded_video_sink_get_queue(struct pdraw *pdraw, - struct pdraw_coded_video_sink *sink); - - -/** - * Signal that a coded video sink has been flushed. - * This function is used to signal that flushing is complete. When the flush - * video sink callback function is called, the application must flush the sink - * queue by calling mbuf_coded_video_frame_queue_flush() and must return all - * frames outside of the queue by calling mbuf_coded_video_frame_unref(); once - * the flushing is complete, this function must be called. - * @param pdraw: PDrAW instance handle - * @param sink: video sink handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_coded_video_sink_queue_flushed(struct pdraw *pdraw, - struct pdraw_coded_video_sink *sink); - - -/** - * Create a raw video sink. - * This function creates a raw video sink on a media of the given media_id. - * The media idenfifiers are known when the media_added or media_removed - * general callback functions are called. - * Once the sink is created, video frames are retrieved by getting them - * from the queue returned by the pdraw_raw_video_sink_get_queue() function. - * Once a video sink is no longer used, it must be destroyed by calling the - * pdraw_raw_video_sink_destroy() function. - * The params structure must be provided but all parameters are optional and - * can be left null. - * The callbacks structure must be provided and the flush callback function is - * required to be implemented; all callback functions are called from the - * pomp_loop thread. When the flush callback function is called, the - * application must flush the sink queue by calling - * mbuf_raw_video_frame_queue_flush() and must return all frames outside of - * the queue by calling mbuf_raw_video_frame_unref(); once the flushing is - * complete, the pdraw_raw_video_sink_queue_flushed() function must be called. - * - * @note media_id must refer to a raw video media. - * - * @param pdraw: PDrAW instance handle - * @param media_id: identifier of the media on which to create the sink - * @param params: video sink parameters - * @param cbs: video sink callback functions - * @param userdata: callback functions user data (optional, can be null) - * @param ret_obj: video sink handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_raw_video_sink_new(struct pdraw *pdraw, - unsigned int media_id, - const struct pdraw_video_sink_params *params, - const struct pdraw_raw_video_sink_cbs *cbs, - void *userdata, - struct pdraw_raw_video_sink **ret_obj); - - -/** - * Destroy a raw video sink. - * This function stops a running video sink and frees the associated resources. - * A video sink must not be destroyed unless all frames outside of the queue - * have been returned by calling mbuf_raw_video_frame_unref(). Once a video - * sink is destroyed the queue returned by pdraw_raw_video_sink_get_queue() must - * no longer be used. - * @param pdraw: PDrAW instance handle - * @param sink: video sink handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_raw_video_sink_destroy(struct pdraw *pdraw, - struct pdraw_raw_video_sink *sink); - - -/** - * Get the raw video sink frame queue. - * This function returns the frame queue to use in order to retrieve frames - * from a running video sink. Frames are retrieved from the queue by using the - * mbuf_raw_video_frame_queue_pop() function. - * @param pdraw: PDrAW instance handle - * @param sink: video sink handle - * @return a pointer on a mbuf_raw_video_frame_queue object on success, NULL - * in case of error - */ -PDRAW_API struct mbuf_raw_video_frame_queue * -pdraw_raw_video_sink_get_queue(struct pdraw *pdraw, - struct pdraw_raw_video_sink *sink); - - -/** - * Signal that a raw video sink has been flushed. - * This function is used to signal that flushing is complete. When the flush - * video sink callback function is called, the application must flush the sink - * queue by calling mbuf_raw_video_frame_queue_flush() and must return all - * frames outside of the queue by calling mbuf_raw_video_frame_unref(); once - * the flushing is complete, this function must be called. - * @param pdraw: PDrAW instance handle - * @param sink: video sink handle - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_raw_video_sink_queue_flushed(struct pdraw *pdraw, - struct pdraw_raw_video_sink *sink); - - -/** - * Settings API - */ - -/** - * Get the PDrAW instance friendly name. - * This function fills the str array with the null-terminated friendly name. - * The string must have been previously allocated. The function writes up to - * len characters. The friendly name is generally either the device's friendly - * name (e.g. "Bob's phone") or the application's name (e.g. - * "MyDroneControllerApp"). It is used for example in metadata exchanged with a - * streaming server. - * @param pdraw: PDrAW instance handle - * @param str: pointer to the string to write to (output) - * @param len: maximum length of the string - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_get_friendly_name_setting(struct pdraw *pdraw, char *str, size_t len); - - -/** - * Set the PDrAW instance friendly name. - * The friendly_name string is copied internally. The friendly name is generally - * either the device's friendly name (e.g. "Bob's phone") or the application's - * name (e.g. "MyDroneControllerApp"). It is used for example in metadata - * exchanged with a streaming server. Setting the friendly name value is - * optional. - * @param pdraw: PDrAW instance handle - * @param friendly_name: pointer to the friendly name string - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_set_friendly_name_setting(struct pdraw *pdraw, - const char *friendly_name); - - -/** - * Get the PDrAW instance serial number. - * This function fills the str array with the null-terminated serial number. - * The string must have been previously allocated. The function writes up to - * len characters. The serial number is generally the unique serial number of - * the device on which PDrAW is running. It is used for example in metadata - * exchanged with a streaming server. - * @param pdraw: PDrAW instance handle - * @param str: pointer to the string to write to (output) - * @param len: maximum length of the string - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_get_serial_number_setting(struct pdraw *pdraw, char *str, size_t len); - - -/** - * Set the PDrAW instance serial number. - * The serial_number string is copied internally. The serial number is generally - * the unique serial number of the device on which PDrAW is running. It is used - * for example in metadata exchanged with a streaming server. Setting the serial - * number value is recommended as it is used as a unique identifier in the - * streaming protocols. - * @param pdraw: PDrAW instance handle - * @param serial_number: pointer to the serial number string - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_set_serial_number_setting(struct pdraw *pdraw, - const char *serial_number); - - -/** - * Get the PDrAW instance software version. - * This function fills the str array with the null-terminated software version. - * The string must have been previously allocated. The function writes up to - * len characters. The software version is generally the version number of the - * application running PDrAW (e.g. "MyApp v1.2.3"). It is used for example in - * metadata exchanged with a streaming server. - * @param pdraw: PDrAW instance handle - * @param str: pointer to the string to write to (output) - * @param len: maximum length of the string - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_get_software_version_setting(struct pdraw *pdraw, char *str, size_t len); - - -/** - * Set the PDrAW instance software version. - * The software_version string is copied internally. The software version is - * generally the version number of the application running PDrAW (e.g. - * "MyApp v1.2.3"). It is used for example in metadata exchanged with a - * streaming server. Setting the software version value is optional. - * @param pdraw: PDrAW instance handle - * @param software_version: pointer to the software version string - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_set_software_version_setting(struct pdraw *pdraw, - const char *software_version); - - -/** - * Get the pipeline mode setting. - * This function returns the pipeline mode of a PDrAW instance. The pipeline - * mode controls whether to decode the selected video media (for full processing - * up to the rendering), or to disable video decoding (e.g. when no rendering - * is required, only a coded video sink). - * @param pdraw: PDrAW instance handle - * @return the pipeline mode, or PDRAW_PIPELINE_MODE_DECODE_ALL in case of error - */ -PDRAW_API enum pdraw_pipeline_mode -pdraw_get_pipeline_mode_setting(struct pdraw *pdraw); - - -/** - * Set the pipeline mode setting. - * This function sets the pipeline mode of a PDrAW instance. This function can - * be called only prior to any open operation. The pipeline mode controls - * whether to decode the selected video media (for full processing up to the - * rendering), or to disable video decoding (e.g. when no rendering is required, - * only an coded video sink). - * @param pdraw: PDrAW instance handle - * @param mode: pipeline mode - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_set_pipeline_mode_setting(struct pdraw *pdraw, - enum pdraw_pipeline_mode mode); - - -/** - * Get the display screen settings. - * This function returns the display screen settings through the xdpi, ydpi and - * device_margin_* parameters. This is only useful if HMD distortion correction - * is enabled in the video rendering. The xdpi and ydpi are pixel densities in - * dots per inches. The device margins are in millimeters. - * @param pdraw: PDrAW instance handle - * @param xdpi: horizontal pixel density (output) - * @param ydpi: vertical pixel density (output) - * @param device_margin_top: top device margin (output) - * @param device_margin_bottom: bottom device margin (output) - * @param device_margin_left: left device margin (output) - * @param device_margin_right: right device margin (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_get_display_screen_settings(struct pdraw *pdraw, - float *xdpi, - float *ydpi, - float *device_margin_top, - float *device_margin_bottom, - float *device_margin_left, - float *device_margin_right); - - -/** - * Set the display screen settings. - * This function sets the display screen settings. This is only useful if HMD - * distortion correction is enabled in the video rendering. The xdpi and ydpi - * are pixel densities in dots per inches. The device margins are in - * millimeters. - * @param pdraw: PDrAW instance handle - * @param xdpi: horizontal pixel density - * @param ydpi: vertical pixel density - * @param device_margin_top: top device margin - * @param device_margin_bottom: bottom device margin - * @param device_margin_left: left device margin - * @param device_margin_right: right device margin - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_set_display_screen_settings(struct pdraw *pdraw, - float xdpi, - float ydpi, - float device_margin_top, - float device_margin_bottom, - float device_margin_left, - float device_margin_right); - - -/** - * Get the HMD model setting. - * This function returns the head-mounted display (HMD) model. This is only - * useful if HMD distortion correction is enabled in the video rendering. - * @param pdraw: PDrAW instance handle - * @return the HMD model, or PDRAW_HMD_MODEL_UNKNOWN in case of error - */ -PDRAW_API enum pdraw_hmd_model pdraw_get_hmd_model_setting(struct pdraw *pdraw); - - -/** - * Set the HMD model setting. - * This function sets the head-mounted display (HMD) model. This is only - * useful if HMD distortion correction is enabled in the video rendering. - * @param pdraw: PDrAW instance handle - * @param hmd_model: HMD model - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_set_hmd_model_setting(struct pdraw *pdraw, - enum pdraw_hmd_model hmd_model); - -/** - * Platform-specific API - */ - -/** - * Set the Android JVM pointer. - * This function sets the JVM pointer for internal calls to the Android SDK API. - * This is only useful on Android and is ignored on other platforms. If the - * JVM pointer is not provided on Android platforms, some features may not be - * available. - * @param pdraw: PDrAW instance handle - * @param jvm: JVM pointer - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_set_android_jvm(struct pdraw *pdraw, void *jvm); - - -/** - * Debug API - */ - -/** - * Dump the current pipeline as a directed graph using the DOT file format. - * @param pdraw: PDrAW instance handle - * @param file_name: DOT file to write to - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_dump_pipeline(struct pdraw *pdraw, const char *file_name); - - -/** - * Helpers - */ - -/** - * ToString function for enum pdraw_hmd_model. - * @param val: HMD model value to convert - * @return a string description of the HMD model - */ -PDRAW_API const char *pdraw_hmd_model_str(enum pdraw_hmd_model val); - - -/** - * ToString function for enum pdraw_pipeline_mode. - * @param val: pipeline mode value to convert - * @return a string description of the pipeline mode - */ -PDRAW_API const char *pdraw_pipeline_mode_str(enum pdraw_pipeline_mode val); - - -/** - * ToString function for enum pdraw_playback_type. - * @param val: playback type value to convert - * @return a string description of the playback type - */ -PDRAW_API const char *pdraw_playback_type_str(enum pdraw_playback_type val); - - -/** - * ToString function for enum pdraw_media_type. - * @param val: media type value to convert - * @return a string description of the media type - */ -PDRAW_API const char *pdraw_media_type_str(enum pdraw_media_type val); - - -/** - * ToString function for enum pdraw_video_type. - * @param val: video type value to convert - * @return a string description of the video type - */ -PDRAW_API const char *pdraw_video_type_str(enum pdraw_video_type val); - - -/** - * ToString function for enum pdraw_histogram_channel. - * @param val: histogram channel value to convert - * @return a string description of the histogram channel - */ -PDRAW_API const char * -pdraw_histogram_channel_str(enum pdraw_histogram_channel val); - - -/** - * ToString function for enum pdraw_video_renderer_scheduling_mode. - * @param val: video renderer scheduling mode value to convert - * @return a string description of the video renderer scheduling mode - */ -PDRAW_API const char *pdraw_video_renderer_scheduling_mode_str( - enum pdraw_video_renderer_scheduling_mode val); - - -/** - * ToString function for enum pdraw_video_renderer_fill_mode. - * @param val: video renderer fill mode value to convert - * @return a string description of the video renderer fill mode - */ -PDRAW_API const char * -pdraw_video_renderer_fill_mode_str(enum pdraw_video_renderer_fill_mode val); - - -/** - * ToString function for enum pdraw_video_renderer_transition_flag. - * @param val: video renderer transition flag value to convert - * @return a string description of the video renderer transition flag - */ -PDRAW_API const char *pdraw_video_renderer_transition_flag_str( - enum pdraw_video_renderer_transition_flag val); - - -/** - * Convert a video frame metadata structure to a JSON object. - * The JSON object must have been previously created. The function appends - * new elements in this object. - * @param frame: pointer to a video frame structure - * @param metadata: optional pointer to the frame metadata - * @param jobj: pointer to a JSON object to fill (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdraw_video_frame_to_json(const struct pdraw_video_frame *frame, - struct vmeta_frame *metadata, - struct json_object *jobj); - - -/** - * Convert a video frame metadata structure to a JSON string. - * This function fills the str array with the null-terminated JSON string. - * The string must have been previously allocated. The function writes - * up to len characters. - * @param frame: pointer to a video frame structure - * @param metadata: optional pointer to the frame metadata - * @param str: pointer to the string to write to (output) - * @param len: maximum length of the string - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int -pdraw_video_frame_to_json_str(const struct pdraw_video_frame *frame, - struct vmeta_frame *metadata, - char *str, - unsigned int len); - -/** - * Duplicate a media_info structure. - * @param src: pointer to the media_info structure to duplicate - * @return a pointer to the newly allocated structure or NULL on error. - */ -PDRAW_API struct pdraw_media_info * -pdraw_media_info_dup(const struct pdraw_media_info *src); - - -/** - * Free a media_info structure. - * @param media_info: pointer to the media_info structure to free - */ -PDRAW_API void pdraw_media_info_free(struct pdraw_media_info *media_info); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* !_PDRAW_H_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_H_ +#define _PDRAW_H_ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include + +#include "pdraw_defs.h" + + +/* Forward declarations */ +struct pdraw; +struct pdraw_demuxer; +struct pdraw_muxer; +struct pdraw_video_renderer; +struct pdraw_coded_video_sink; +struct pdraw_raw_video_sink; + + +/* General callback functions */ +struct pdraw_cbs { + /* Stop response callback function, called when a stop operation is + * complete or has failed (optional, can be null, but highly recommended + * for correct PDrAW session management). + * The status parameter is the stop operation status: 0 on success, + * or a negative errno value in case of error. + * @param pdraw: PDrAW instance handle + * @param status: 0 on success, negative errno value in case of error + * @param userdata: user data pointer */ + void (*stop_resp)(struct pdraw *pdraw, int status, void *userdata); + + /* Media added callback function, called when a media has been added + * internally in the PDrAW pipeline. Medias are for example raw or coded + * video medias. The info structure gives the media identifier that can + * be used for example to create a video sink on this media. + * @param pdraw: PDrAW instance handle + * @param info: pointer on the media information + * @param userdata: user data pointer */ + void (*media_added)(struct pdraw *pdraw, + const struct pdraw_media_info *info, + void *userdata); + + /* Media removed callback function, called when a media has been removed + * internally from the PDrAW pipeline. Medias are for example raw or + * coded video medias. When a media is removed, any video sink created + * on this media must then be stopped. + * @param pdraw: PDrAW instance handle + * @param info: pointer on the media information + * @param userdata: user data pointer */ + void (*media_removed)(struct pdraw *pdraw, + const struct pdraw_media_info *info, + void *userdata); + + /* Socket creation callback function, called immediately after a + * socket creation with its file descriptor as parameter (optional, + * can be null). + * @param pdraw: PDrAW instance handle + * @param fd: socket file descriptor + * @param userdata: user data pointer */ + void (*socket_created)(struct pdraw *pdraw, int fd, void *userdata); +}; + + +/* Demuxer callback functions */ +struct pdraw_demuxer_cbs { + /* Open response callback function, called when an open operation is + * complete or has failed (optional, can be null, but highly recommended + * for correct PDrAW session management). + * The status parameter is the open operation status: 0 on success, + * or a negative errno value in case of error. + * If this function reports an error, the demuxer still needs to be + * closed: pdraw_demuxer_close() must be called and one must wait for + * the close_resp callback function to be issued prior to calling + * pdraw_demuxer_destroy(). + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param status: 0 on success, negative errno value in case of error + * @param userdata: user data pointer */ + void (*open_resp)(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + int status, + void *userdata); + + /* Close response callback function, called when a close operation is + * complete or has failed (optional, can be null, but highly recommended + * for correct PDrAW session management). + * The status parameter is the close operation status: 0 on success, + * or a negative errno value in case of error. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param status: 0 on success, negative errno value in case of error + * @param userdata: user data pointer */ + void (*close_resp)(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + int status, + void *userdata); + + /* Unrecoverable error callback function, called when a previously + * opened session is no longer running. When this function is called, + * the demuxer session is no longer running; pdraw_demuxer_close() must + * be called and one must wait for the close_resp callback function to + * be issued prior to calling pdraw_demuxer_destroy(). + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param userdata: user data pointer */ + void (*unrecoverable_error)(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + void *userdata); + + /* Demuxer media selection callback function, called with a list of + * video medias found from which the application must choose one or more + * to process in the pipeline. The return value of the callback function + * must be a bitfield of the identifiers of the chosen medias (from the + * pdraw_demuxer_media structure), or 0 to choose the default medias. If + * the return value is -ENOSYS, the callback is considered not + * implemented and the default medias are chosen. If the return value is + * -ECANCELED no media is chosen and the open operation is aborted. If + * the return value is another negative errno or an invalid bitfield the + * open_resp callback function will be called if an open operation is in + * progress, or the unrecoverable_error callback function otherwise. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param medias: array of demuxer media + * @param count: demuxer media array element count + * @param userdata: user data pointer + * @return a bitfield of the identifiers of the chosen medias, + * 0 or -ENOSYS to choose the default medias, + * -ECANCELED to choose no media and abort the open operation, + * or another negative errno value in case of error */ + int (*select_media)(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + const struct pdraw_demuxer_media *medias, + size_t count, + void *userdata); + + /* Ready to play callback function, called when the playback is ready + * to start (optional, can be null). This function is called to indicate + * that the PDrAW session is ready to process play operations. + * Generally the session is ready to play as soon as the open_resp + * callback function has been called with a success status. One case + * when the open operation was successful and the playback is not ready + * is when connected to a SkyController's RTSP server but the + * SkyController itself is not yet connected to a drone. Similarly, + * when connected to a drone's stream through a SkyController's RTSP + * server, if the drone is disconnected from the SkyController, this + * function will be called with a 0 value in the ready parameter. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param ready: 1 if the session is ready to play, 0 otherwise + * @param userdata: user data pointer */ + void (*ready_to_play)(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + int ready, + void *userdata); + + /* End of range callback function, called when the playback is suspended + * after having reached the end of the playback duration (optional, + * can be null). This function is only called for replays (either local + * or streamed), not for live streams. The timestamp parameter is the + * current play time in microseconds at the moment the playback is + * suspended. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param timestamp: current playback time in microseconds + * @param userdata: user data pointer */ + void (*end_of_range)(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + uint64_t timestamp, + void *userdata); + + /* Play response callback function, called when a play operation is + * complete (the playback has started) or has failed (optional, can be + * null). The status parameter is the play operation status: 0 on + * success, or a negative errno value in case of error. The timestamp + * parameter is the current play time in microseconds at the moment the + * playback is started. The speed parameter is the current playback + * speed; a negative value means playing backward. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param status: 0 on success, negative errno value in case of error + * @param timestamp: current playback time in microseconds + * @param speed: current playback speed, negative means backward + * @param userdata: user data pointer */ + void (*play_resp)(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + int status, + uint64_t timestamp, + float speed, + void *userdata); + + /* Pause response callback function, called when a pause operation is + * complete (the playback is suspended) or has failed (optional, can be + * null). The status parameter is the pause operation status: 0 on + * success, or a negative errno value in case of error. The timestamp + * parameter is the current play time in microseconds at the moment the + * playback is paused. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param status: 0 on success, negative errno value in case of error + * @param timestamp: current playback time in microseconds + * @param userdata: user data pointer */ + void (*pause_resp)(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + int status, + uint64_t timestamp, + void *userdata); + + /* Seek response callback function, called when a seek operation is + * complete or has failed (optional, can be null). + * The status parameter is the seek operation status: 0 on success, + * or a negative errno value in case of error. The timestamp parameter + * is the current play time in microseconds after seeking. The speed + * parameter is the current playback speed; a negative value means + * playing backward. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param status: 0 on success, negative errno value in case of error + * @param timestamp: current playback time in microseconds + * @param speed: current playback speed, negative means backward + * @param userdata: user data pointer */ + void (*seek_resp)(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + int status, + uint64_t timestamp, + float speed, + void *userdata); +}; + + +/* Video renderer callback functions */ +struct pdraw_video_renderer_cbs { + /* Media added callback function, called when a media has been added + * internally to the renderer. Medias are raw video medias. + * This function is called from the pomp_loop thread. + * @param pdraw: PDrAW instance handle + * @param renderer: renderer handle + * @param info: pointer on the media information + * @param userdata: user data pointer */ + void (*media_added)(struct pdraw *pdraw, + struct pdraw_video_renderer *renderer, + const struct pdraw_media_info *info, + void *userdata); + + /* Media removed callback function, called when a media has been removed + * internally from the renderer. Medias are raw video medias. + * This function is called from the pomp_loop thread. + * @param pdraw: PDrAW instance handle + * @param renderer: renderer handle + * @param info: pointer on the media information + * @param userdata: user data pointer */ + void (*media_removed)(struct pdraw *pdraw, + struct pdraw_video_renderer *renderer, + const struct pdraw_media_info *info, + void *userdata); + + /* Render ready callback function, called both when a new frame is + * ready for rendering and periodically (optional, can be null). + * This function is called from the pomp_loop thread. + * @param pdraw: PDrAW instance handle + * @param renderer: renderer handle + * @param userdata: user data pointer */ + void (*render_ready)(struct pdraw *pdraw, + struct pdraw_video_renderer *renderer, + void *userdata); + + /* External texture loading callback function (optional, can be null). + * This function is called before the rendering of the video frame in + * order to override the frame loading as a texture. This can be used + * to transform the video frames outside of PDrAW before resuming the + * rendering. This function is called from the rendering thread. + * @param pdraw: PDrAW instance handle + * @param renderer: renderer handle + * @param texture_width: texture width in pixels + * @param texture_height: texture height in pixels + * @param media_info: media information + * @param frame: frame information + * @param frame_userdata: frame user data buffer + * @param frame_userdata_len: frame user data buffer size in bytes + * @param userdata: user data pointer + * @return 0 on success, negative errno value in case of error */ + int (*load_texture)(struct pdraw *pdraw, + struct pdraw_video_renderer *renderer, + unsigned int texture_width, + unsigned int texture_height, + const struct pdraw_media_info *media_info, + struct mbuf_raw_video_frame *frame, + const void *frame_userdata, + size_t frame_userdata_len, + void *userdata); + + /* Overlay rendering callback function (optional, can be null). + * This function is called after the rendering of the video frame + * (if one is available) in order to render an application overlay + * on top of the video. When HMD distorsion correction is enabled + * in the renderer, it is applied after the overlay rendering. + * When no frame is available for the rendering, the frame_meta + * and frame_extra parameters are NULL. This function is called + * from the rendering thread. + * @param pdraw: PDrAW instance handle + * @param renderer: renderer handle + * @param render_pos: rendering position + * @param content_pos: video content position + * @param view_mat: 4x4 view matrix + * @param proj_mat: 4x4 projection matrix + * @param media_info: media information + * @param frame_meta: frame metadata (optional, can be NULL) + * @param frame_extra: frame extra information (optional, can be NULL) + * @param userdata: user data pointer */ + void (*render_overlay)( + struct pdraw *pdraw, + struct pdraw_video_renderer *renderer, + const struct pdraw_rect *render_pos, + const struct pdraw_rect *content_pos, + const float *view_mat, + const float *proj_mat, + const struct pdraw_media_info *media_info, + struct vmeta_frame *frame_meta, + const struct pdraw_video_frame_extra *frame_extra, + void *userdata); +}; + + +/* Coded video sink callback functions */ +struct pdraw_coded_video_sink_cbs { + /* Coded video sink flush callback function, called when flushing is + * required (mandatory). When this function is called, the application + * must flush the sink queue by calling + * mbuf_coded_video_frame_queue_flush() and must return all frames + * outside of the queue by calling mbuf_coded_video_frame_unref(); once + * the flushing is done, the pdraw_coded_video_sink_queue_flushed() + * function must be called. + * @param pdraw: PDrAW instance handle + * @param sink: coded video sink handle + * @param userdata: user data pointer */ + void (*flush)(struct pdraw *pdraw, + struct pdraw_coded_video_sink *sink, + void *userdata); +}; + + +/* Raw video sink callback functions */ +struct pdraw_raw_video_sink_cbs { + /* Raw video sink flush callback function, called when flushing is + * required (mandatory). When this function is called, the application + * must flush the sink queue by calling + * mbuf_raw_video_frame_queue_flush() and must return all frames outside + * of the queue by calling mbuf_raw_video_frame_unref(); once the + * flushing is done, the pdraw_raw_video_sink_queue_flushed() function + * must be called. + * @param pdraw: PDrAW instance handle + * @param sink: raw video sink handle + * @param userdata: user data pointer */ + void (*flush)(struct pdraw *pdraw, + struct pdraw_raw_video_sink *sink, + void *userdata); +}; + + +/** + * Instance management API + */ + +/** + * Create a PDrAW instance. + * A running pomp_loop must be provided; all functions (with the exception of + * rendering functions) must be called from the pomp_loop thread; all callback + * functions (with the exception of rendering callbacks) are issued from the + * pomp_loop thread. The callbacks structure must be provided but all callback + * functions are optional. However, for correct management of the session it is + * highly recommended to implement at least the open_resp and close_resp + * functions. The instance handle is returned through the ret_obj parameter. + * When no longer needed, the instance must be freed using the pdraw_destroy() + * function. The pdraw_stop() function must be called and one must wait for the + * stop_resp callback function to be issued prior to calling pdraw_destroy(). + * @param loop: pomp_loop to use + * @param cbs: PDrAW callback functions + * @param userdata: callback functions user data (optional, can be null) + * @param ret_obj: PDrAW instance handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_new(struct pomp_loop *loop, + const struct pdraw_cbs *cbs, + void *userdata, + struct pdraw **ret_obj); + + +/** + * Free a PDrAW instance. + * This function frees all resources associated with a PDrAW instance. + * If a successful pdraw_open_*() was made, the pdraw_close() function must + * be called and one must wait for the close_resp callback function to be + * issued prior to calling pdraw_destroy(). + * @param pdraw: PDrAW instance handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_destroy(struct pdraw *pdraw); + + +/** + * Stop a PDrAW instance. + * This function stops a PDrAW instance and all the associated objects. + * The function returns before the actual stopping is done. If the function + * returns 0, the stop_resp callback function will be called once the stopping + * is successful (0 status) or has failed (negative errno status). If the + * function returns a negative errno value (immediate failure), the stop_resp + * callback function will not be called. After a successful stop, the PDrAW + * instance must be destroyed by calling pdraw_destroy(). + * @param pdraw: PDrAW instance handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_stop(struct pdraw *pdraw); + + +/** + * Demuxer API + */ + +/** + * Create a demuxer on a URL (stream or local file). + * The URL can be either an RTSP URL (starting with "rtsp://") or a local file + * path (either absolute or relative). + * The function returns before the actual opening is done. If the function + * returns 0, the open_resp callback function will be issued once the open + * operation is successful (0 status) or has failed (negative errno status). + * If the function returns a negative errno value (immediate failure), the + * open_resp callback function will not be issued. + * Once a demuxer is no longer used, it must be closed and then destroyed + * (@see the pdraw_demuxer_close() function). + * @param pdraw: PDrAW instance handle + * @param url: URL of the resource to open + * @param cbs: demuxer callback functions + * @param userdata: callback functions user data (optional, can be null) + * @param ret_obj: demuxer handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_demuxer_new_from_url(struct pdraw *pdraw, + const char *url, + const struct pdraw_demuxer_cbs *cbs, + void *userdata, + struct pdraw_demuxer **ret_obj); + + +/** + * Create a demuxer on a single stream. + * This function opens an RTP/AVP stream. No session management is done: it + * is the application's responsibility to handle the ports negociation with + * the sender. If null local ports are given as parameter, the effective port + * numbers used can be retrieved using the + * pdraw_get_single_stream_local_stream_port() for the RTP port and + * pdraw_get_single_stream_local_control_port() for the RTCP functions. + * If the local_addr parameter is left null, any local network interface will + * be used. The remote_addr, remote_stream_port and remote_control_port + * parameters can be left null if unknown; they will be known once the stream + * is being received. + * The function returns before the actual opening is done. If the function + * returns 0, the open_resp callback function will be issued once the open + * operation is successful (0 status) or has failed (negative errno status). + * If the function returns a negative errno value (immediate failure), the + * open_resp callback function will not be issued. + * Once a demuxer is no longer used, it must be closed and then destroyed + * (@see the pdraw_demuxer_close() function). + * @param pdraw: PDrAW instance handle + * @param local_addr: local IP address (optional, can be NULL) + * @param local_stream_port: local stream (RTP) port (optional, can be 0) + * @param local_control_port: local control (RTCP) port (optional, can be 0) + * @param remote_addr: remote IP address (optional, can be NULL) + * @param remote_stream_port: remote stream (RTP) port (optional, can be 0) + * @param remote_control_port: remote control (RTCP) port (optional, can be 0) + * @param cbs: demuxer callback functions + * @param userdata: callback functions user data (optional, can be null) + * @param ret_obj: demuxer handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_demuxer_new_single_stream(struct pdraw *pdraw, + const char *local_addr, + uint16_t local_stream_port, + uint16_t local_control_port, + const char *remote_addr, + uint16_t remote_stream_port, + uint16_t remote_control_port, + const struct pdraw_demuxer_cbs *cbs, + void *userdata, + struct pdraw_demuxer **ret_obj); + + +/** + * Create a demuxer on a stream URL through a mux channel. + * The URL must be an RTSP URL (starting with "rtsp://"). Mux channels are used + * to transfer data between a SkyController remote and a smartphone through USB; + * see Parrot's libmux for more information. + * No concurrent sessions can run on the mux channel; therefore the user must + * take care of limiting the number of PDrAW instances and demuxer objects + * running on the mux channel to only one. + * The function returns before the actual opening is done. If the function + * returns 0, the open_resp callback function will be issued once the open + * operation is successful (0 status) or has failed (negative errno status). + * If the function returns a negative errno value (immediate failure), the + * open_resp callback function will not be issued. + * Once a demuxer is no longer used, it must be closed and then destroyed + * (@see the pdraw_demuxer_close() function). + * @param pdraw: PDrAW instance handle + * @param url: URL of the resource to open + * @param mux: mux instance handle + * @param cbs: demuxer callback functions + * @param userdata: callback functions user data (optional, can be null) + * @param ret_obj: demuxer handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_demuxer_new_from_url_on_mux(struct pdraw *pdraw, + const char *url, + struct mux_ctx *mux, + const struct pdraw_demuxer_cbs *cbs, + void *userdata, + struct pdraw_demuxer **ret_obj); + + +/** + * Destroy a demuxer. + * This function stops a running demuxer and frees the associated resources. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_demuxer_destroy(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer); + + +/** + * Close a demuxer. + * This function closes a previously opened demuxer (either record or stream). + * The function returns before the actual closing is done. If the function + * returns 0, the close_resp callback function will be issued once the close is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the close_resp callback + * function will not be issued. After a successful close, the demuxer must be + * destroyed by calling pdraw_demuxer_destroy(). + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_demuxer_close(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer); + + +/** + * Get the single stream local stream port. + * This function returns the local stream (RTP) port currently in use after + * a succesful open operation on a single stream (RTP/AVP) or an RTSP URL. + * If no open operation has been done, or if an open operation has been done + * on a mux channel, 0 is returned. + * This is useful when calling pdraw_open_single_stream() with null local + * ports to let PDrAW open sockets on any available port. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @return the stream port on success, 0 in case of error + */ +PDRAW_API uint16_t pdraw_demuxer_get_single_stream_local_stream_port( + struct pdraw *pdraw, + struct pdraw_demuxer *demuxer); + + +/** + * Get the single stream local control port. + * This function returns the local control (RTCP) port currently in use after + * a succesful open operation on a single stream (RTP/AVP) or an RTSP URL. + * If no open operation has been done, or if an open operation has been done + * on a mux channel, 0 is returned. + * This is useful when calling pdraw_open_single_stream() with null local + * ports to let PDrAW open sockets on any available port. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @return the stream port on success, 0 in case of error + */ +PDRAW_API uint16_t pdraw_demuxer_get_single_stream_local_control_port( + struct pdraw *pdraw, + struct pdraw_demuxer *demuxer); + + +/** + * Get the ready to play status. + * This function returns 1 if a successful open operation has been completed + * and if the playback is ready to start, 0 otherwise. One case when a + * successful open operation is complete but the playback is not ready is when + * connected to a SkyController's RTSP server but the SkyController itself is + * not yet connected to a drone. + * The value returned by this function is identical to the ready parameter + * passed to the ready_to_play callback function when it is issued. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @return the ready to play status on success, 0 in case of error + */ +PDRAW_API int pdraw_demuxer_is_ready_to_play(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer); + + +/** + * Get the pause status. + * This function returns 1 if the playback is currently paused, 0 otherwise. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @return the pause status on success, 0 in case of error + */ +PDRAW_API int pdraw_demuxer_is_paused(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer); + + +/** + * Play at normal speed (x1.0). + * This function starts the playback of the video. + * The function returns before the actual operation is done. If the function + * returns 0, the play_resp callback function will be issued once the play is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the play_resp callback + * function will not be issued. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_demuxer_play(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer); + + +/** + * Play at the given speed. + * This function starts the playback of the video. If the speed parameter is + * negative, the video is played backward. If the speed is greater than or + * equal to PDRAW_PLAY_SPEED_MAX, the speed is ignored and the video is played + * at the maximum speed achievable. If the speed is less than or equal to + * -PDRAW_PLAY_SPEED_MAX, the speed is ignored and the video is played backward + * at the maximum speed achievable. A 0.0 speed has the same effet as calling + * the pdraw_pause() function. On a live stream, the speed parameter has + * no effect. + * The function returns before the actual operation is done. If the function + * returns 0, the play_resp callback function will be issued once the play is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the play_resp callback + * function will not be issued. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param speed: playback speed (0.0 means pause, negative value means + * play backward) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_demuxer_play_with_speed(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + float speed); + + +/** + * Pause the playback. + * This function suspends the playback of the video. The session is not closed + * and the playback can be resumed using the play functions. + * The function returns before the actual operation is done. If the function + * returns 0, the pause_resp callback function will be issued once the pause is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the pause_resp callback + * function will not be issued. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_demuxer_pause(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer); + + +/** + * Go to previous frame in frame-by-frame playback. + * This function plays the previous frame while the playback is paused. If the + * playback is not currently paused an error is returned. Frame-by-frame is + * only available on local replays (MP4 records). + * The function returns before the actual operation is done. If the function + * returns 0, the seek_resp callback function will be issued once the seek is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the seek_resp callback + * function will not be issued. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_demuxer_previous_frame(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer); + + +/** + * Go to next frame in frame-by-frame playback. + * This function plays the next frame while the playback is paused. If the + * playback is not currently paused an error is returned. Frame-by-frame is + * only available on local replays (MP4 records). + * The function returns before the actual operation is done. If the function + * returns 0, the seek_resp callback function will be issued once the seek is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the seek_resp callback + * function will not be issued. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_demuxer_next_frame(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer); + + +/** + * Seek forward or backward. + * This function seeks forward (positive delta) or backward (negative delta). + * The delta parameter is in microseconds. The exact parameter is a boolean + * value; when exact is 0 the seek is done to the nearest synchronization sample + * preceeding the delta, otherwise the seek is done to the sample nearest to the + * delta. Seeking is only available on replays (either local or streamed), + * not on live streams. + * The function returns before the actual operation is done. If the function + * returns 0, the seek_resp callback function will be issued once the seek is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the seek_resp callback + * function will not be issued. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param delta: time delta in microseconds (positive or negative) + * @param exact: 1 means seek to the sample closest to the delta, 0 means seek + * to the nearest synchronization sample preceeding the delta + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_demuxer_seek(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + int64_t delta, + int exact); + + +/** + * Seek forward. + * This function seeks forward by delta microseconds. It has the same + * behavior as calling pdraw_seek() with a positive delta. The exact parameter + * is a boolean value; when exact is 0 the seek is done to the nearest + * synchronization sample preceeding the delta, otherwise the seek is done to + * the sample nearest to the delta. Seeking is only available on replays + * (either local or streamed), not on live streams. + * The function returns before the actual operation is done. If the function + * returns 0, the seek_resp callback function will be issued once the seek is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the seek_resp callback + * function will not be issued. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param delta: positive time delta forward in microseconds + * @param exact: 1 means seek to the sample closest to the delta, 0 means seek + * to the nearest synchronization sample preceeding the delta + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_demuxer_seek_forward(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + uint64_t delta, + int exact); + + +/** + * Seek backward. + * This function seeks backward by delta microseconds (postive). It has the same + * behavior as calling pdraw_seek() with a negative delta. The exact parameter + * is a boolean value; when exact is 0 the seek is done to the nearest + * synchronization sample preceeding the delta, otherwise the seek is done to + * the sample nearest to the delta. Seeking is only available on replays + * (either local or streamed), not on live streams. + * The function returns before the actual operation is done. If the function + * returns 0, the seek_resp callback function will be issued once the seek is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the seek_resp callback + * function will not be issued. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param delta: positive time delta backward in microseconds + * @param exact: 1 means seek to the sample closest to the delta, 0 means seek + * to the nearest synchronization sample preceeding the delta + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_demuxer_seek_back(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + uint64_t delta, + int exact); + + +/** + * Seek to a given time. + * This function seeks to the given play timestamp in microseconds. The exact + * parameter is a boolean value; when exact is 0 the seek is done to the nearest + * synchronization sample preceeding the timestamp, otherwise the seek is done + * to the sample nearest to the timestamp. Seeking is only available on replays + * (either local or streamed), not on live streams. + * The function returns before the actual operation is done. If the function + * returns 0, the seek_resp callback function will be issued once the seek is + * successful (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the seek_resp callback + * function will not be issued. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param timestamp: play timestamp in microseconds + * @param exact: 1 means seek to the sample closest to the timestamp, + * 0 means seek to the nearest synchronization sample + * preceeding the timestamp + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_demuxer_seek_to(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + uint64_t timestamp, + int exact); + + +/** + * Get the playback duration. + * This function returns the playback duration in microseconds. The duration is + * only available on replays (either local or streamed), not on live streams. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @return the duration in microseconds on success, 0 in case of error + */ +PDRAW_API uint64_t pdraw_demuxer_get_duration(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer); + + +/** + * Get the playback current time. + * This function returns the current playback position in microseconds. + * On replays (either local or streamed) this is the position between 0 and + * the duration; on live streams this is the time since the start of the + * stream session. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @return the current time in microseconds on success, 0 in case of error + */ +PDRAW_API uint64_t +pdraw_demuxer_get_current_time(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer); + + +/** + * Muxer API + */ + +/** + * Create a muxer (experimental). + * This function creates a muxer with a given URL. + * Once the muxer is created medias can be added by id using the + * pdraw_muxer_add_media() function. Once a muxer is no longer used it must be + * destroyed by calling the pdraw_muxer_destroy() function. If writing to an + * MP4 file, the file is finalized in the pdraw_muxer_destroy() function. + * @note: experimental only, the function returns -ENOSYS + * @param pdraw: PDrAW instance handle + * @param url: destination URL + * @param ret_obj: muxer handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_muxer_new(struct pdraw *pdraw, + const char *url, + struct pdraw_muxer **ret_obj); + + +/** + * Destroy a muxer. + * This function stops a running muxer and frees the associated resources. + * @param pdraw: PDrAW instance handle + * @param muxer: muxer handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_muxer_destroy(struct pdraw *pdraw, + struct pdraw_muxer *muxer); + + +/** + * Add a media to a muxer. + * This function adds a media to the muxer by its media_id. The media + * idenfifiers are known when the media_added or media_removed general + * callback functions are called. + * The params structure is only relevant for video medias; the structure must + * then be provided but all parameters are optional and can be left null. + * @param pdraw: PDrAW instance handle + * @param muxer: muxer handle + * @param media_id: identifier of the media to add to the muxer + * @param params: muxer video media parameters + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_muxer_add_media(struct pdraw *pdraw, + struct pdraw_muxer *muxer, + unsigned int media_id, + const struct pdraw_muxer_video_media_params *params); + + +/** + * Video renderer API + * @warning: all functions must be called from the application's + * rendering thread + */ + +/** + * Create a video renderer. + * This function creates a video renderer on a media of the given media id; + * if the media id is zero the first raw media encountered is used. + * Once the renderer is created, the rendering is done by calling the + * pdraw_video_renderer_render*() functions. Once a renderer is no longer used + * it must be destroyed by calling the pdraw_video_renderer_destroy() function. + * The render_pos parameter sets the position and size of the rendering in the + * window/view; these coordinates are in pixels from the bottom-left corner + * (OpenGL coordinates). The params structure must be provided but all + * parameters are optional and can be left null. + * The callbacks structure must be provided but all callback functions are + * optional; all callback functions are called from the rendering thread, + * except the render_ready function which is called from the pomp_loop thread. + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW instance handle + * @param media_id: identifier of the raw media to render (from a + * pdraw_media_info structure); if zero the first + * raw media found is used for rendering + * @param render_pos: rendering position and size + * @param params: renderer parameters + * @param cbs: renderer callback functions + * @param userdata: callback functions user data (optional, can be null) + * @param ret_obj: renderer handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_video_renderer_new(struct pdraw *pdraw, + unsigned int media_id, + const struct pdraw_rect *render_pos, + const struct pdraw_video_renderer_params *params, + const struct pdraw_video_renderer_cbs *cbs, + void *userdata, + struct pdraw_video_renderer **ret_obj); + + +/** + * Create a video renderer on an EGL display. + * This function creates a video renderer on an active EGL display context on a + * media of the given media id; if the media id is zero the first raw + * media encountered is used. Once the renderer is created, the rendering is + * done by calling the pdraw_video_renderer_render*() functions. Once a renderer + * is no longer used it must be destroyed by calling the + * pdraw_video_renderer_destroy() function. The render_pos parameter sets the + * position and size of the rendering in the window/view; these coordinates are + * in pixels from the bottom-left corner (OpenGL coordinates). The params + * structure must be provided but all parameters are optional and can be left + * null. The callbacks structure must be provided but all callback functions are + * optional; all callback functions are called from the rendering thread, + * except the render_ready function which is called from the pomp_loop thread. + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW instance handle + * @param media_id: identifier of the raw media to render (from a + * pdraw_media_info structure); if zero the first + * raw media found is used for rendering + * @param render_pos: rendering position and size + * @param params: renderer parameters + * @param cbs: renderer callback functions + * @param userdata: callback functions user data (optional, can be null) + * @param egl_display: EGL display context + * @param ret_obj: renderer handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_video_renderer_new_egl(struct pdraw *pdraw, + unsigned int media_id, + const struct pdraw_rect *render_pos, + const struct pdraw_video_renderer_params *params, + const struct pdraw_video_renderer_cbs *cbs, + void *userdata, + struct egl_display *egl_display, + struct pdraw_video_renderer **ret_obj); + + +/** + * Destroy a video renderer. + * This function stops a running video renderer and frees the associated + * resources. + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW instance handle + * @param renderer: renderer handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_video_renderer_destroy(struct pdraw *pdraw, + struct pdraw_video_renderer *renderer); + + +/** + * Resize a video renderer. + * This function updates the rendering position on a running video renderer. + * The render_pos parameter sets the position and size of the rendering in the + * window/view; these coordinates are in pixels from the bottom-left corner + * (OpenGL coordinates). + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW instance handle + * @param renderer: renderer handle + * @param render_pos: rendering position and size + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_video_renderer_resize(struct pdraw *pdraw, + struct pdraw_video_renderer *renderer, + const struct pdraw_rect *render_pos); + + +/** + * Set the video renderer media identifier. + * This function updates the identifier of the media on which the rendering is + * done; if the media id is zero the first raw media encountered is used. + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW instance handle + * @param renderer: renderer handle + * @param media_id: identifier of the raw media to render (from a + * pdraw_media_info structure); if zero the first raw media + * found is used for rendering + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_video_renderer_set_media_id(struct pdraw *pdraw, + struct pdraw_video_renderer *renderer, + unsigned int media_id); + + +/** + * Get the video renderer media identifier. + * This function retrieves the identifier of the media on which the rendering + * is done. + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW instance handle + * @param renderer: renderer handle + * @return the identifier of the media on success, 0 if no media is being + * renderered or in case of error + */ +PDRAW_API unsigned int +pdraw_video_renderer_get_media_id(struct pdraw *pdraw, + struct pdraw_video_renderer *renderer); + + +/** + * Set the video renderer parameters. + * This function updates the rendering parameters on a running video renderer. + * The params structure must be provided but all parameters are optional and + * can be left null. + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW instance handle + * @param renderer: renderer handle + * @param params: renderer parameters + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_video_renderer_set_params( + struct pdraw *pdraw, + struct pdraw_video_renderer *renderer, + const struct pdraw_video_renderer_params *params); + + +/** + * Get the video renderer parameters. + * This function retrieves the rendering parameters on a running video renderer. + * The provided params structure is filled by the function. + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW instance handle + * @param renderer: renderer handle + * @param params: renderer parameters (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_video_renderer_get_params(struct pdraw *pdraw, + struct pdraw_video_renderer *renderer, + struct pdraw_video_renderer_params *params); + + +/** + * Render the video. + * This function renders the video with the current rendering position and + * rendering parameters. + * For render-on-demand, consider using the render_ready callback function + * which is called both when a new frame is ready for rendering and + * periodically. Note that the render_ready callback function is called from + * the pomp_loop thread, not the rendering thread; the synchronization is up + * to the caller. + * If a content_pos structure is provided, it is filled with the actual position + * and size of the video within the rendering position; these coordinates are + * in pixels from the bottom-left corner (OpenGL coordinates). + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW instance handle + * @param renderer: renderer handle + * @param content_pos: video content position (output; optional, can be null) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_video_renderer_render(struct pdraw *pdraw, + struct pdraw_video_renderer *renderer, + struct pdraw_rect *content_pos); + + +/** + * Render the video with provided matrices. + * This function renders the video with the current rendering position and + * rendering parameters, and the provided view and projection matrices. + * The view_mat and proj_mat matrices are 4x4 OpenGL matrices. + * For render-on-demand, consider using the render_ready callback function + * which is called both when a new frame is ready for rendering and + * periodically. Note that the render_ready callback function is called from + * the pomp_loop thread, not the rendering thread; the synchronization is up + * to the caller. + * If a content_pos structure is provided, it is filled with the actual position + * and size of the video within the rendering position; these coordinates are + * in pixels from the bottom-left corner (OpenGL coordinates). + * @warning: this function must be called from the application's rendering + * thread. + * @param pdraw: PDrAW instance handle + * @param renderer: renderer handle + * @param content_pos: video content position (output; optional, can be null) + * @param view_mat: 4x4 view matrix + * @param proj_mat: 4x4 projection matrix + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_video_renderer_render_mat(struct pdraw *pdraw, + struct pdraw_video_renderer *renderer, + struct pdraw_rect *content_pos, + const float *view_mat, + const float *proj_mat); + + +/** + * Video sink API + */ + +/** + * Create a coded video sink. + * This function creates a coded video sink on a media of the given media_id. + * The media idenfifiers are known when the media_added or media_removed + * general callback functions are called. + * Once the sink is created, video frames are retrieved by getting them + * from the queue returned by the pdraw_coded_video_sink_get_queue() function. + * Once a video sink is no longer used, it must be destroyed by calling the + * pdraw_coded_video_sink_destroy() function. + * The params structure must be provided but all parameters are optional and + * can be left null. + * The callbacks structure must be provided and the flush callback function is + * required to be implemented; all callback functions are called from the + * pomp_loop thread. When the flush callback function is called, the + * application must flush the sink queue by calling + * mbuf_coded_video_frame_queue_flush() and must return all frames outside of + * the queue by calling mbuf_coded_video_frame_unref(); once the flushing is + * complete, the pdraw_coded_video_sink_queue_flushed() function must be called. + * + * @note media_id must refer to a coded video media. + * + * @param pdraw: PDrAW instance handle + * @param media_id: identifier of the media on which to create the sink + * @param params: video sink parameters + * @param cbs: video sink callback functions + * @param userdata: callback functions user data (optional, can be null) + * @param ret_obj: video sink handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_coded_video_sink_new(struct pdraw *pdraw, + unsigned int media_id, + const struct pdraw_video_sink_params *params, + const struct pdraw_coded_video_sink_cbs *cbs, + void *userdata, + struct pdraw_coded_video_sink **ret_obj); + + +/** + * Destroy a coded video sink. + * This function stops a running video sink and frees the associated resources. + * A video sink must not be destroyed unless all frames outside of the queue + * have been returned by calling mbuf_coded_video_frame_unref(). Once a video + * sink is destroyed the queue returned by pdraw_coded_video_sink_get_queue() + * must no longer be used. + * @param pdraw: PDrAW instance handle + * @param sink: video sink handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_coded_video_sink_destroy(struct pdraw *pdraw, + struct pdraw_coded_video_sink *sink); + + +/** + * Resynchronize a coded video sink. + * This function schedules the output of a synchronization frame (IDR) for a + * running video sink. It can be used for example in case of unrecoverable video + * decoder errors to restart decoding. After a video sink creation, the first + * frame that is output is always a synchronization frame; therefore it is not + * necessary to call this function immediately after a video sink creation. + * @param pdraw: PDrAW instance handle + * @param sink: video sink handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_coded_video_sink_resync(struct pdraw *pdraw, + struct pdraw_coded_video_sink *sink); + + +/** + * Get the coded video sink frame queue. + * This function returns the frame queue to use in order to retrieve frames + * from a running video sink. Frames are retrieved from the queue by using the + * mbuf_coded_video_frame_queue_pop() function. + * @param pdraw: PDrAW instance handle + * @param sink: video sink handle + * @return a pointer on a mbuf_coded_video_frame_queue object on success, NULL + * in case of error + */ +PDRAW_API struct mbuf_coded_video_frame_queue * +pdraw_coded_video_sink_get_queue(struct pdraw *pdraw, + struct pdraw_coded_video_sink *sink); + + +/** + * Signal that a coded video sink has been flushed. + * This function is used to signal that flushing is complete. When the flush + * video sink callback function is called, the application must flush the sink + * queue by calling mbuf_coded_video_frame_queue_flush() and must return all + * frames outside of the queue by calling mbuf_coded_video_frame_unref(); once + * the flushing is complete, this function must be called. + * @param pdraw: PDrAW instance handle + * @param sink: video sink handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_coded_video_sink_queue_flushed(struct pdraw *pdraw, + struct pdraw_coded_video_sink *sink); + + +/** + * Create a raw video sink. + * This function creates a raw video sink on a media of the given media_id. + * The media idenfifiers are known when the media_added or media_removed + * general callback functions are called. + * Once the sink is created, video frames are retrieved by getting them + * from the queue returned by the pdraw_raw_video_sink_get_queue() function. + * Once a video sink is no longer used, it must be destroyed by calling the + * pdraw_raw_video_sink_destroy() function. + * The params structure must be provided but all parameters are optional and + * can be left null. + * The callbacks structure must be provided and the flush callback function is + * required to be implemented; all callback functions are called from the + * pomp_loop thread. When the flush callback function is called, the + * application must flush the sink queue by calling + * mbuf_raw_video_frame_queue_flush() and must return all frames outside of + * the queue by calling mbuf_raw_video_frame_unref(); once the flushing is + * complete, the pdraw_raw_video_sink_queue_flushed() function must be called. + * + * @note media_id must refer to a raw video media. + * + * @param pdraw: PDrAW instance handle + * @param media_id: identifier of the media on which to create the sink + * @param params: video sink parameters + * @param cbs: video sink callback functions + * @param userdata: callback functions user data (optional, can be null) + * @param ret_obj: video sink handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_raw_video_sink_new(struct pdraw *pdraw, + unsigned int media_id, + const struct pdraw_video_sink_params *params, + const struct pdraw_raw_video_sink_cbs *cbs, + void *userdata, + struct pdraw_raw_video_sink **ret_obj); + + +/** + * Destroy a raw video sink. + * This function stops a running video sink and frees the associated resources. + * A video sink must not be destroyed unless all frames outside of the queue + * have been returned by calling mbuf_raw_video_frame_unref(). Once a video + * sink is destroyed the queue returned by pdraw_raw_video_sink_get_queue() must + * no longer be used. + * @param pdraw: PDrAW instance handle + * @param sink: video sink handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_raw_video_sink_destroy(struct pdraw *pdraw, + struct pdraw_raw_video_sink *sink); + + +/** + * Get the raw video sink frame queue. + * This function returns the frame queue to use in order to retrieve frames + * from a running video sink. Frames are retrieved from the queue by using the + * mbuf_raw_video_frame_queue_pop() function. + * @param pdraw: PDrAW instance handle + * @param sink: video sink handle + * @return a pointer on a mbuf_raw_video_frame_queue object on success, NULL + * in case of error + */ +PDRAW_API struct mbuf_raw_video_frame_queue * +pdraw_raw_video_sink_get_queue(struct pdraw *pdraw, + struct pdraw_raw_video_sink *sink); + + +/** + * Signal that a raw video sink has been flushed. + * This function is used to signal that flushing is complete. When the flush + * video sink callback function is called, the application must flush the sink + * queue by calling mbuf_raw_video_frame_queue_flush() and must return all + * frames outside of the queue by calling mbuf_raw_video_frame_unref(); once + * the flushing is complete, this function must be called. + * @param pdraw: PDrAW instance handle + * @param sink: video sink handle + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_raw_video_sink_queue_flushed(struct pdraw *pdraw, + struct pdraw_raw_video_sink *sink); + + +/** + * Settings API + */ + +/** + * Get the PDrAW instance friendly name. + * This function fills the str array with the null-terminated friendly name. + * The string must have been previously allocated. The function writes up to + * len characters. The friendly name is generally either the device's friendly + * name (e.g. "Bob's phone") or the application's name (e.g. + * "MyDroneControllerApp"). It is used for example in metadata exchanged with a + * streaming server. + * @param pdraw: PDrAW instance handle + * @param str: pointer to the string to write to (output) + * @param len: maximum length of the string + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_get_friendly_name_setting(struct pdraw *pdraw, char *str, size_t len); + + +/** + * Set the PDrAW instance friendly name. + * The friendly_name string is copied internally. The friendly name is generally + * either the device's friendly name (e.g. "Bob's phone") or the application's + * name (e.g. "MyDroneControllerApp"). It is used for example in metadata + * exchanged with a streaming server. Setting the friendly name value is + * optional. + * @param pdraw: PDrAW instance handle + * @param friendly_name: pointer to the friendly name string + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_set_friendly_name_setting(struct pdraw *pdraw, + const char *friendly_name); + + +/** + * Get the PDrAW instance serial number. + * This function fills the str array with the null-terminated serial number. + * The string must have been previously allocated. The function writes up to + * len characters. The serial number is generally the unique serial number of + * the device on which PDrAW is running. It is used for example in metadata + * exchanged with a streaming server. + * @param pdraw: PDrAW instance handle + * @param str: pointer to the string to write to (output) + * @param len: maximum length of the string + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_get_serial_number_setting(struct pdraw *pdraw, char *str, size_t len); + + +/** + * Set the PDrAW instance serial number. + * The serial_number string is copied internally. The serial number is generally + * the unique serial number of the device on which PDrAW is running. It is used + * for example in metadata exchanged with a streaming server. Setting the serial + * number value is recommended as it is used as a unique identifier in the + * streaming protocols. + * @param pdraw: PDrAW instance handle + * @param serial_number: pointer to the serial number string + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_set_serial_number_setting(struct pdraw *pdraw, + const char *serial_number); + + +/** + * Get the PDrAW instance software version. + * This function fills the str array with the null-terminated software version. + * The string must have been previously allocated. The function writes up to + * len characters. The software version is generally the version number of the + * application running PDrAW (e.g. "MyApp v1.2.3"). It is used for example in + * metadata exchanged with a streaming server. + * @param pdraw: PDrAW instance handle + * @param str: pointer to the string to write to (output) + * @param len: maximum length of the string + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_get_software_version_setting(struct pdraw *pdraw, char *str, size_t len); + + +/** + * Set the PDrAW instance software version. + * The software_version string is copied internally. The software version is + * generally the version number of the application running PDrAW (e.g. + * "MyApp v1.2.3"). It is used for example in metadata exchanged with a + * streaming server. Setting the software version value is optional. + * @param pdraw: PDrAW instance handle + * @param software_version: pointer to the software version string + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_set_software_version_setting(struct pdraw *pdraw, + const char *software_version); + + +/** + * Get the pipeline mode setting. + * This function returns the pipeline mode of a PDrAW instance. The pipeline + * mode controls whether to decode the selected video media (for full processing + * up to the rendering), or to disable video decoding (e.g. when no rendering + * is required, only a coded video sink). + * @param pdraw: PDrAW instance handle + * @return the pipeline mode, or PDRAW_PIPELINE_MODE_DECODE_ALL in case of error + */ +PDRAW_API enum pdraw_pipeline_mode +pdraw_get_pipeline_mode_setting(struct pdraw *pdraw); + + +/** + * Set the pipeline mode setting. + * This function sets the pipeline mode of a PDrAW instance. This function can + * be called only prior to any open operation. The pipeline mode controls + * whether to decode the selected video media (for full processing up to the + * rendering), or to disable video decoding (e.g. when no rendering is required, + * only an coded video sink). + * @param pdraw: PDrAW instance handle + * @param mode: pipeline mode + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_set_pipeline_mode_setting(struct pdraw *pdraw, + enum pdraw_pipeline_mode mode); + + +/** + * Get the display screen settings. + * This function returns the display screen settings through the xdpi, ydpi and + * device_margin_* parameters. This is only useful if HMD distortion correction + * is enabled in the video rendering. The xdpi and ydpi are pixel densities in + * dots per inches. The device margins are in millimeters. + * @param pdraw: PDrAW instance handle + * @param xdpi: horizontal pixel density (output) + * @param ydpi: vertical pixel density (output) + * @param device_margin_top: top device margin (output) + * @param device_margin_bottom: bottom device margin (output) + * @param device_margin_left: left device margin (output) + * @param device_margin_right: right device margin (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_get_display_screen_settings(struct pdraw *pdraw, + float *xdpi, + float *ydpi, + float *device_margin_top, + float *device_margin_bottom, + float *device_margin_left, + float *device_margin_right); + + +/** + * Set the display screen settings. + * This function sets the display screen settings. This is only useful if HMD + * distortion correction is enabled in the video rendering. The xdpi and ydpi + * are pixel densities in dots per inches. The device margins are in + * millimeters. + * @param pdraw: PDrAW instance handle + * @param xdpi: horizontal pixel density + * @param ydpi: vertical pixel density + * @param device_margin_top: top device margin + * @param device_margin_bottom: bottom device margin + * @param device_margin_left: left device margin + * @param device_margin_right: right device margin + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_set_display_screen_settings(struct pdraw *pdraw, + float xdpi, + float ydpi, + float device_margin_top, + float device_margin_bottom, + float device_margin_left, + float device_margin_right); + + +/** + * Get the HMD model setting. + * This function returns the head-mounted display (HMD) model. This is only + * useful if HMD distortion correction is enabled in the video rendering. + * @param pdraw: PDrAW instance handle + * @return the HMD model, or PDRAW_HMD_MODEL_UNKNOWN in case of error + */ +PDRAW_API enum pdraw_hmd_model pdraw_get_hmd_model_setting(struct pdraw *pdraw); + + +/** + * Set the HMD model setting. + * This function sets the head-mounted display (HMD) model. This is only + * useful if HMD distortion correction is enabled in the video rendering. + * @param pdraw: PDrAW instance handle + * @param hmd_model: HMD model + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_set_hmd_model_setting(struct pdraw *pdraw, + enum pdraw_hmd_model hmd_model); + +/** + * Platform-specific API + */ + +/** + * Set the Android JVM pointer. + * This function sets the JVM pointer for internal calls to the Android SDK API. + * This is only useful on Android and is ignored on other platforms. If the + * JVM pointer is not provided on Android platforms, some features may not be + * available. + * @param pdraw: PDrAW instance handle + * @param jvm: JVM pointer + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_set_android_jvm(struct pdraw *pdraw, void *jvm); + + +/** + * Debug API + */ + +/** + * Dump the current pipeline as a directed graph using the DOT file format. + * @param pdraw: PDrAW instance handle + * @param file_name: DOT file to write to + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_dump_pipeline(struct pdraw *pdraw, const char *file_name); + + +/** + * Helpers + */ + +/** + * ToString function for enum pdraw_hmd_model. + * @param val: HMD model value to convert + * @return a string description of the HMD model + */ +PDRAW_API const char *pdraw_hmd_model_str(enum pdraw_hmd_model val); + + +/** + * ToString function for enum pdraw_pipeline_mode. + * @param val: pipeline mode value to convert + * @return a string description of the pipeline mode + */ +PDRAW_API const char *pdraw_pipeline_mode_str(enum pdraw_pipeline_mode val); + + +/** + * ToString function for enum pdraw_playback_type. + * @param val: playback type value to convert + * @return a string description of the playback type + */ +PDRAW_API const char *pdraw_playback_type_str(enum pdraw_playback_type val); + + +/** + * ToString function for enum pdraw_media_type. + * @param val: media type value to convert + * @return a string description of the media type + */ +PDRAW_API const char *pdraw_media_type_str(enum pdraw_media_type val); + + +/** + * ToString function for enum pdraw_video_type. + * @param val: video type value to convert + * @return a string description of the video type + */ +PDRAW_API const char *pdraw_video_type_str(enum pdraw_video_type val); + + +/** + * ToString function for enum pdraw_histogram_channel. + * @param val: histogram channel value to convert + * @return a string description of the histogram channel + */ +PDRAW_API const char * +pdraw_histogram_channel_str(enum pdraw_histogram_channel val); + + +/** + * ToString function for enum pdraw_video_renderer_scheduling_mode. + * @param val: video renderer scheduling mode value to convert + * @return a string description of the video renderer scheduling mode + */ +PDRAW_API const char *pdraw_video_renderer_scheduling_mode_str( + enum pdraw_video_renderer_scheduling_mode val); + + +/** + * ToString function for enum pdraw_video_renderer_fill_mode. + * @param val: video renderer fill mode value to convert + * @return a string description of the video renderer fill mode + */ +PDRAW_API const char * +pdraw_video_renderer_fill_mode_str(enum pdraw_video_renderer_fill_mode val); + + +/** + * ToString function for enum pdraw_video_renderer_transition_flag. + * @param val: video renderer transition flag value to convert + * @return a string description of the video renderer transition flag + */ +PDRAW_API const char *pdraw_video_renderer_transition_flag_str( + enum pdraw_video_renderer_transition_flag val); + + +/** + * Convert a video frame metadata structure to a JSON object. + * The JSON object must have been previously created. The function appends + * new elements in this object. + * @param frame: pointer to a video frame structure + * @param metadata: optional pointer to the frame metadata + * @param jobj: pointer to a JSON object to fill (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdraw_video_frame_to_json(const struct pdraw_video_frame *frame, + struct vmeta_frame *metadata, + struct json_object *jobj); + + +/** + * Convert a video frame metadata structure to a JSON string. + * This function fills the str array with the null-terminated JSON string. + * The string must have been previously allocated. The function writes + * up to len characters. + * @param frame: pointer to a video frame structure + * @param metadata: optional pointer to the frame metadata + * @param str: pointer to the string to write to (output) + * @param len: maximum length of the string + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int +pdraw_video_frame_to_json_str(const struct pdraw_video_frame *frame, + struct vmeta_frame *metadata, + char *str, + unsigned int len); + +/** + * Duplicate a media_info structure. + * @param src: pointer to the media_info structure to duplicate + * @return a pointer to the newly allocated structure or NULL on error. + */ +PDRAW_API struct pdraw_media_info * +pdraw_media_info_dup(const struct pdraw_media_info *src); + + +/** + * Free a media_info structure. + * @param media_info: pointer to the media_info structure to free + */ +PDRAW_API void pdraw_media_info_free(struct pdraw_media_info *media_info); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* !_PDRAW_H_ */ diff --git a/libpdraw/include/pdraw/pdraw.hpp b/libpdraw/include/pdraw/pdraw.hpp index 1d9a0c2..43e9786 100644 --- a/libpdraw/include/pdraw/pdraw.hpp +++ b/libpdraw/include/pdraw/pdraw.hpp @@ -1,1547 +1,1547 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_HPP_ -#define _PDRAW_HPP_ - -#include - -#include - -#include "pdraw_defs.h" - -namespace Pdraw { - - -/* PDrAW object interface; - * see the createPdraw() function for creating a PDrAW instance */ -class IPdraw { -public: - /* PDrAW listener object */ - class Listener { - public: - /** - * PDrAW listener object destructor. - */ - virtual ~Listener(void) {} - - /** - * Stop response function, called when a stop operation is - * complete or has failed. The status parameter is the stop - * operation status: 0 on success, or a negative errno value - * in case of error. - * @param pdraw: PDrAW instance handle - * @param status: 0 on success, negative errno value in case - * of error - */ - virtual void stopResponse(IPdraw *pdraw, int status) = 0; - - /** - * Media added function, called when a media has been added - * internally in the PDrAW pipeline. Medias are for example - * raw or coded video medias. The info structure gives the - * media identifier that can be used for example to create a - * video sink on this media. - * @param pdraw: PDrAW instance handle - * @param info: pointer on the media information - */ - virtual void - onMediaAdded(IPdraw *pdraw, - const struct pdraw_media_info *info) = 0; - - /** - * Media removed function, called when a media has been removed - * internally from the PDrAW pipeline. Medias are for example - * raw or coded video medias. When a media is removed, any - * video sink created on this media must then be stopped. - * @param pdraw: PDrAW instance handle - * @param info: pointer on the media information - */ - virtual void - onMediaRemoved(IPdraw *pdraw, - const struct pdraw_media_info *info) = 0; - - /** - * Socket creation function, called immediately after a socket - * creation with its file descriptor as parameter. - * @param pdraw: PDrAW instance handle - * @param fd: socket file descriptor - */ - virtual void onSocketCreated(IPdraw *pdraw, int fd) = 0; - }; - - - /** - * Instance management API - */ - - /** - * Destroy a PDrAW instance. - * This function frees all resources associated with a PDrAW instance. - * The stop() function must be called prior to destroying and one must - * wait for the stopResponse() listener function to be called prior to - * destroying the PDrAW instance. - */ - virtual ~IPdraw(void) {} - - /** - * Stop a session. - * This function stops a session and all the associated objects. - * The function returns before the actual stopping is done. If the - * function returns 0, the stopResponse() listener function will be - * called once the stopping is successful (0 status) or has failed - * (negative errno status). If the function returns a negative errno - * value (immediate failure), the stopResponse() listener function - * will not be called. After a successful stop, the PDrAW instance - * must be destroyed. - * @return 0 on success, negative errno value in case of error - */ - virtual int stop(void) = 0; - - - /** - * Demuxer API - * @see the createDemuxer() function for the demuxer object creation - */ - class IDemuxer { - public: - /* Demuxer listener object */ - class Listener { - public: - /** - * Demuxer listener object destructor. - */ - virtual ~Listener(void) {} - - /** - * Open response function, called when an open operation - * is complete or has failed. The status parameter is - * the open operation status: 0 on success, or a - * negative errno value in case of error. - * If this function reports an error, the demuxer still - * needs to be closed: the close() function must be - * called and one must wait for the - * demuxerCloseResponse() listener function to be called - * prior to destroying the demuxer. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param status: 0 on success, negative errno value in - * case of error - */ - virtual void - demuxerOpenResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status) = 0; - - /** - * Close response function, called when a close - * operation is complete or has failed. The status - * parameter is the close operation status: 0 on - * success, or a negative errno value in case of error. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param status: 0 on success, negative errno value in - * case of error - */ - virtual void - demuxerCloseResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status) = 0; - - /** - * Unrecoverable error function, called when a - * previously opened demuxer is no longer running. - * is called, the demuxer is no longer running; - * the close() function must be called and one must wait - * for the demuxerCloseResponse() listener function to - * be called prior to destroying the demuxer. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - */ - virtual void onDemuxerUnrecoverableError( - IPdraw *pdraw, - IPdraw::IDemuxer *demuxer) = 0; - - /** - * Demuxer media selection function, called with a list - * of video medias found from which the application must - * choose one or more to process in the pipeline. The - * return value of the function must be a bitfield of - * the identifiers of the chosen medias (from the - * pdraw_demuxer_media structure), or 0 to choose the - * default medias. If the return value is -ENOSYS, the - * callback is considered not implemented and the - * default medias are chosen. If the return value is - * -ECANCELED no media is chosen and the open operation - * is aborted. If the return value is another negative - * errno or an invalid bitfield the - * demuxerOpenResponse() function will be called if an - * open operation is in progress, or the - * onDemuxerUnrecoverableError() function otherwise. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param medias: array of demuxer media - * @param count: demuxer media array element count - * @return a bitfield of the identifiers of the chosen - * medias, 0 or -ENOSYS to choose the default - * medias, -ECANCELED to choose no media and - * abort the open operation, or another negative - * errno value in case of error - */ - virtual int demuxerSelectMedia( - IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - const struct pdraw_demuxer_media *medias, - size_t count) = 0; - - /** - * Ready to play function, called when the playback is - * ready to start. This function is called to indicate - * that the demuxer is ready to process play operations. - * Generally the demuxer is ready to play as soon as the - * demuxerOpenResponse() function has been called with a - * success status. One case when the open operation was - * successful and the playback is not ready is when - * connected to a SkyController's RTSP server but the - * SkyController itself is not yet connected to a drone. - * Similarly, when connected to a drone's stream through - * a SkyController's RTSP server, if the drone is - * disconnected from the SkyController, this function - * will be called with a false value in the ready - * parameter. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param ready: true if the session is ready to play, - * false otherwise - */ - virtual void - demuxerReadyToPlay(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - bool ready) = 0; - - /** - * End of range function, called when the playback is - * suspended after having reached the end of the - * playback duration. This function is only called for - * replays (either local or streamed), not for live - * streams. The timestamp parameter is the current play - * time in microseconds at the moment the playback is - * suspended. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param timestamp: current playback time in - * microseconds - */ - virtual void - onDemuxerEndOfRange(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - uint64_t timestamp) = 0; - - /** - * Play response function, called when a play operation - * is complete (the playback has started) or has failed. - * The status parameter is the play operation status: 0 - * on success, or a negative errno value in case of - * error. The timestamp parameter is the current play - * time in microseconds at the moment the playback is - * started. The speed parameter is the current playback - * speed; a negative value means playing backward. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param status: 0 on success, negative errno value - * in case of error - * @param timestamp: current playback time in - * microseconds - * @param speed: current playback speed, negative means - * backward - */ - virtual void - demuxerPlayResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp, - float speed) = 0; - - /** - * Pause response function, called when a pause - * operation is complete (the playback is suspended) or - * has failed. The status parameter is the pause - * operation status: 0 on success, or a negative errno - * value in case of error. The timestamp parameter is - * the current play time in microseconds at the moment - * the playback is paused. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param status: 0 on success, negative errno value - * in case of error - * @param timestamp: current playback time in - * microseconds - */ - virtual void - demuxerPauseResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp) = 0; - - /** - * Seek response function, called when a seek operation - * is complete or has failed. The status parameter is - * the seek operation status: 0 on success, or a - * negative errno value in case of error. The timestamp - * parameter is the current play time in microseconds - * after seeking. The speed parameter is the current - * playback speed; a negative value means playing - * backward. - * @param pdraw: PDrAW instance handle - * @param demuxer: demuxer handle - * @param status: 0 on success, negative errno value - * in case of error - * @param timestamp: current playback time in - * microseconds - * @param speed: current playback speed, negative means - * backward - */ - virtual void - demuxerSeekResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp, - float speed) = 0; - }; - - /** - * Destroy a demuxer. - * This function stops a running demuxer and frees the - * associated resources. - */ - virtual ~IDemuxer(void) {} - - /** - * Close a demuxer. - * This function closes a previously opened demuxer. The - * function returns before the actual closing is done. If the - * function returns 0, the demuxerCloseResponse() listener - * function will be called once the close is successful (0 - * status) or has failed (negative errno status). If the - * function returns a negative errno value (immediate failure), - * the demuxerCloseResponse() listener function will not be - * called. After a successful close, the demuxer must be - * destroyed. - * @return 0 on success, negative errno value in case of error - */ - virtual int close(void) = 0; - - /** - * Get the single stream local stream port. - * This function returns the local stream (RTP) port currently - * in use after a succesful open operation on a single stream - * (RTP/AVP) or an RTSP URL. If no open operation has been done, - * or if an open operation has been done on a mux channel, 0 is - * returned. This is useful when opening a single stream with - * null local ports to let PDrAW open sockets on any available - * port. - * @return the stream port on success, 0 in case of error - */ - virtual uint16_t getSingleStreamLocalStreamPort(void) = 0; - - /** - * Get the single stream local control port. - * This function returns the local control (RTCP) port currently - * in use after a succesful open operation on a single stream - * (RTP/AVP) or an RTSP URL. If no open operation has been done, - * or if an open operation has been done on a mux channel, 0 is - * returned. This is useful when opening a single stream with - * null local ports to let PDrAW open sockets on any available - * port. - * @return the stream port on success, 0 in case of error - */ - virtual uint16_t getSingleStreamLocalControlPort(void) = 0; - - /** - * Get the ready to play status. - * This function returns true if a successful open operation has - * been completed and if the playback is ready to start, false - * otherwise. One case when a successful open operation is - * complete but the playback is not ready is when connected to a - * SkyController's RTSP server but the SkyController itself is - * not yet connected to a drone. The value returned by this - * function is identical to the ready parameter passed to the - * readyToPlay() listener function when it is called. - * @return the ready to play status on success, false in case of - * error - */ - virtual bool isReadyToPlay(void) = 0; - - /** - * Get the pause status. - * This function returns true if the playback is currently - * paused, false otherwise. - * @return the pause status on success, false in case of error - */ - virtual bool isPaused(void) = 0; - - /** - * Play at the given speed. - * This function starts the playback of the video. The speed - * parameter is optional and defaults to 1.0. If the speed - * parameter is negative, the video is played backward. If the - * speed is greater than or equal to PDRAW_PLAY_SPEED_MAX, the - * speed is ignored and the video is played at the maximum speed - * achievable. If the speed is less than or equal to - * -PDRAW_PLAY_SPEED_MAX, the speed is ignored and the video is - * played backward at the maximum speed achievable. A 0.0 speed - * has the same effet as calling the pause() function. On a live - * stream, the speed parameter has no effect. The function - * returns before the actual operation is done. If the function - * returns 0, the demuxerPlayResponse() listener function will - * be called once the play is successful (0 status) or has - * failed (negative errno status). If the function returns a - * negative errno value (immediate failure), the - * demuxerPlayResponse() listener function will not be called. - * @param speed: playback speed (0.0 means pause, negative value - * means play backward) - * @return 0 on success, negative errno value in case of error - */ - virtual int play(float speed = 1.0f) = 0; - - /** - * Pause the playback. - * This function suspends the playback of the video. The session - * is not closed and the playback can be resumed using the - * play() function. The function returns before the actual - * operation is done. If the function returns 0, the - * demuxerPauseResponse() listener function will be called once - * the pause is successful (0 status) or has failed (negative - * errno status). If the function returns a negative errno value - * (immediate failure), the demuxerPauseResponse() listener - * function will not be called. - * @return 0 on success, negative errno value in case of error - */ - virtual int pause(void) = 0; - - /** - * Go to previous frame in frame-by-frame playback. - * This function plays the previous frame while the playback is - * paused. If the playback is not currently paused an error is - * returned. Frame-by-frame is only available on local replays - * (MP4 records). The function returns before the actual - * operation is done. If the function returns 0, the - * demuxerSeekResponse() listener function will be called once - * the seek is successful (0 status) or has failed (negative - * errno status). If the function returns a negative errno value - * (immediate failure), the demuxerSeekResponse() listener - * function will not be called. - * @return 0 on success, negative errno value in case of error - */ - virtual int previousFrame(void) = 0; - - /** - * Go to next frame in frame-by-frame playback. - * This function plays the next frame while the playback is - * paused. If the playback is not currently paused an error is - * returned. Frame-by-frame is only available on local replays - * (MP4 records). The function returns before the actual - * operation is done. If the function returns 0, the - * demuxerSeekResponse() listener function will be called once - * the seek is successful (0 status) or has failed (negative - * errno status). If the function returns a negative errno value - * (immediate failure), the demuxerSeekResponse() listener - * function will not be called. - * @return 0 on success, negative errno value in case of error - */ - virtual int nextFrame(void) = 0; - - /** - * Seek forward or backward. - * This function seeks forward (positive delta) or backward - * (negative delta). The delta parameter is in microseconds. - * When exact is false the seek is done to the nearest - * synchronization sample preceeding the delta, otherwise the - * seek is done to the sample nearest to the delta. Seeking is - * only available on replays (either local or streamed), not on - * live streams. The function returns before the actual - * operation is done. If the function returns 0, the - * demuxerSeekResponse() listener function will be called once - * the seek is successful (0 status) or has failed (negative - * errno status). If the function returns a negative errno value - * (immediate failure), the demuxerSeekResponse() listener - * function will not be called. - * @param delta: time delta in microseconds (positive or - * negative) - * @param exact: true means seek to the sample closest to the - * delta, false means seek to the nearest - * synchronization sample preceeding the delta - * @return 0 on success, negative errno value in case of error - */ - virtual int seek(int64_t delta, bool exact = false) = 0; - - /** - * Seek forward. - * This function seeks forward by delta microseconds. It has the - * same behavior as calling seek() with a positive delta. When - * exact is false the seek is done to the nearest - * synchronization sample preceeding the delta, otherwise the - * seek is done to the sample nearest to the delta. Seeking is - * only available on replays (either local or streamed), not on - * live streams. The function returns before the actual - * operation is done. If the function returns 0, the - * demuxerSeekResponse() listener function will be called once - * the seek is successful (0 status) or has failed (negative - * errno status). If the function returns a negative errno value - * (immediate failure), the demuxerSeekResponse() listener - * function will not be called. - * @param delta: positive time delta forward in microseconds - * @param exact: true means seek to the sample closest to the - * delta, false means seek to the nearest - * synchronization sample preceeding the delta - * @return 0 on success, negative errno value in case of error - */ - virtual int seekForward(uint64_t delta, bool exact = false) = 0; - - /** - * Seek backward. - * This function seeks backward by delta microseconds (postive). - * It has the same behavior as calling seek() with a negative - * delta. When exact is false the seek is done to the nearest - * synchronization sample preceeding the delta, otherwise the - * seek is done to the sample nearest to the delta. Seeking is - * only available on replays (either local or streamed), not on - * live streams. The function returns before the actual - * operation is done. If the function returns 0, the - * demuxerSeekResponse() listener function will be called once - * the seek is successful (0 status) or has failed (negative - * errno status). If the function returns a negative errno value - * (immediate failure), the demuxerSeekResponse() listener - * function will not be called. - * @param delta: positive time delta backward in microseconds - * @param exact: true means seek to the sample closest to the - * delta, false means seek to the nearest - * synchronization sample preceeding the delta - * @return 0 on success, negative errno value in case of error - */ - virtual int seekBack(uint64_t delta, bool exact = false) = 0; - - /** - * Seek to a given time. - * This function seeks to the given play timestamp in - * microseconds. When exact is false the seek is done to the - * nearest synchronization sample preceeding the timestamp, - * otherwise the seek is done to the sample nearest to the - * timestamp. Seeking is only available on replays (either local - * or streamed), not on live streams. The function returns - * before the actual operation is done. If the function returns - * 0, the demuxerSeekResponse() listener function will be called - * once the seek is successful (0 status) or has failed - * (negative errno status). If the function returns a negative - * errno value (immediate failure), the demuxerSeekResponse() - * listener function will not be called. - * @param timestamp: play timestamp in microseconds - * @param exact: true means seek to the sample closest to the - * timestamp, false means seek to the nearest - * synchronization sample preceeding the timestamp - * @return 0 on success, negative errno value in case of error - */ - virtual int seekTo(uint64_t timestamp, bool exact = false) = 0; - - /** - * Get the playback duration. - * This function returns the playback duration in microseconds. - * The duration is only available on replays (either local or - * streamed), not on live streams. - * @return the duration in microseconds on success, - * 0 in case of error - */ - virtual uint64_t getDuration(void) = 0; - - /** - * Get the playback current time. - * This function returns the current playback position in - * microseconds. On replays (either local or streamed) this is - * the position between 0 and the duration; on live streams this - * is the time since the start of the stream session. - * @return the current time in microseconds on success, - * 0 in case of error - */ - virtual uint64_t getCurrentTime(void) = 0; - }; - - /** - * Create a demuxer on a URL (stream or local file). - * The URL can be either an RTSP URL (starting with "rtsp://") or a - * local file path (either absolute or relative). - * The function returns before the actual opening is done. If the - * function returns 0, the demuxerOpenResponse() listener function will - * be called once the open operation is successful (0 status) or has - * failed (negative errno status). If the function returns a negative - * errno value (immediate failure), the demuxerOpenResponse() listener - * function will not be called. Once a demuxer is no longer used, it - * must be closed and then destroyed (@see the IDemuxer::close() - * function). - * @param url: URL of the resource to open - * @param listener: demuxer listener functions implementation - * @param retObj: demuxer object pointer (output) - * @return 0 on success, negative errno value in case of error - */ - virtual int createDemuxer(const std::string &url, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj) = 0; - - /** - * Create a demuxer on a single stream. - * This function opens an RTP/AVP stream. No session management is done: - * it is the application's responsibility to handle the ports - * negociation with the sender. If null local ports are given as - * parameter, the effective port numbers used can be retrieved using the - * getSingleStreamLocalStreamPort() for the RTP port and - * getSingleStreamLocalControlPort() for the RTCP port functions. - * If the localAddr parameter is left empty, any local network interface - * will be used. The remoteAddr, remoteStreamPort and remoteControlPort - * parameters can be left null/empty if unknown; they will be known once - * the stream is being received. - * The function returns before the actual opening is done. If the - * function returns 0, the demuxerOpenResponse() listener function will - * be called once the open operation is successful (0 status) or has - * failed (negative errno status). If the function returns a negative - * errno value (immediate failure), the demuxerOpenResponse() listener - * function will not be called. Once a demuxer is no longer used, it - * must be closed and then destroyed (@see the IDemuxer::close() - * function). - * @param localAddr: local IP address (optional, can be empty) - * @param localStreamPort: local stream (RTP) port (optional, can be 0) - * @param localControlPort: local control (RTCP) port (optional, - * can be 0) - * @param remoteAddr: remote IP address (optional, can be empty) - * @param remoteStreamPort: remote stream (RTP) port (optional, - * can be 0) - * @param remoteControlPort: remote control (RTCP) port (optional, - * can be 0) - * @param listener: demuxer listener functions implementation - * @param retObj: demuxer object pointer (output) - * @return 0 on success, negative errno value in case of error - */ - virtual int createDemuxer(const std::string &localAddr, - uint16_t localStreamPort, - uint16_t localControlPort, - const std::string &remoteAddr, - uint16_t remoteStreamPort, - uint16_t remoteControlPort, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj) = 0; - - /** - * Create a demuxer on a stream URL through a mux channel. - * The URL must be an RTSP URL (starting with "rtsp://"). Mux channels - * are used to transfer data between a SkyController remote and a - * smartphone through USB; see Parrot's libmux for more information. - * No concurrent sessions can run on the mux channel; therefore the user - * must take care of limiting the number of PDrAW instances and demuxer - * objects running on the mux channel to only one. - * The function returns before the actual opening is done. If the - * function returns 0, the demuxerOpenResponse() listener function will - * be called once the open operation is successful (0 status) or has - * failed (negative errno status). If the function returns a negative - * errno value (immediate failure), the demuxerOpenResponse() listener - * function will not be called. Once a demuxer is no longer used, it - * must be closed and then destroyed (@see the IDemuxer::close() - * function). - * @param url: URL of the resource to open - * @param mux: mux instance handle - * @param listener: demuxer listener functions implementation - * @param retObj: demuxer object pointer (output) - * @return 0 on success, negative errno value in case of error - */ - virtual int createDemuxer(const std::string &url, - struct mux_ctx *mux, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj) = 0; - - - /** - * Muxer API - * @see the createMuxer() function for the muxer object creation - */ - class IMuxer { - public: - /** - * Destroy a muxer. - * This function stops a running muxer and frees the - * associated resources. - */ - virtual ~IMuxer(void) {} - - /** - * Add a media to a muxer. - * This function adds a media to the muxer by its mediaId. - * The media idenfifiers are known when the onMediaAdded() or - * onMediaRemoved() general listener functions are called. - * The params structure is only relevant for video medias; - * the structure must then be provided but all parameters are - * optional and can be left null. - * @param mediaId: identifier of the media to add to the muxer - * @param params: muxer video media parameters - * @return 0 on success, negative errno value in case of error - */ - virtual int addMedia(unsigned int mediaId, - const struct pdraw_muxer_video_media_params - *params) = 0; - }; - - /** - * Create a muxer (experimental). - * This function creates a muxer with a given URL. - * Once the muxer is created medias can be added by id using the - * addMedia() function. Once a muxer is no longer used, it must be - * destroyed. If writing to an MP4 file, the file is finalized in - * the destructor. - * @note: experimental only, the function returns -ENOSYS - * @param url: destination URL - * @param retObj: muxer object pointer (output) - * @return 0 on success, negative errno value in case of error - */ - virtual int createMuxer(const std::string &url, - IPdraw::IMuxer **retObj) = 0; - - - /** - * Video renderer API - * @warning all functions must be called from the application's - * rendering thread - * @see the createVideoRenderer() function for the video renderer - * object creation - */ - class IVideoRenderer { - public: - /* Video renderer listener object */ - class Listener { - public: - /** - * Video renderer listener object destructor. - */ - virtual ~Listener(void) {} - - /** - * Media added function, called when a media has been - * added internally to the renderer. Medias are raw - * video medias. This function is called from the - * pomp_loop thread. - * @param pdraw: PDrAW instance handle - * @param renderer: renderer handle - * @param info: pointer on the media information - */ - virtual void onVideoRendererMediaAdded( - IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer, - const struct pdraw_media_info *info) = 0; - - /** - * Media removed function, called when a media has been - * removed internally from the renderer. Medias are raw - * video medias. This function is called from the - * pomp_loop thread. - * @param pdraw: PDrAW instance handle - * @param renderer: renderer handle - * @param info: pointer on the media information - */ - virtual void onVideoRendererMediaRemoved( - IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer, - const struct pdraw_media_info *info) = 0; - - /** - * Render ready function, called both when a new frame - * is ready for rendering and periodically. This - * function is called from the pomp_loop thread. - * @param pdraw: PDrAW instance handle - * @param renderer: renderer handle - */ - virtual void onVideoRenderReady( - IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer) = 0; - - /** - * External texture loading function. This function is - * called before the rendering of the video frame in - * order to override the frame loading as a texture. - * This can be used to transform the video frames - * outside of PDrAW before resuming the rendering. This - * function is called from the rendering thread. If no - * implementation of this function is required by the - * application, -ENOSYS must be returned (before - * checking input values, so that implementation can be - * tested with all arguments null). - * @param pdraw: PDrAW instance handle - * @param renderer: renderer handle - * @param textureWidth: texture width in pixels - * @param textureHeight: texture height in pixels - * @param mediaInfo: media information - * @param frame: frame information - * @param frameUserdata: frame user data buffer - * @param frameUserdataLen: frame user data buffer size - * in bytes - * @return 0 on success, -ENOSYS if not implemented, - * or another negative errno value in case of - * error - */ - virtual int loadVideoTexture( - IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer, - unsigned int textureWidth, - unsigned int textureHeight, - const struct pdraw_media_info *mediaInfo, - struct mbuf_raw_video_frame *frame, - const void *frameUserdata, - size_t frameUserdataLen) = 0; - - /** - * Overlay rendering function. This function is called - * after the rendering of the video frame (if one is - * available) in order to render an application overlay - * on top of the video. When HMD distorsion correction - * is enabled in the renderer, it is applied after the - * overlay rendering. When no frame is available for - * the rendering, the frameMeta and frameExtra - * parameters are null. This function is called from - * the rendering thread. If no implementation of this - * function is required by the application, -ENOSYS - * must be returned (before checking input values, so - * that implementation can be tested with all arguments - * null). - * @param pdraw: PDrAW instance handle - * @param renderer: renderer handle - * @param renderPos: rendering position - * @param contentPos: video content position - * @param viewMat: 4x4 view matrix - * @param projMat: 4x4 projection matrix - * @param mediaInfo: media information - * @param frameMeta: frame metadata (optional, - * can be null) - * @param frameExtra: frame extra information - * (optional, can be null) - * @return 0 on success, -ENOSYS if not implemented, - * or another negative errno value in case of - * error - */ - virtual int renderVideoOverlay( - IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer, - const struct pdraw_rect *renderPos, - const struct pdraw_rect *contentPos, - const float *viewMat, - const float *projMat, - const struct pdraw_media_info *mediaInfo, - struct vmeta_frame *frameMeta, - const struct pdraw_video_frame_extra - *frameExtra) = 0; - }; - - /** - * Destroy a video renderer. - * This function stops a running video renderer and frees the - * associated resources. - * @warning this function must be called from the application's - * rendering thread. - */ - virtual ~IVideoRenderer(void) {} - - /** - * Resize a video renderer. - * This function updates the rendering position and size on a - * running video renderer. The render_pos parameter sets the - * position of the rendering in the window/view; these - * coordinates are in pixels from the bottom-left corner (OpenGL - * coordinates). - * @warning this function must be called from the application's - * rendering thread. - * @param renderPos: rendering position and size - * @return 0 on success, negative errno value in case of error - */ - virtual int resize(const struct pdraw_rect *renderPos) = 0; - - /** - * Set the video renderer media identifier. - * This function updates the identifier of the media on which - * the rendering is done; if the media id is zero the first raw - * media encountered is used. - * @warning this function must be called from the application's - * rendering thread. - * @param mediaId: identifier of the raw media to render (from - * a pdraw_media_info structure); if zero the - * first raw media found is used for rendering - * @return 0 on success, negative errno value in case of error - */ - virtual int setMediaId(unsigned int mediaId) = 0; - - /** - * Get the video renderer media identifier. - * This function retrieves the identifier of the media on which - * the rendering is done. - * @warning this function must be called from the application's - * rendering thread. - * @return the identifier of the media on success, - * 0 if no media is being renderered or in case of error - */ - virtual unsigned int getMediaId(void) = 0; - - /** - * Set the video renderer parameters. - * This function updates the rendering parameters on a running - * video renderer. The params structure must be provided but all - * parameters are optional and can be left null. - * @warning this function must be called from the application's - * rendering thread. - * @param params: renderer parameters - * @return 0 on success, negative errno value in case of error - */ - virtual int - setParams(const struct pdraw_video_renderer_params *params) = 0; - - /** - * Get the video renderer parameters. - * This function retrieves the rendering parameters on a running - * video renderer. The provided params structure is filled by - * the function. - * @warning this function must be called from the application's - * rendering thread. - * @param params: renderer parameters (output) - * @return 0 on success, negative errno value in case of error - */ - virtual int - getParams(struct pdraw_video_renderer_params *params) = 0; - - /** - * Render the video. - * This function renders the video with the current rendering - * position and rendering parameters, and optionally with the - * provided view and projection matrices. The viewMat and - * projMat matrices are 4x4 OpenGL matrices. For - * render-on-demand, consider using the onVideoRenderReady() - * listener function which is called both when a new frame is - * ready for rendering and periodically. Note that the - * onVideoRenderReady() listener function is called from the - * pomp_loop thread, not the rendering thread; the - * synchronization is up to the caller. If a contentPos - * structure is provided, it is filled with the actual position - * and size of the video within the rendering position; these - * coordinates are in pixels from the bottom-left corner (OpenGL - * coordinates). - * @warning this function must be called from the application's - * rendering thread. - * @param contentPos: video content position (output; - * optional, can be null) - * @param viewMat: 4x4 view matrix (optional, can be null) - * @param projMat: 4x4 projection matrix (optional, can be null) - * @return 0 on success, negative errno value in case of error - */ - virtual int render(struct pdraw_rect *contentPos, - const float *viewMat = nullptr, - const float *projMat = nullptr) = 0; - }; - - /** - * Create a video renderer. - * This function creates a video renderer on a media of the given media - * id; if the media id is zero the first raw media encountered is used. - * Optionally an active EGL display context can be provided. Once the - * renderer is created, the rendering is done by calling the render() - * function. Once a renderer is no longer used it must be - * destroyed. The renderPos parameter sets the position and size - * of the rendering in the window/view; these coordinates are in - * pixels from the bottom-left corner (OpenGL coordinates). The - * params structure must be provided but all parameters are - * optional and can be left null. A valid listener must be - * provided and all functions must be implemented; the - * loadVideoTexture() and renderVideoOverlay() are optional and - * can return -ENOSYS if no real implementation is provided. All - * listener functions are called from the rendering thread, - * except the onVideoRenderReady() function which is called from - * the pomp_loop thread. - * @warning this function must be called from the application's - * rendering thread. - * @param mediaId: identifier of the raw media to render (from a - * pdraw_media_info structure); if zero the first - * raw media found is used for rendering - * @param renderPos: rendering position and size - * @param params: renderer parameters - * @param listener: renderer listener functions implementation - * @param retObj: renderer object pointer (output) - * @param eglDisplay: EGL display context - * @return 0 on success, negative errno value in case of error - */ - virtual int - createVideoRenderer(unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - IPdraw::IVideoRenderer::Listener *listener, - IPdraw::IVideoRenderer **retObj, - struct egl_display *eglDisplay = nullptr) = 0; - - - /** - * Coded video sink API - * @see the createCodedVideoSink() function for the coded video sink - * object creation - */ - class ICodedVideoSink { - public: - /* Video sink listener object */ - class Listener { - public: - /** - * Coded video sink listener object destructor. - */ - virtual ~Listener(void) {} - - /** - * Coded video sink flush function, called when flushing - * is required. When this function is called, the - * application must flush the sink queue by calling - * mbuf_coded_video_frame_queue_flush() and must return - * all frames outside of the queue by calling - * mbuf_coded_frame_unref(); once the flushing is done, - * the queueFlushed() function must be called. - * @param pdraw: PDrAW instance handle - * @param sink: coded video sink handle - */ - virtual void onCodedVideoSinkFlush( - IPdraw *pdraw, - IPdraw::ICodedVideoSink *sink) = 0; - }; - - /** - * Destroy a coded video sink. - * This function stops a running coded video sink and frees the - * associated resources. - */ - virtual ~ICodedVideoSink(void) {} - - /** - * Resynchronize a coded video sink. - * This function schedules the output of a synchronization frame - * (IDR) for a running coded video sink. - * It can be used for example in case of unrecoverable video - * decoder errors to restart decoding. After a coded video sink - * creation, the first frame that is output is always a - * synchronization frame; therefore it is not necessary to call - * this function immediately after a video sink creation. - * @return 0 on success, negative errno value in case of error - */ - virtual int resync(void) = 0; - - /** - * Get the coded video sink frame queue. - * This function returns the frame queue to use in order to - * retrieve frames from a running coded video sink. Frames are - * retrieved from the queue by using the - * mbuf_coded_video_frame_queue_pop() function. - * @return a pointer on a mbuf_coded_video_frame_queue object on - * success, nullptr in case of error - */ - virtual struct mbuf_coded_video_frame_queue *getQueue(void) = 0; - - /** - * Signal that a coded video sink has been flushed. - * This function is used to signal that flushing is complete. - * When the onCodedVideoSinkFlush() video sink listener function - * is called, the application must flush the sink queue by - * calling mbuf_coded_video_frame_queue_flush() and must return - * all frames outside of the queue by calling - * mbuf_coded_video_frame_unref(); once the flushing is - * complete, this function must be called. - * @return 0 on success, negative errno value in case of error - */ - virtual int queueFlushed(void) = 0; - }; - - - /** - * Raw video sink API - * @see the createRawVideoSink() function for the raw video sink - * object creation - */ - class IRawVideoSink { - public: - /* Video sink listener object */ - class Listener { - public: - /** - * Raw video sink listener object destructor. - */ - virtual ~Listener(void) {} - - /** - * Raw video sink flush function, called when flushing - * is required. When this function is called, the - * application must flush the sink queue by calling - * mbuf_raw_video_frame_queue_flush() and must return - * all frames outside of the queue by calling - * mbuf_raw_frame_unref(); once the flushing is done, - * the queueFlushed() function must be called. - * @param pdraw: PDrAW instance handle - * @param sink: raw video sink handle - */ - virtual void - onRawVideoSinkFlush(IPdraw *pdraw, - IPdraw::IRawVideoSink *sink) = 0; - }; - - /** - * Destroy a raw video sink. - * This function stops a running raw video sink and frees the - * associated resources. - */ - virtual ~IRawVideoSink(void) {} - - /** - * Get the raw video sink frame queue. - * This function returns the frame queue to use in order to - * retrieve frames from a running raw video sink. Frames are - * retrieved from the queue by using the - * mbuf_raw_video_frame_queue_pop() function. - * @return a pointer on a mbuf_raw_video_frame_queue object on - * success, nullptr in case of error - */ - virtual struct mbuf_raw_video_frame_queue *getQueue(void) = 0; - - /** - * Signal that a raw video sink has been flushed. - * This function is used to signal that flushing is complete. - * When the onRawVideoSinkFlush() video sink listener function - * is called, the application must flush the sink queue by - * calling mbuf_raw_video_frame_queue_flush() and must return - * all frames outside of the queue by calling - * mbuf_raw_video_frame_unref(); once the flushing is - * complete, this function must be called. - * @return 0 on success, negative errno value in case of error - */ - virtual int queueFlushed(void) = 0; - }; - - - /** - * Create a coded video sink. - * This function creates a video sink on a media of the given mediaId. - * The media idenfifiers are known when the onMediaAdded() or - * onMediaRemoved() general listener functions are called. Once the - * sink is created, video frames are retrieved by getting them from - * the frame queue returned by the getQueue() function. Once a video - * sink is no longer used, it must be destroyed. - * The params structure must be provided but all parameters are optional - * and can be left null. The listener must be provided and the - * onCodedVideoSinkFlush() function is required to be implemented; all - * listener functions are called from the pomp_loop thread. When the - * onCodedVideoSinkFlush() function is called, the application must - * flush the sink queue by calling mbuf_coded_video_frame_queue_flush() - * and must return all frames outside of the queue by calling - * mbuf_coded_video_frame_unref(); once the flushing is complete, the - * queueFlushed() function must be called. - * - * @note mediaId must refer to a coded video media. - * - * @param mediaId: identifier of the media on which to create the sink - * @param params: video sink parameters - * @param listener: video sink listener functions implementation - * @param retObj: video sink object pointer (output) - * @return 0 on success, negative errno value in case of error - */ - virtual int - createCodedVideoSink(unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::ICodedVideoSink::Listener *listener, - IPdraw::ICodedVideoSink **retObj) = 0; - - /** - * Create a raw video sink. - * This function creates a video sink on a media of the given mediaId. - * The media idenfifiers are known when the onMediaAdded() or - * onMediaRemoved() general listener functions are called. Once the - * sink is created, video frames are retrieved by getting them from - * the frame queue returned by the getQueue() function. Once a video - * sink is no longer used, it must be destroyed. - * The params structure must be provided but all parameters are optional - * and can be left null. The listener must be provided and the - * onRawVideoSinkFlush() function is required to be implemented; all - * listener functions are called from the pomp_loop thread. When the - * onRawVideoSinkFlush() function is called, the application must - * flush the sink queue by calling mbuf_raw_video_frame_queue_flush() - * and must return all frames outside of the queue by calling - * mbuf_raw_video_frame_unref(); once the flushing is complete, the - * queueFlushed() function must be called. - * - * @note mediaId must refer to a raw video media. - * - * @param mediaId: identifier of the media on which to create the sink - * @param params: video sink parameters - * @param listener: video sink listener functions implementation - * @param retObj: video sink object pointer (output) - * @return 0 on success, negative errno value in case of error - */ - virtual int - createRawVideoSink(unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::IRawVideoSink::Listener *listener, - IPdraw::IRawVideoSink **retObj) = 0; - - - /** - * Settings API - */ - - /** - * Get the PDrAW instance friendly name. - * This function fills the friendlyName string with the friendly name. - * The string must have been previously allocated. The friendly name is - * generally either the device's friendly name (e.g. "Bob's phone") or - * the application's name (e.g. "MyDroneControllerApp"). It is used for - * example in metadata exchanged with a streaming server. - * @param friendlyName: pointer to the string to write to (output) - */ - virtual void getFriendlyNameSetting(std::string *friendlyName) = 0; - - /** - * Set the PDrAW instance friendly name. - * The friendlyName string is copied internally. The friendly name is - * generally either the device's friendly name (e.g. "Bob's phone") or - * the application's name (e.g. "MyDroneControllerApp"). It is used for - * example in metadata exchanged with a streaming server. Setting the - * friendly name value is optional. - * @param friendlyName: friendly name string - */ - virtual void - setFriendlyNameSetting(const std::string &friendlyName) = 0; - - /** - * Get the PDrAW instance serial number. - * This function fills the serialNumber string with the serial number. - * The string must have been previously allocated. The serial number is - * generally the unique serial number of the device on which PDrAW is - * running. It is used for example in metadata exchanged with a - * streaming server. - * @param serialNumber: pointer to the string to write to (output) - */ - virtual void getSerialNumberSetting(std::string *serialNumber) = 0; - - /** - * Set the PDrAW instance serial number. - * The serialNumber string is copied internally. The serial number is - * generally the unique serial number of the device on which PDrAW is - * running. It is used for example in metadata exchanged with a - * streaming server. Setting the serial number value is recommended as - * it is used as a unique identifier in the streaming protocols. - * @param serialNumber: serial number string - */ - virtual void - setSerialNumberSetting(const std::string &serialNumber) = 0; - - /** - * Get the PDrAW instance software version. - * This function fills the softwareVersion string with the software - * version. The string must have been previously allocated. The software - * version is generally the version number of the application running - * PDrAW (e.g. "MyApp v1.2.3"). It is used for example in metadata - * exchanged with a streaming server. - * @param softwareVersion: pointer to the string to write to (output) - */ - virtual void - getSoftwareVersionSetting(std::string *softwareVersion) = 0; - - /** - * Set the PDrAW instance software version. - * The softwareVersion string is copied internally. The software version - * is generally the version number of the application running PDrAW - * (e.g. "MyApp v1.2.3"). It is used for example in metadata exchanged - * with a streaming server. Setting the software version value is - * optional. - * @param softwareVersion: software version string - */ - virtual void - setSoftwareVersionSetting(const std::string &softwareVersion) = 0; - - /** - * Get the pipeline mode setting. - * This function returns the pipeline mode of a PDrAW instance. The - * pipeline mode controls whether to decode the selected video media - * (for full processing up to the rendering), or to disable video - * decoding (e.g. when no rendering is required, only a coded video - * sink). - * @return the pipeline mode, or PDRAW_PIPELINE_MODE_DECODE_ALL - * in case of error - */ - virtual enum pdraw_pipeline_mode getPipelineModeSetting(void) = 0; - - /** - * Set the pipeline mode setting. - * This function sets the pipeline mode of a PDrAW instance. This - * function can be called only prior to any open operation. The pipeline - * mode controls whether to decode the selected video media (for full - * processing up to the rendering), or to disable video decoding (e.g. - * when no rendering is required, only a coded video sink). - * @param mode: pipeline mode - */ - virtual void setPipelineModeSetting(enum pdraw_pipeline_mode mode) = 0; - - /** - * Get the display screen settings. - * This function returns the display screen settings through the xdpi, - * ydpi and deviceMargin* parameters. This is only useful if HMD - * distortion correction is enabled in the video rendering. The xdpi - * and ydpi are pixel densities in dots per inches. The device margins - * are in millimeters. - * @param xdpi: horizontal pixel density (output) - * @param ydpi: vertical pixel density (output) - * @param deviceMarginTop: top device margin (output) - * @param deviceMarginBottom: bottom device margin (output) - * @param deviceMarginLeft: left device margin (output) - * @param deviceMarginRight: right device margin (output) - */ - virtual void getDisplayScreenSettings(float *xdpi, - float *ydpi, - float *deviceMarginTop, - float *deviceMarginBottom, - float *deviceMarginLeft, - float *deviceMarginRight) = 0; - - /** - * Set the display screen settings. - * This function sets the display screen settings. This is only useful - * if HMD distortion correction is enabled in the video rendering. - * The xdpi and ydpi are pixel densities in dots per inches. The device - * margins are in millimeters. - * @param xdpi: horizontal pixel density - * @param ydpi: vertical pixel density - * @param deviceMarginTop: top device margin - * @param deviceMarginBottom: bottom device margin - * @param deviceMarginLeft: left device margin - * @param deviceMarginRight: right device margin - */ - virtual void setDisplayScreenSettings(float xdpi, - float ydpi, - float deviceMarginTop, - float deviceMarginBottom, - float deviceMarginLeft, - float deviceMarginRight) = 0; - - /** - * Get the HMD model setting. - * This function returns the head-mounted display (HMD) model. This is - * only useful if HMD distortion correction is enabled in the video - * rendering. - * @return the HMD model, or PDRAW_HMD_MODEL_UNKNOWN in case of error - */ - virtual enum pdraw_hmd_model getHmdModelSetting(void) = 0; - - /** - * Set the HMD model setting. - * This function sets the head-mounted display (HMD) model. This is - * only useful if HMD distortion correction is enabled in the video - * rendering. - * @param hmdModel: HMD model - */ - virtual void setHmdModelSetting(enum pdraw_hmd_model hmdModel) = 0; - - - /** - * Platform-specific API - */ - - /** - * Set the Android JVM pointer. - * This function sets the JVM pointer for internal calls to the Android - * SDK API. This is only useful on Android and is ignored on other - * platforms. If the JVM pointer is not provided on Android platforms, - * some features may not be available. - * @param jvm: JVM pointer - */ - virtual void setAndroidJvm(void *jvm) = 0; - - - /** - * Debug API - */ - - /** - * Dump the current pipeline as a directed graph using the DOT file - * format. - * @param fileName: DOT file to write to - * @return 0 on success, negative errno value in case of error - */ - virtual int dumpPipeline(const std::string &fileName) = 0; -}; - - -/** - * Create a PDrAW instance. - * A running pomp_loop must be provided; all functions (with the exception of - * rendering functions) must be called from the pomp_loop thread. All listener - * functions are called from the pomp_loop thread; a valid listener must be - * provided. The instance handle is returned through the retObj parameter. - * When no longer needed, the instance must be deleted to free the resources. - * The stop() function must be called and one must wait for the stopResponse() - * listener function to be called prior to destroying the instance. - * @param loop: pomp_loop to use - * @param listener: listener functions implementation - * @param retObj: PDrAW instance handle (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int createPdraw(struct pomp_loop *loop, - IPdraw::Listener *listener, - IPdraw **retObj); - - -/** - * Helpers - */ - -/** - * ToString function for enum pdraw_hmd_model. - * @param val: HMD model value to convert - * @return a string description of the HMD model - */ -PDRAW_API const char *pdrawHmdModelStr(enum pdraw_hmd_model val); - - -/** - * ToString function for enum pdraw_pipeline_mode. - * @param val: pipeline mode value to convert - * @return a string description of the pipeline mode - */ -PDRAW_API const char *pdrawPipelineModeStr(enum pdraw_pipeline_mode val); - - -/** - * ToString function for enum pdraw_playback_type. - * @param val: playback type value to convert - * @return a string description of the playback type - */ -PDRAW_API const char *pdrawPlaybackTypeStr(enum pdraw_playback_type val); - - -/** - * ToString function for enum pdraw_media_type. - * @param val: media type value to convert - * @return a string description of the media type - */ -PDRAW_API const char *pdrawMediaTypeStr(enum pdraw_media_type val); - - -/** - * ToString function for enum pdraw_video_type. - * @param val: video type value to convert - * @return a string description of the video type - */ -PDRAW_API const char *pdrawVideoTypeStr(enum pdraw_video_type val); - - -/** - * ToString function for enum pdraw_histogram_channel. - * @param val: histogram channel value to convert - * @return a string description of the histogram channel - */ -PDRAW_API const char * -pdrawHistogramChannelStr(enum pdraw_histogram_channel val); - - -/** - * ToString function for enum pdraw_video_renderer_scheduling_mode. - * @param val: video renderer scheduling mode value to convert - * @return a string description of the video renderer scheduling mode - */ -PDRAW_API const char *pdrawVideoRendererSchedulingModeStr( - enum pdraw_video_renderer_scheduling_mode val); - - -/** - * ToString function for enum pdraw_video_renderer_fill_mode. - * @param val: video renderer fill mode value to convert - * @return a string description of the video renderer fill mode - */ -PDRAW_API const char * -pdrawVideoRendererFillModeStr(enum pdraw_video_renderer_fill_mode val); - - -/** - * ToString function for enum pdraw_video_renderer_transition_flag. - * @param val: video renderer transition flag value to convert - * @return a string description of the video renderer transition flag - */ -PDRAW_API const char *pdrawVideoRendererTransitionFlagStr( - enum pdraw_video_renderer_transition_flag val); - - -/** - * Convert a video frame metadata structure to a JSON object. - * The JSON object must have been previously created. The function appends - * new elements in this object. - * If `metadata` is not null, its own json dump will be added to the frame dump. - * @param frame: pointer to a video frame structure - * @param metadata: optional pointer to the frame metadata - * @param jobj: pointer to a JSON object to fill (output) - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdrawVideoFrameToJson(const struct pdraw_video_frame *frame, - struct vmeta_frame *metadata, - struct json_object *jobj); - - -/** - * Convert a video frame metadata structure to a JSON string. - * This function fills the str array with the null-terminated JSON string. - * The string must have been previously allocated. The function writes - * up to len characters. - * If `metadata` is not null, its own json dump will be added to the frame dump. - * @param frame: pointer to a video frame structure - * @param metadata: optional pointer to the frame metadata - * @param str: pointer to the string to write to (output) - * @param len: maximum length of the string - * @return 0 on success, negative errno value in case of error - */ -PDRAW_API int pdrawVideoFrameToJsonStr(const struct pdraw_video_frame *frame, - struct vmeta_frame *metadata, - char *str, - unsigned int len); - - -/** - * Duplicate a media_info structure. - * @param src: pointer to the media_info structure to duplicate - * @return a pointer to the newly allocated structure or NULL on error. - */ -PDRAW_API struct pdraw_media_info * -pdrawMediaInfoDup(const struct pdraw_media_info *src); - - -/** - * Free a media_info structure. - * @param media_info: pointer to the media_info structure to free - */ -PDRAW_API void pdrawMediaInfoFree(struct pdraw_media_info *media_info); - - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_HPP_ +#define _PDRAW_HPP_ + +#include + +#include + +#include "pdraw_defs.h" + +namespace Pdraw { + + +/* PDrAW object interface; + * see the createPdraw() function for creating a PDrAW instance */ +class IPdraw { +public: + /* PDrAW listener object */ + class Listener { + public: + /** + * PDrAW listener object destructor. + */ + virtual ~Listener(void) {} + + /** + * Stop response function, called when a stop operation is + * complete or has failed. The status parameter is the stop + * operation status: 0 on success, or a negative errno value + * in case of error. + * @param pdraw: PDrAW instance handle + * @param status: 0 on success, negative errno value in case + * of error + */ + virtual void stopResponse(IPdraw *pdraw, int status) = 0; + + /** + * Media added function, called when a media has been added + * internally in the PDrAW pipeline. Medias are for example + * raw or coded video medias. The info structure gives the + * media identifier that can be used for example to create a + * video sink on this media. + * @param pdraw: PDrAW instance handle + * @param info: pointer on the media information + */ + virtual void + onMediaAdded(IPdraw *pdraw, + const struct pdraw_media_info *info) = 0; + + /** + * Media removed function, called when a media has been removed + * internally from the PDrAW pipeline. Medias are for example + * raw or coded video medias. When a media is removed, any + * video sink created on this media must then be stopped. + * @param pdraw: PDrAW instance handle + * @param info: pointer on the media information + */ + virtual void + onMediaRemoved(IPdraw *pdraw, + const struct pdraw_media_info *info) = 0; + + /** + * Socket creation function, called immediately after a socket + * creation with its file descriptor as parameter. + * @param pdraw: PDrAW instance handle + * @param fd: socket file descriptor + */ + virtual void onSocketCreated(IPdraw *pdraw, int fd) = 0; + }; + + + /** + * Instance management API + */ + + /** + * Destroy a PDrAW instance. + * This function frees all resources associated with a PDrAW instance. + * The stop() function must be called prior to destroying and one must + * wait for the stopResponse() listener function to be called prior to + * destroying the PDrAW instance. + */ + virtual ~IPdraw(void) {} + + /** + * Stop a session. + * This function stops a session and all the associated objects. + * The function returns before the actual stopping is done. If the + * function returns 0, the stopResponse() listener function will be + * called once the stopping is successful (0 status) or has failed + * (negative errno status). If the function returns a negative errno + * value (immediate failure), the stopResponse() listener function + * will not be called. After a successful stop, the PDrAW instance + * must be destroyed. + * @return 0 on success, negative errno value in case of error + */ + virtual int stop(void) = 0; + + + /** + * Demuxer API + * @see the createDemuxer() function for the demuxer object creation + */ + class IDemuxer { + public: + /* Demuxer listener object */ + class Listener { + public: + /** + * Demuxer listener object destructor. + */ + virtual ~Listener(void) {} + + /** + * Open response function, called when an open operation + * is complete or has failed. The status parameter is + * the open operation status: 0 on success, or a + * negative errno value in case of error. + * If this function reports an error, the demuxer still + * needs to be closed: the close() function must be + * called and one must wait for the + * demuxerCloseResponse() listener function to be called + * prior to destroying the demuxer. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param status: 0 on success, negative errno value in + * case of error + */ + virtual void + demuxerOpenResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status) = 0; + + /** + * Close response function, called when a close + * operation is complete or has failed. The status + * parameter is the close operation status: 0 on + * success, or a negative errno value in case of error. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param status: 0 on success, negative errno value in + * case of error + */ + virtual void + demuxerCloseResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status) = 0; + + /** + * Unrecoverable error function, called when a + * previously opened demuxer is no longer running. + * is called, the demuxer is no longer running; + * the close() function must be called and one must wait + * for the demuxerCloseResponse() listener function to + * be called prior to destroying the demuxer. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + */ + virtual void onDemuxerUnrecoverableError( + IPdraw *pdraw, + IPdraw::IDemuxer *demuxer) = 0; + + /** + * Demuxer media selection function, called with a list + * of video medias found from which the application must + * choose one or more to process in the pipeline. The + * return value of the function must be a bitfield of + * the identifiers of the chosen medias (from the + * pdraw_demuxer_media structure), or 0 to choose the + * default medias. If the return value is -ENOSYS, the + * callback is considered not implemented and the + * default medias are chosen. If the return value is + * -ECANCELED no media is chosen and the open operation + * is aborted. If the return value is another negative + * errno or an invalid bitfield the + * demuxerOpenResponse() function will be called if an + * open operation is in progress, or the + * onDemuxerUnrecoverableError() function otherwise. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param medias: array of demuxer media + * @param count: demuxer media array element count + * @return a bitfield of the identifiers of the chosen + * medias, 0 or -ENOSYS to choose the default + * medias, -ECANCELED to choose no media and + * abort the open operation, or another negative + * errno value in case of error + */ + virtual int demuxerSelectMedia( + IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + const struct pdraw_demuxer_media *medias, + size_t count) = 0; + + /** + * Ready to play function, called when the playback is + * ready to start. This function is called to indicate + * that the demuxer is ready to process play operations. + * Generally the demuxer is ready to play as soon as the + * demuxerOpenResponse() function has been called with a + * success status. One case when the open operation was + * successful and the playback is not ready is when + * connected to a SkyController's RTSP server but the + * SkyController itself is not yet connected to a drone. + * Similarly, when connected to a drone's stream through + * a SkyController's RTSP server, if the drone is + * disconnected from the SkyController, this function + * will be called with a false value in the ready + * parameter. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param ready: true if the session is ready to play, + * false otherwise + */ + virtual void + demuxerReadyToPlay(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + bool ready) = 0; + + /** + * End of range function, called when the playback is + * suspended after having reached the end of the + * playback duration. This function is only called for + * replays (either local or streamed), not for live + * streams. The timestamp parameter is the current play + * time in microseconds at the moment the playback is + * suspended. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param timestamp: current playback time in + * microseconds + */ + virtual void + onDemuxerEndOfRange(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + uint64_t timestamp) = 0; + + /** + * Play response function, called when a play operation + * is complete (the playback has started) or has failed. + * The status parameter is the play operation status: 0 + * on success, or a negative errno value in case of + * error. The timestamp parameter is the current play + * time in microseconds at the moment the playback is + * started. The speed parameter is the current playback + * speed; a negative value means playing backward. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param status: 0 on success, negative errno value + * in case of error + * @param timestamp: current playback time in + * microseconds + * @param speed: current playback speed, negative means + * backward + */ + virtual void + demuxerPlayResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp, + float speed) = 0; + + /** + * Pause response function, called when a pause + * operation is complete (the playback is suspended) or + * has failed. The status parameter is the pause + * operation status: 0 on success, or a negative errno + * value in case of error. The timestamp parameter is + * the current play time in microseconds at the moment + * the playback is paused. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param status: 0 on success, negative errno value + * in case of error + * @param timestamp: current playback time in + * microseconds + */ + virtual void + demuxerPauseResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp) = 0; + + /** + * Seek response function, called when a seek operation + * is complete or has failed. The status parameter is + * the seek operation status: 0 on success, or a + * negative errno value in case of error. The timestamp + * parameter is the current play time in microseconds + * after seeking. The speed parameter is the current + * playback speed; a negative value means playing + * backward. + * @param pdraw: PDrAW instance handle + * @param demuxer: demuxer handle + * @param status: 0 on success, negative errno value + * in case of error + * @param timestamp: current playback time in + * microseconds + * @param speed: current playback speed, negative means + * backward + */ + virtual void + demuxerSeekResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp, + float speed) = 0; + }; + + /** + * Destroy a demuxer. + * This function stops a running demuxer and frees the + * associated resources. + */ + virtual ~IDemuxer(void) {} + + /** + * Close a demuxer. + * This function closes a previously opened demuxer. The + * function returns before the actual closing is done. If the + * function returns 0, the demuxerCloseResponse() listener + * function will be called once the close is successful (0 + * status) or has failed (negative errno status). If the + * function returns a negative errno value (immediate failure), + * the demuxerCloseResponse() listener function will not be + * called. After a successful close, the demuxer must be + * destroyed. + * @return 0 on success, negative errno value in case of error + */ + virtual int close(void) = 0; + + /** + * Get the single stream local stream port. + * This function returns the local stream (RTP) port currently + * in use after a succesful open operation on a single stream + * (RTP/AVP) or an RTSP URL. If no open operation has been done, + * or if an open operation has been done on a mux channel, 0 is + * returned. This is useful when opening a single stream with + * null local ports to let PDrAW open sockets on any available + * port. + * @return the stream port on success, 0 in case of error + */ + virtual uint16_t getSingleStreamLocalStreamPort(void) = 0; + + /** + * Get the single stream local control port. + * This function returns the local control (RTCP) port currently + * in use after a succesful open operation on a single stream + * (RTP/AVP) or an RTSP URL. If no open operation has been done, + * or if an open operation has been done on a mux channel, 0 is + * returned. This is useful when opening a single stream with + * null local ports to let PDrAW open sockets on any available + * port. + * @return the stream port on success, 0 in case of error + */ + virtual uint16_t getSingleStreamLocalControlPort(void) = 0; + + /** + * Get the ready to play status. + * This function returns true if a successful open operation has + * been completed and if the playback is ready to start, false + * otherwise. One case when a successful open operation is + * complete but the playback is not ready is when connected to a + * SkyController's RTSP server but the SkyController itself is + * not yet connected to a drone. The value returned by this + * function is identical to the ready parameter passed to the + * readyToPlay() listener function when it is called. + * @return the ready to play status on success, false in case of + * error + */ + virtual bool isReadyToPlay(void) = 0; + + /** + * Get the pause status. + * This function returns true if the playback is currently + * paused, false otherwise. + * @return the pause status on success, false in case of error + */ + virtual bool isPaused(void) = 0; + + /** + * Play at the given speed. + * This function starts the playback of the video. The speed + * parameter is optional and defaults to 1.0. If the speed + * parameter is negative, the video is played backward. If the + * speed is greater than or equal to PDRAW_PLAY_SPEED_MAX, the + * speed is ignored and the video is played at the maximum speed + * achievable. If the speed is less than or equal to + * -PDRAW_PLAY_SPEED_MAX, the speed is ignored and the video is + * played backward at the maximum speed achievable. A 0.0 speed + * has the same effet as calling the pause() function. On a live + * stream, the speed parameter has no effect. The function + * returns before the actual operation is done. If the function + * returns 0, the demuxerPlayResponse() listener function will + * be called once the play is successful (0 status) or has + * failed (negative errno status). If the function returns a + * negative errno value (immediate failure), the + * demuxerPlayResponse() listener function will not be called. + * @param speed: playback speed (0.0 means pause, negative value + * means play backward) + * @return 0 on success, negative errno value in case of error + */ + virtual int play(float speed = 1.0f) = 0; + + /** + * Pause the playback. + * This function suspends the playback of the video. The session + * is not closed and the playback can be resumed using the + * play() function. The function returns before the actual + * operation is done. If the function returns 0, the + * demuxerPauseResponse() listener function will be called once + * the pause is successful (0 status) or has failed (negative + * errno status). If the function returns a negative errno value + * (immediate failure), the demuxerPauseResponse() listener + * function will not be called. + * @return 0 on success, negative errno value in case of error + */ + virtual int pause(void) = 0; + + /** + * Go to previous frame in frame-by-frame playback. + * This function plays the previous frame while the playback is + * paused. If the playback is not currently paused an error is + * returned. Frame-by-frame is only available on local replays + * (MP4 records). The function returns before the actual + * operation is done. If the function returns 0, the + * demuxerSeekResponse() listener function will be called once + * the seek is successful (0 status) or has failed (negative + * errno status). If the function returns a negative errno value + * (immediate failure), the demuxerSeekResponse() listener + * function will not be called. + * @return 0 on success, negative errno value in case of error + */ + virtual int previousFrame(void) = 0; + + /** + * Go to next frame in frame-by-frame playback. + * This function plays the next frame while the playback is + * paused. If the playback is not currently paused an error is + * returned. Frame-by-frame is only available on local replays + * (MP4 records). The function returns before the actual + * operation is done. If the function returns 0, the + * demuxerSeekResponse() listener function will be called once + * the seek is successful (0 status) or has failed (negative + * errno status). If the function returns a negative errno value + * (immediate failure), the demuxerSeekResponse() listener + * function will not be called. + * @return 0 on success, negative errno value in case of error + */ + virtual int nextFrame(void) = 0; + + /** + * Seek forward or backward. + * This function seeks forward (positive delta) or backward + * (negative delta). The delta parameter is in microseconds. + * When exact is false the seek is done to the nearest + * synchronization sample preceeding the delta, otherwise the + * seek is done to the sample nearest to the delta. Seeking is + * only available on replays (either local or streamed), not on + * live streams. The function returns before the actual + * operation is done. If the function returns 0, the + * demuxerSeekResponse() listener function will be called once + * the seek is successful (0 status) or has failed (negative + * errno status). If the function returns a negative errno value + * (immediate failure), the demuxerSeekResponse() listener + * function will not be called. + * @param delta: time delta in microseconds (positive or + * negative) + * @param exact: true means seek to the sample closest to the + * delta, false means seek to the nearest + * synchronization sample preceeding the delta + * @return 0 on success, negative errno value in case of error + */ + virtual int seek(int64_t delta, bool exact = false) = 0; + + /** + * Seek forward. + * This function seeks forward by delta microseconds. It has the + * same behavior as calling seek() with a positive delta. When + * exact is false the seek is done to the nearest + * synchronization sample preceeding the delta, otherwise the + * seek is done to the sample nearest to the delta. Seeking is + * only available on replays (either local or streamed), not on + * live streams. The function returns before the actual + * operation is done. If the function returns 0, the + * demuxerSeekResponse() listener function will be called once + * the seek is successful (0 status) or has failed (negative + * errno status). If the function returns a negative errno value + * (immediate failure), the demuxerSeekResponse() listener + * function will not be called. + * @param delta: positive time delta forward in microseconds + * @param exact: true means seek to the sample closest to the + * delta, false means seek to the nearest + * synchronization sample preceeding the delta + * @return 0 on success, negative errno value in case of error + */ + virtual int seekForward(uint64_t delta, bool exact = false) = 0; + + /** + * Seek backward. + * This function seeks backward by delta microseconds (postive). + * It has the same behavior as calling seek() with a negative + * delta. When exact is false the seek is done to the nearest + * synchronization sample preceeding the delta, otherwise the + * seek is done to the sample nearest to the delta. Seeking is + * only available on replays (either local or streamed), not on + * live streams. The function returns before the actual + * operation is done. If the function returns 0, the + * demuxerSeekResponse() listener function will be called once + * the seek is successful (0 status) or has failed (negative + * errno status). If the function returns a negative errno value + * (immediate failure), the demuxerSeekResponse() listener + * function will not be called. + * @param delta: positive time delta backward in microseconds + * @param exact: true means seek to the sample closest to the + * delta, false means seek to the nearest + * synchronization sample preceeding the delta + * @return 0 on success, negative errno value in case of error + */ + virtual int seekBack(uint64_t delta, bool exact = false) = 0; + + /** + * Seek to a given time. + * This function seeks to the given play timestamp in + * microseconds. When exact is false the seek is done to the + * nearest synchronization sample preceeding the timestamp, + * otherwise the seek is done to the sample nearest to the + * timestamp. Seeking is only available on replays (either local + * or streamed), not on live streams. The function returns + * before the actual operation is done. If the function returns + * 0, the demuxerSeekResponse() listener function will be called + * once the seek is successful (0 status) or has failed + * (negative errno status). If the function returns a negative + * errno value (immediate failure), the demuxerSeekResponse() + * listener function will not be called. + * @param timestamp: play timestamp in microseconds + * @param exact: true means seek to the sample closest to the + * timestamp, false means seek to the nearest + * synchronization sample preceeding the timestamp + * @return 0 on success, negative errno value in case of error + */ + virtual int seekTo(uint64_t timestamp, bool exact = false) = 0; + + /** + * Get the playback duration. + * This function returns the playback duration in microseconds. + * The duration is only available on replays (either local or + * streamed), not on live streams. + * @return the duration in microseconds on success, + * 0 in case of error + */ + virtual uint64_t getDuration(void) = 0; + + /** + * Get the playback current time. + * This function returns the current playback position in + * microseconds. On replays (either local or streamed) this is + * the position between 0 and the duration; on live streams this + * is the time since the start of the stream session. + * @return the current time in microseconds on success, + * 0 in case of error + */ + virtual uint64_t getCurrentTime(void) = 0; + }; + + /** + * Create a demuxer on a URL (stream or local file). + * The URL can be either an RTSP URL (starting with "rtsp://") or a + * local file path (either absolute or relative). + * The function returns before the actual opening is done. If the + * function returns 0, the demuxerOpenResponse() listener function will + * be called once the open operation is successful (0 status) or has + * failed (negative errno status). If the function returns a negative + * errno value (immediate failure), the demuxerOpenResponse() listener + * function will not be called. Once a demuxer is no longer used, it + * must be closed and then destroyed (@see the IDemuxer::close() + * function). + * @param url: URL of the resource to open + * @param listener: demuxer listener functions implementation + * @param retObj: demuxer object pointer (output) + * @return 0 on success, negative errno value in case of error + */ + virtual int createDemuxer(const std::string &url, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj) = 0; + + /** + * Create a demuxer on a single stream. + * This function opens an RTP/AVP stream. No session management is done: + * it is the application's responsibility to handle the ports + * negociation with the sender. If null local ports are given as + * parameter, the effective port numbers used can be retrieved using the + * getSingleStreamLocalStreamPort() for the RTP port and + * getSingleStreamLocalControlPort() for the RTCP port functions. + * If the localAddr parameter is left empty, any local network interface + * will be used. The remoteAddr, remoteStreamPort and remoteControlPort + * parameters can be left null/empty if unknown; they will be known once + * the stream is being received. + * The function returns before the actual opening is done. If the + * function returns 0, the demuxerOpenResponse() listener function will + * be called once the open operation is successful (0 status) or has + * failed (negative errno status). If the function returns a negative + * errno value (immediate failure), the demuxerOpenResponse() listener + * function will not be called. Once a demuxer is no longer used, it + * must be closed and then destroyed (@see the IDemuxer::close() + * function). + * @param localAddr: local IP address (optional, can be empty) + * @param localStreamPort: local stream (RTP) port (optional, can be 0) + * @param localControlPort: local control (RTCP) port (optional, + * can be 0) + * @param remoteAddr: remote IP address (optional, can be empty) + * @param remoteStreamPort: remote stream (RTP) port (optional, + * can be 0) + * @param remoteControlPort: remote control (RTCP) port (optional, + * can be 0) + * @param listener: demuxer listener functions implementation + * @param retObj: demuxer object pointer (output) + * @return 0 on success, negative errno value in case of error + */ + virtual int createDemuxer(const std::string &localAddr, + uint16_t localStreamPort, + uint16_t localControlPort, + const std::string &remoteAddr, + uint16_t remoteStreamPort, + uint16_t remoteControlPort, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj) = 0; + + /** + * Create a demuxer on a stream URL through a mux channel. + * The URL must be an RTSP URL (starting with "rtsp://"). Mux channels + * are used to transfer data between a SkyController remote and a + * smartphone through USB; see Parrot's libmux for more information. + * No concurrent sessions can run on the mux channel; therefore the user + * must take care of limiting the number of PDrAW instances and demuxer + * objects running on the mux channel to only one. + * The function returns before the actual opening is done. If the + * function returns 0, the demuxerOpenResponse() listener function will + * be called once the open operation is successful (0 status) or has + * failed (negative errno status). If the function returns a negative + * errno value (immediate failure), the demuxerOpenResponse() listener + * function will not be called. Once a demuxer is no longer used, it + * must be closed and then destroyed (@see the IDemuxer::close() + * function). + * @param url: URL of the resource to open + * @param mux: mux instance handle + * @param listener: demuxer listener functions implementation + * @param retObj: demuxer object pointer (output) + * @return 0 on success, negative errno value in case of error + */ + virtual int createDemuxer(const std::string &url, + struct mux_ctx *mux, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj) = 0; + + + /** + * Muxer API + * @see the createMuxer() function for the muxer object creation + */ + class IMuxer { + public: + /** + * Destroy a muxer. + * This function stops a running muxer and frees the + * associated resources. + */ + virtual ~IMuxer(void) {} + + /** + * Add a media to a muxer. + * This function adds a media to the muxer by its mediaId. + * The media idenfifiers are known when the onMediaAdded() or + * onMediaRemoved() general listener functions are called. + * The params structure is only relevant for video medias; + * the structure must then be provided but all parameters are + * optional and can be left null. + * @param mediaId: identifier of the media to add to the muxer + * @param params: muxer video media parameters + * @return 0 on success, negative errno value in case of error + */ + virtual int addMedia(unsigned int mediaId, + const struct pdraw_muxer_video_media_params + *params) = 0; + }; + + /** + * Create a muxer (experimental). + * This function creates a muxer with a given URL. + * Once the muxer is created medias can be added by id using the + * addMedia() function. Once a muxer is no longer used, it must be + * destroyed. If writing to an MP4 file, the file is finalized in + * the destructor. + * @note: experimental only, the function returns -ENOSYS + * @param url: destination URL + * @param retObj: muxer object pointer (output) + * @return 0 on success, negative errno value in case of error + */ + virtual int createMuxer(const std::string &url, + IPdraw::IMuxer **retObj) = 0; + + + /** + * Video renderer API + * @warning all functions must be called from the application's + * rendering thread + * @see the createVideoRenderer() function for the video renderer + * object creation + */ + class IVideoRenderer { + public: + /* Video renderer listener object */ + class Listener { + public: + /** + * Video renderer listener object destructor. + */ + virtual ~Listener(void) {} + + /** + * Media added function, called when a media has been + * added internally to the renderer. Medias are raw + * video medias. This function is called from the + * pomp_loop thread. + * @param pdraw: PDrAW instance handle + * @param renderer: renderer handle + * @param info: pointer on the media information + */ + virtual void onVideoRendererMediaAdded( + IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer, + const struct pdraw_media_info *info) = 0; + + /** + * Media removed function, called when a media has been + * removed internally from the renderer. Medias are raw + * video medias. This function is called from the + * pomp_loop thread. + * @param pdraw: PDrAW instance handle + * @param renderer: renderer handle + * @param info: pointer on the media information + */ + virtual void onVideoRendererMediaRemoved( + IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer, + const struct pdraw_media_info *info) = 0; + + /** + * Render ready function, called both when a new frame + * is ready for rendering and periodically. This + * function is called from the pomp_loop thread. + * @param pdraw: PDrAW instance handle + * @param renderer: renderer handle + */ + virtual void onVideoRenderReady( + IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer) = 0; + + /** + * External texture loading function. This function is + * called before the rendering of the video frame in + * order to override the frame loading as a texture. + * This can be used to transform the video frames + * outside of PDrAW before resuming the rendering. This + * function is called from the rendering thread. If no + * implementation of this function is required by the + * application, -ENOSYS must be returned (before + * checking input values, so that implementation can be + * tested with all arguments null). + * @param pdraw: PDrAW instance handle + * @param renderer: renderer handle + * @param textureWidth: texture width in pixels + * @param textureHeight: texture height in pixels + * @param mediaInfo: media information + * @param frame: frame information + * @param frameUserdata: frame user data buffer + * @param frameUserdataLen: frame user data buffer size + * in bytes + * @return 0 on success, -ENOSYS if not implemented, + * or another negative errno value in case of + * error + */ + virtual int loadVideoTexture( + IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer, + unsigned int textureWidth, + unsigned int textureHeight, + const struct pdraw_media_info *mediaInfo, + struct mbuf_raw_video_frame *frame, + const void *frameUserdata, + size_t frameUserdataLen) = 0; + + /** + * Overlay rendering function. This function is called + * after the rendering of the video frame (if one is + * available) in order to render an application overlay + * on top of the video. When HMD distorsion correction + * is enabled in the renderer, it is applied after the + * overlay rendering. When no frame is available for + * the rendering, the frameMeta and frameExtra + * parameters are null. This function is called from + * the rendering thread. If no implementation of this + * function is required by the application, -ENOSYS + * must be returned (before checking input values, so + * that implementation can be tested with all arguments + * null). + * @param pdraw: PDrAW instance handle + * @param renderer: renderer handle + * @param renderPos: rendering position + * @param contentPos: video content position + * @param viewMat: 4x4 view matrix + * @param projMat: 4x4 projection matrix + * @param mediaInfo: media information + * @param frameMeta: frame metadata (optional, + * can be null) + * @param frameExtra: frame extra information + * (optional, can be null) + * @return 0 on success, -ENOSYS if not implemented, + * or another negative errno value in case of + * error + */ + virtual int renderVideoOverlay( + IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer, + const struct pdraw_rect *renderPos, + const struct pdraw_rect *contentPos, + const float *viewMat, + const float *projMat, + const struct pdraw_media_info *mediaInfo, + struct vmeta_frame *frameMeta, + const struct pdraw_video_frame_extra + *frameExtra) = 0; + }; + + /** + * Destroy a video renderer. + * This function stops a running video renderer and frees the + * associated resources. + * @warning this function must be called from the application's + * rendering thread. + */ + virtual ~IVideoRenderer(void) {} + + /** + * Resize a video renderer. + * This function updates the rendering position and size on a + * running video renderer. The render_pos parameter sets the + * position of the rendering in the window/view; these + * coordinates are in pixels from the bottom-left corner (OpenGL + * coordinates). + * @warning this function must be called from the application's + * rendering thread. + * @param renderPos: rendering position and size + * @return 0 on success, negative errno value in case of error + */ + virtual int resize(const struct pdraw_rect *renderPos) = 0; + + /** + * Set the video renderer media identifier. + * This function updates the identifier of the media on which + * the rendering is done; if the media id is zero the first raw + * media encountered is used. + * @warning this function must be called from the application's + * rendering thread. + * @param mediaId: identifier of the raw media to render (from + * a pdraw_media_info structure); if zero the + * first raw media found is used for rendering + * @return 0 on success, negative errno value in case of error + */ + virtual int setMediaId(unsigned int mediaId) = 0; + + /** + * Get the video renderer media identifier. + * This function retrieves the identifier of the media on which + * the rendering is done. + * @warning this function must be called from the application's + * rendering thread. + * @return the identifier of the media on success, + * 0 if no media is being renderered or in case of error + */ + virtual unsigned int getMediaId(void) = 0; + + /** + * Set the video renderer parameters. + * This function updates the rendering parameters on a running + * video renderer. The params structure must be provided but all + * parameters are optional and can be left null. + * @warning this function must be called from the application's + * rendering thread. + * @param params: renderer parameters + * @return 0 on success, negative errno value in case of error + */ + virtual int + setParams(const struct pdraw_video_renderer_params *params) = 0; + + /** + * Get the video renderer parameters. + * This function retrieves the rendering parameters on a running + * video renderer. The provided params structure is filled by + * the function. + * @warning this function must be called from the application's + * rendering thread. + * @param params: renderer parameters (output) + * @return 0 on success, negative errno value in case of error + */ + virtual int + getParams(struct pdraw_video_renderer_params *params) = 0; + + /** + * Render the video. + * This function renders the video with the current rendering + * position and rendering parameters, and optionally with the + * provided view and projection matrices. The viewMat and + * projMat matrices are 4x4 OpenGL matrices. For + * render-on-demand, consider using the onVideoRenderReady() + * listener function which is called both when a new frame is + * ready for rendering and periodically. Note that the + * onVideoRenderReady() listener function is called from the + * pomp_loop thread, not the rendering thread; the + * synchronization is up to the caller. If a contentPos + * structure is provided, it is filled with the actual position + * and size of the video within the rendering position; these + * coordinates are in pixels from the bottom-left corner (OpenGL + * coordinates). + * @warning this function must be called from the application's + * rendering thread. + * @param contentPos: video content position (output; + * optional, can be null) + * @param viewMat: 4x4 view matrix (optional, can be null) + * @param projMat: 4x4 projection matrix (optional, can be null) + * @return 0 on success, negative errno value in case of error + */ + virtual int render(struct pdraw_rect *contentPos, + const float *viewMat = nullptr, + const float *projMat = nullptr) = 0; + }; + + /** + * Create a video renderer. + * This function creates a video renderer on a media of the given media + * id; if the media id is zero the first raw media encountered is used. + * Optionally an active EGL display context can be provided. Once the + * renderer is created, the rendering is done by calling the render() + * function. Once a renderer is no longer used it must be + * destroyed. The renderPos parameter sets the position and size + * of the rendering in the window/view; these coordinates are in + * pixels from the bottom-left corner (OpenGL coordinates). The + * params structure must be provided but all parameters are + * optional and can be left null. A valid listener must be + * provided and all functions must be implemented; the + * loadVideoTexture() and renderVideoOverlay() are optional and + * can return -ENOSYS if no real implementation is provided. All + * listener functions are called from the rendering thread, + * except the onVideoRenderReady() function which is called from + * the pomp_loop thread. + * @warning this function must be called from the application's + * rendering thread. + * @param mediaId: identifier of the raw media to render (from a + * pdraw_media_info structure); if zero the first + * raw media found is used for rendering + * @param renderPos: rendering position and size + * @param params: renderer parameters + * @param listener: renderer listener functions implementation + * @param retObj: renderer object pointer (output) + * @param eglDisplay: EGL display context + * @return 0 on success, negative errno value in case of error + */ + virtual int + createVideoRenderer(unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + IPdraw::IVideoRenderer::Listener *listener, + IPdraw::IVideoRenderer **retObj, + struct egl_display *eglDisplay = nullptr) = 0; + + + /** + * Coded video sink API + * @see the createCodedVideoSink() function for the coded video sink + * object creation + */ + class ICodedVideoSink { + public: + /* Video sink listener object */ + class Listener { + public: + /** + * Coded video sink listener object destructor. + */ + virtual ~Listener(void) {} + + /** + * Coded video sink flush function, called when flushing + * is required. When this function is called, the + * application must flush the sink queue by calling + * mbuf_coded_video_frame_queue_flush() and must return + * all frames outside of the queue by calling + * mbuf_coded_frame_unref(); once the flushing is done, + * the queueFlushed() function must be called. + * @param pdraw: PDrAW instance handle + * @param sink: coded video sink handle + */ + virtual void onCodedVideoSinkFlush( + IPdraw *pdraw, + IPdraw::ICodedVideoSink *sink) = 0; + }; + + /** + * Destroy a coded video sink. + * This function stops a running coded video sink and frees the + * associated resources. + */ + virtual ~ICodedVideoSink(void) {} + + /** + * Resynchronize a coded video sink. + * This function schedules the output of a synchronization frame + * (IDR) for a running coded video sink. + * It can be used for example in case of unrecoverable video + * decoder errors to restart decoding. After a coded video sink + * creation, the first frame that is output is always a + * synchronization frame; therefore it is not necessary to call + * this function immediately after a video sink creation. + * @return 0 on success, negative errno value in case of error + */ + virtual int resync(void) = 0; + + /** + * Get the coded video sink frame queue. + * This function returns the frame queue to use in order to + * retrieve frames from a running coded video sink. Frames are + * retrieved from the queue by using the + * mbuf_coded_video_frame_queue_pop() function. + * @return a pointer on a mbuf_coded_video_frame_queue object on + * success, nullptr in case of error + */ + virtual struct mbuf_coded_video_frame_queue *getQueue(void) = 0; + + /** + * Signal that a coded video sink has been flushed. + * This function is used to signal that flushing is complete. + * When the onCodedVideoSinkFlush() video sink listener function + * is called, the application must flush the sink queue by + * calling mbuf_coded_video_frame_queue_flush() and must return + * all frames outside of the queue by calling + * mbuf_coded_video_frame_unref(); once the flushing is + * complete, this function must be called. + * @return 0 on success, negative errno value in case of error + */ + virtual int queueFlushed(void) = 0; + }; + + + /** + * Raw video sink API + * @see the createRawVideoSink() function for the raw video sink + * object creation + */ + class IRawVideoSink { + public: + /* Video sink listener object */ + class Listener { + public: + /** + * Raw video sink listener object destructor. + */ + virtual ~Listener(void) {} + + /** + * Raw video sink flush function, called when flushing + * is required. When this function is called, the + * application must flush the sink queue by calling + * mbuf_raw_video_frame_queue_flush() and must return + * all frames outside of the queue by calling + * mbuf_raw_frame_unref(); once the flushing is done, + * the queueFlushed() function must be called. + * @param pdraw: PDrAW instance handle + * @param sink: raw video sink handle + */ + virtual void + onRawVideoSinkFlush(IPdraw *pdraw, + IPdraw::IRawVideoSink *sink) = 0; + }; + + /** + * Destroy a raw video sink. + * This function stops a running raw video sink and frees the + * associated resources. + */ + virtual ~IRawVideoSink(void) {} + + /** + * Get the raw video sink frame queue. + * This function returns the frame queue to use in order to + * retrieve frames from a running raw video sink. Frames are + * retrieved from the queue by using the + * mbuf_raw_video_frame_queue_pop() function. + * @return a pointer on a mbuf_raw_video_frame_queue object on + * success, nullptr in case of error + */ + virtual struct mbuf_raw_video_frame_queue *getQueue(void) = 0; + + /** + * Signal that a raw video sink has been flushed. + * This function is used to signal that flushing is complete. + * When the onRawVideoSinkFlush() video sink listener function + * is called, the application must flush the sink queue by + * calling mbuf_raw_video_frame_queue_flush() and must return + * all frames outside of the queue by calling + * mbuf_raw_video_frame_unref(); once the flushing is + * complete, this function must be called. + * @return 0 on success, negative errno value in case of error + */ + virtual int queueFlushed(void) = 0; + }; + + + /** + * Create a coded video sink. + * This function creates a video sink on a media of the given mediaId. + * The media idenfifiers are known when the onMediaAdded() or + * onMediaRemoved() general listener functions are called. Once the + * sink is created, video frames are retrieved by getting them from + * the frame queue returned by the getQueue() function. Once a video + * sink is no longer used, it must be destroyed. + * The params structure must be provided but all parameters are optional + * and can be left null. The listener must be provided and the + * onCodedVideoSinkFlush() function is required to be implemented; all + * listener functions are called from the pomp_loop thread. When the + * onCodedVideoSinkFlush() function is called, the application must + * flush the sink queue by calling mbuf_coded_video_frame_queue_flush() + * and must return all frames outside of the queue by calling + * mbuf_coded_video_frame_unref(); once the flushing is complete, the + * queueFlushed() function must be called. + * + * @note mediaId must refer to a coded video media. + * + * @param mediaId: identifier of the media on which to create the sink + * @param params: video sink parameters + * @param listener: video sink listener functions implementation + * @param retObj: video sink object pointer (output) + * @return 0 on success, negative errno value in case of error + */ + virtual int + createCodedVideoSink(unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::ICodedVideoSink::Listener *listener, + IPdraw::ICodedVideoSink **retObj) = 0; + + /** + * Create a raw video sink. + * This function creates a video sink on a media of the given mediaId. + * The media idenfifiers are known when the onMediaAdded() or + * onMediaRemoved() general listener functions are called. Once the + * sink is created, video frames are retrieved by getting them from + * the frame queue returned by the getQueue() function. Once a video + * sink is no longer used, it must be destroyed. + * The params structure must be provided but all parameters are optional + * and can be left null. The listener must be provided and the + * onRawVideoSinkFlush() function is required to be implemented; all + * listener functions are called from the pomp_loop thread. When the + * onRawVideoSinkFlush() function is called, the application must + * flush the sink queue by calling mbuf_raw_video_frame_queue_flush() + * and must return all frames outside of the queue by calling + * mbuf_raw_video_frame_unref(); once the flushing is complete, the + * queueFlushed() function must be called. + * + * @note mediaId must refer to a raw video media. + * + * @param mediaId: identifier of the media on which to create the sink + * @param params: video sink parameters + * @param listener: video sink listener functions implementation + * @param retObj: video sink object pointer (output) + * @return 0 on success, negative errno value in case of error + */ + virtual int + createRawVideoSink(unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::IRawVideoSink::Listener *listener, + IPdraw::IRawVideoSink **retObj) = 0; + + + /** + * Settings API + */ + + /** + * Get the PDrAW instance friendly name. + * This function fills the friendlyName string with the friendly name. + * The string must have been previously allocated. The friendly name is + * generally either the device's friendly name (e.g. "Bob's phone") or + * the application's name (e.g. "MyDroneControllerApp"). It is used for + * example in metadata exchanged with a streaming server. + * @param friendlyName: pointer to the string to write to (output) + */ + virtual void getFriendlyNameSetting(std::string *friendlyName) = 0; + + /** + * Set the PDrAW instance friendly name. + * The friendlyName string is copied internally. The friendly name is + * generally either the device's friendly name (e.g. "Bob's phone") or + * the application's name (e.g. "MyDroneControllerApp"). It is used for + * example in metadata exchanged with a streaming server. Setting the + * friendly name value is optional. + * @param friendlyName: friendly name string + */ + virtual void + setFriendlyNameSetting(const std::string &friendlyName) = 0; + + /** + * Get the PDrAW instance serial number. + * This function fills the serialNumber string with the serial number. + * The string must have been previously allocated. The serial number is + * generally the unique serial number of the device on which PDrAW is + * running. It is used for example in metadata exchanged with a + * streaming server. + * @param serialNumber: pointer to the string to write to (output) + */ + virtual void getSerialNumberSetting(std::string *serialNumber) = 0; + + /** + * Set the PDrAW instance serial number. + * The serialNumber string is copied internally. The serial number is + * generally the unique serial number of the device on which PDrAW is + * running. It is used for example in metadata exchanged with a + * streaming server. Setting the serial number value is recommended as + * it is used as a unique identifier in the streaming protocols. + * @param serialNumber: serial number string + */ + virtual void + setSerialNumberSetting(const std::string &serialNumber) = 0; + + /** + * Get the PDrAW instance software version. + * This function fills the softwareVersion string with the software + * version. The string must have been previously allocated. The software + * version is generally the version number of the application running + * PDrAW (e.g. "MyApp v1.2.3"). It is used for example in metadata + * exchanged with a streaming server. + * @param softwareVersion: pointer to the string to write to (output) + */ + virtual void + getSoftwareVersionSetting(std::string *softwareVersion) = 0; + + /** + * Set the PDrAW instance software version. + * The softwareVersion string is copied internally. The software version + * is generally the version number of the application running PDrAW + * (e.g. "MyApp v1.2.3"). It is used for example in metadata exchanged + * with a streaming server. Setting the software version value is + * optional. + * @param softwareVersion: software version string + */ + virtual void + setSoftwareVersionSetting(const std::string &softwareVersion) = 0; + + /** + * Get the pipeline mode setting. + * This function returns the pipeline mode of a PDrAW instance. The + * pipeline mode controls whether to decode the selected video media + * (for full processing up to the rendering), or to disable video + * decoding (e.g. when no rendering is required, only a coded video + * sink). + * @return the pipeline mode, or PDRAW_PIPELINE_MODE_DECODE_ALL + * in case of error + */ + virtual enum pdraw_pipeline_mode getPipelineModeSetting(void) = 0; + + /** + * Set the pipeline mode setting. + * This function sets the pipeline mode of a PDrAW instance. This + * function can be called only prior to any open operation. The pipeline + * mode controls whether to decode the selected video media (for full + * processing up to the rendering), or to disable video decoding (e.g. + * when no rendering is required, only a coded video sink). + * @param mode: pipeline mode + */ + virtual void setPipelineModeSetting(enum pdraw_pipeline_mode mode) = 0; + + /** + * Get the display screen settings. + * This function returns the display screen settings through the xdpi, + * ydpi and deviceMargin* parameters. This is only useful if HMD + * distortion correction is enabled in the video rendering. The xdpi + * and ydpi are pixel densities in dots per inches. The device margins + * are in millimeters. + * @param xdpi: horizontal pixel density (output) + * @param ydpi: vertical pixel density (output) + * @param deviceMarginTop: top device margin (output) + * @param deviceMarginBottom: bottom device margin (output) + * @param deviceMarginLeft: left device margin (output) + * @param deviceMarginRight: right device margin (output) + */ + virtual void getDisplayScreenSettings(float *xdpi, + float *ydpi, + float *deviceMarginTop, + float *deviceMarginBottom, + float *deviceMarginLeft, + float *deviceMarginRight) = 0; + + /** + * Set the display screen settings. + * This function sets the display screen settings. This is only useful + * if HMD distortion correction is enabled in the video rendering. + * The xdpi and ydpi are pixel densities in dots per inches. The device + * margins are in millimeters. + * @param xdpi: horizontal pixel density + * @param ydpi: vertical pixel density + * @param deviceMarginTop: top device margin + * @param deviceMarginBottom: bottom device margin + * @param deviceMarginLeft: left device margin + * @param deviceMarginRight: right device margin + */ + virtual void setDisplayScreenSettings(float xdpi, + float ydpi, + float deviceMarginTop, + float deviceMarginBottom, + float deviceMarginLeft, + float deviceMarginRight) = 0; + + /** + * Get the HMD model setting. + * This function returns the head-mounted display (HMD) model. This is + * only useful if HMD distortion correction is enabled in the video + * rendering. + * @return the HMD model, or PDRAW_HMD_MODEL_UNKNOWN in case of error + */ + virtual enum pdraw_hmd_model getHmdModelSetting(void) = 0; + + /** + * Set the HMD model setting. + * This function sets the head-mounted display (HMD) model. This is + * only useful if HMD distortion correction is enabled in the video + * rendering. + * @param hmdModel: HMD model + */ + virtual void setHmdModelSetting(enum pdraw_hmd_model hmdModel) = 0; + + + /** + * Platform-specific API + */ + + /** + * Set the Android JVM pointer. + * This function sets the JVM pointer for internal calls to the Android + * SDK API. This is only useful on Android and is ignored on other + * platforms. If the JVM pointer is not provided on Android platforms, + * some features may not be available. + * @param jvm: JVM pointer + */ + virtual void setAndroidJvm(void *jvm) = 0; + + + /** + * Debug API + */ + + /** + * Dump the current pipeline as a directed graph using the DOT file + * format. + * @param fileName: DOT file to write to + * @return 0 on success, negative errno value in case of error + */ + virtual int dumpPipeline(const std::string &fileName) = 0; +}; + + +/** + * Create a PDrAW instance. + * A running pomp_loop must be provided; all functions (with the exception of + * rendering functions) must be called from the pomp_loop thread. All listener + * functions are called from the pomp_loop thread; a valid listener must be + * provided. The instance handle is returned through the retObj parameter. + * When no longer needed, the instance must be deleted to free the resources. + * The stop() function must be called and one must wait for the stopResponse() + * listener function to be called prior to destroying the instance. + * @param loop: pomp_loop to use + * @param listener: listener functions implementation + * @param retObj: PDrAW instance handle (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int createPdraw(struct pomp_loop *loop, + IPdraw::Listener *listener, + IPdraw **retObj); + + +/** + * Helpers + */ + +/** + * ToString function for enum pdraw_hmd_model. + * @param val: HMD model value to convert + * @return a string description of the HMD model + */ +PDRAW_API const char *pdrawHmdModelStr(enum pdraw_hmd_model val); + + +/** + * ToString function for enum pdraw_pipeline_mode. + * @param val: pipeline mode value to convert + * @return a string description of the pipeline mode + */ +PDRAW_API const char *pdrawPipelineModeStr(enum pdraw_pipeline_mode val); + + +/** + * ToString function for enum pdraw_playback_type. + * @param val: playback type value to convert + * @return a string description of the playback type + */ +PDRAW_API const char *pdrawPlaybackTypeStr(enum pdraw_playback_type val); + + +/** + * ToString function for enum pdraw_media_type. + * @param val: media type value to convert + * @return a string description of the media type + */ +PDRAW_API const char *pdrawMediaTypeStr(enum pdraw_media_type val); + + +/** + * ToString function for enum pdraw_video_type. + * @param val: video type value to convert + * @return a string description of the video type + */ +PDRAW_API const char *pdrawVideoTypeStr(enum pdraw_video_type val); + + +/** + * ToString function for enum pdraw_histogram_channel. + * @param val: histogram channel value to convert + * @return a string description of the histogram channel + */ +PDRAW_API const char * +pdrawHistogramChannelStr(enum pdraw_histogram_channel val); + + +/** + * ToString function for enum pdraw_video_renderer_scheduling_mode. + * @param val: video renderer scheduling mode value to convert + * @return a string description of the video renderer scheduling mode + */ +PDRAW_API const char *pdrawVideoRendererSchedulingModeStr( + enum pdraw_video_renderer_scheduling_mode val); + + +/** + * ToString function for enum pdraw_video_renderer_fill_mode. + * @param val: video renderer fill mode value to convert + * @return a string description of the video renderer fill mode + */ +PDRAW_API const char * +pdrawVideoRendererFillModeStr(enum pdraw_video_renderer_fill_mode val); + + +/** + * ToString function for enum pdraw_video_renderer_transition_flag. + * @param val: video renderer transition flag value to convert + * @return a string description of the video renderer transition flag + */ +PDRAW_API const char *pdrawVideoRendererTransitionFlagStr( + enum pdraw_video_renderer_transition_flag val); + + +/** + * Convert a video frame metadata structure to a JSON object. + * The JSON object must have been previously created. The function appends + * new elements in this object. + * If `metadata` is not null, its own json dump will be added to the frame dump. + * @param frame: pointer to a video frame structure + * @param metadata: optional pointer to the frame metadata + * @param jobj: pointer to a JSON object to fill (output) + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdrawVideoFrameToJson(const struct pdraw_video_frame *frame, + struct vmeta_frame *metadata, + struct json_object *jobj); + + +/** + * Convert a video frame metadata structure to a JSON string. + * This function fills the str array with the null-terminated JSON string. + * The string must have been previously allocated. The function writes + * up to len characters. + * If `metadata` is not null, its own json dump will be added to the frame dump. + * @param frame: pointer to a video frame structure + * @param metadata: optional pointer to the frame metadata + * @param str: pointer to the string to write to (output) + * @param len: maximum length of the string + * @return 0 on success, negative errno value in case of error + */ +PDRAW_API int pdrawVideoFrameToJsonStr(const struct pdraw_video_frame *frame, + struct vmeta_frame *metadata, + char *str, + unsigned int len); + + +/** + * Duplicate a media_info structure. + * @param src: pointer to the media_info structure to duplicate + * @return a pointer to the newly allocated structure or NULL on error. + */ +PDRAW_API struct pdraw_media_info * +pdrawMediaInfoDup(const struct pdraw_media_info *src); + + +/** + * Free a media_info structure. + * @param media_info: pointer to the media_info structure to free + */ +PDRAW_API void pdrawMediaInfoFree(struct pdraw_media_info *media_info); + + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_HPP_ */ diff --git a/libpdraw/include/pdraw/pdraw_defs.h b/libpdraw/include/pdraw/pdraw_defs.h index d39994b..22cfcf6 100644 --- a/libpdraw/include/pdraw/pdraw_defs.h +++ b/libpdraw/include/pdraw/pdraw_defs.h @@ -1,581 +1,581 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_DEFS_H_ -#define _PDRAW_DEFS_H_ - -/* To be used for all public API */ -#ifdef PDRAW_API_EXPORTS -# ifdef _WIN32 -# define PDRAW_API __declspec(dllexport) -# else /* !_WIN32 */ -# define PDRAW_API __attribute__((visibility("default"))) -# endif /* !_WIN32 */ -# ifdef __cplusplus -/* codecheck_ignore[STORAGE_CLASS] */ -# define PDRAW_API_VAR extern "C" PDRAW_API -# else /* !__cplusplus */ -# define PDRAW_API_VAR PDRAW_API -# endif -#else /* !PDRAW_API_EXPORTS */ -# define PDRAW_API -/* codecheck_ignore[STORAGE_CLASS] */ -# define PDRAW_API_VAR extern -#endif /* !PDRAW_API_EXPORTS */ - -#include - -#include -#include -#include -#include -#include - - -/* Forward declarations */ -struct egl_display; -struct json_object; -struct mux_ctx; - - -/* Absolute maximum value of playback speed for records; if the requested - * speed is either greater than PDRAW_PLAY_SPEED_MAX of less than - * -PDRAW_PLAY_SPEED_MAX, the video is played as fast as possible */ -#define PDRAW_PLAY_SPEED_MAX 1000.f - -/** - * mbuf ancillary data key for pdraw_video_frame structs - */ -PDRAW_API_VAR const char *PDRAW_ANCILLARY_DATA_KEY_VIDEOFRAME; - - -/* Head-mounted display model */ -enum pdraw_hmd_model { - /* Unknown HMD model */ - PDRAW_HMD_MODEL_UNKNOWN = 0, - - /* Parrot Cockpit Glasses */ - PDRAW_HMD_MODEL_COCKPITGLASSES = 0, - - /* Parrot Cockpit Glasses 2 */ - PDRAW_HMD_MODEL_COCKPITGLASSES_2, -}; - - -/* Pipeline mode */ -enum pdraw_pipeline_mode { - /* Decode all the selected media (required for rendering) */ - PDRAW_PIPELINE_MODE_DECODE_ALL = 0, - - /* Decode none of the selected media (useful to just forward - * the received media outside of libpdraw without rendering) */ - PDRAW_PIPELINE_MODE_DECODE_NONE, -}; - - -/* Playback type */ -enum pdraw_playback_type { - /* Unknown playback type */ - PDRAW_PLAYBACK_TYPE_UNKNOWN = 0, - - /* Live stream */ - PDRAW_PLAYBACK_TYPE_LIVE, - - /* Replay (either streamed or local) */ - PDRAW_PLAYBACK_TYPE_REPLAY, -}; - - -/* Media type */ -enum pdraw_media_type { - /* Unknown media type */ - PDRAW_MEDIA_TYPE_UNKNOWN = 0, - - /* Video media */ - PDRAW_MEDIA_TYPE_VIDEO, -}; - - -/* Video type */ -enum pdraw_video_type { - /* Default camera video */ - PDRAW_VIDEO_TYPE_DEFAULT_CAMERA = 0, - - /* Front camera video */ - PDRAW_VIDEO_TYPE_FRONT_CAMERA = 0, -}; - - -/* Histogram channel */ -enum pdraw_histogram_channel { - /* Red channel */ - PDRAW_HISTOGRAM_CHANNEL_RED = 0, - - /* Green channel */ - PDRAW_HISTOGRAM_CHANNEL_GREEN, - - /* Blue channel */ - PDRAW_HISTOGRAM_CHANNEL_BLUE, - - /* Luminance channel */ - PDRAW_HISTOGRAM_CHANNEL_LUMA, - - /* Enum values count (invalid value) */ - PDRAW_HISTOGRAM_CHANNEL_MAX, -}; - - -/* Video renderer scheduling mode */ -enum pdraw_video_renderer_scheduling_mode { - /* Render frames as soon as possible (minimize the latency) */ - PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ASAP = 0, - - /* Adaptive jitter buffer mode (trade-off between smooth playback - * and minimizing the latency) */ - PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ADAPTIVE, - - /* Enum values count (invalid value) */ - PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_MAX, -}; - - -/* Video renderer fill mode */ -enum pdraw_video_renderer_fill_mode { - /* Fit fill mode (the video fits in the render zone) */ - PDRAW_VIDEO_RENDERER_FILL_MODE_FIT = 0, - - /* Crop fill mode (fills the render zone, cropping the video) */ - PDRAW_VIDEO_RENDERER_FILL_MODE_CROP, - - /* Fit fill mode with blurred crop padding (the padding fills - * the render zone, cropping the video) */ - PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_CROP, - - /* Fit fill mode with blurred extension padding (the padding fills - * the render zone, extending the video sides) */ - PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_EXTEND, - - /* Enum values count (invalid value) */ - PDRAW_VIDEO_RENDERER_FILL_MODE_MAX, -}; - - -/* Video renderer transition flag */ -enum pdraw_video_renderer_transition_flag { - /* Transition on start of stream (live and replay) */ - PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_SOS = (1 << 0), - - /* Transition on end of stream (live and replay) */ - PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_EOS = (1 << 1), - - /* Transition on streaming source reconfiguration (live only) */ - PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_RECONFIGURE = (1 << 2), - - /* Transition on frame reception timeout (live and replay) */ - PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_TIMEOUT = (1 << 3), - - /* Transition on streaming source photo trigger (live only) */ - PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_PHOTO_TRIGGER = (1 << 4), -}; - -/* Enable all video renderer transitions */ -#define PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_ALL UINT32_MAX - - -/* Raw video media information */ -struct pdraw_raw_video_info { - /* Raw video format */ - struct vdef_raw_format format; - - /* Format information */ - struct vdef_format_info info; -}; - - -/* Coded video media information */ -struct pdraw_coded_video_info { - /* Coded video format */ - struct vdef_coded_format format; - - /* Format information */ - struct vdef_format_info info; - - union { - /* H.264 information */ - struct { - /* SPS (raw NALU without start code) */ - uint8_t sps[64]; - - /* SPS size in bytes */ - size_t spslen; - - /* PPS (raw NALU without start code) */ - uint8_t pps[64]; - - /* PPS size in bytes */ - size_t ppslen; - } h264; - - /* H.265 information */ - struct { - /* VPS (raw NALU without start code) */ - uint8_t vps[64]; - - /* VPS size in bytes */ - size_t vpslen; - - /* SPS (raw NALU without start code) */ - uint8_t sps[64]; - - /* SPS size in bytes */ - size_t spslen; - - /* PPS (raw NALU without start code) */ - uint8_t pps[64]; - - /* PPS size in bytes */ - size_t ppslen; - } h265; - }; -}; - - -/* Video media information */ -struct pdraw_video_info { - /* Video media format */ - enum vdef_frame_type format; - - /* Video type */ - enum pdraw_video_type type; - - union { - /* Raw video media information */ - struct pdraw_raw_video_info raw; - - /* Coded video media information */ - struct pdraw_coded_video_info coded; - }; -}; - - -/* Media information */ -struct pdraw_media_info { - /* Media type */ - enum pdraw_media_type type; - - /* Media identifier (unique within a same libpdraw instance) */ - unsigned int id; - - /* Media name (unique within a same libpdraw instance) */ - const char *name; - - /* Media path (unique within a same libpdraw instance) */ - const char *path; - - /* Playback type */ - enum pdraw_playback_type playback_type; - - /* Playback duration in microseconds (replay only, 0 otherwise) */ - uint64_t duration; - - /* Session metadata */ - const struct vmeta_session *session_meta; - - union { - /* Video media information */ - struct pdraw_video_info video; - }; -}; - - -/* Video frame information */ -struct pdraw_video_frame { - /* Video media format */ - enum vdef_frame_type format; - - union { - /* Raw video frame information */ - struct vdef_raw_frame raw; - - /* Coded video frame information */ - struct vdef_coded_frame coded; - }; - - /* 1 if the frame is a synchronization sample (IDR frame), - * 0 otherwise. - * Only meaningful for coded frames */ - int is_sync; - - /* 1 if the frame is a reference frame, 0 otherwise. - * Only meaningful for coded frames */ - int is_ref; - - /* NTP timestamp in microseconds; this timestamp is monotonic; - * a 0 value can mean the timestamp is not available; for records (MP4) - * this is a monotonic timestamp increasing by the samples duration - * (i.e. it is monotonic even when seeking); when ntp_timestamp and - * ntp_unskewed_timestamp are not available ntp_raw_timestamp or - * ntp_raw_unskewed_timestamp should be used */ - uint64_t ntp_timestamp; - - /* Unskewed NTP timestamp in microseconds; same as ntp_timestamp - * but taking into account the clock skew between the sender and - * the receiver on a stream; for records (MP4) the value is identical - * to ntp_timestamp; a 0 value means the timestamp is not available */ - uint64_t ntp_unskewed_timestamp; - - /* Raw NTP timestamp in microseconds computed from RTP timestamp on - * streams; for records (MP4) the value is identical to ntp_timestamp; - * this timestamp is monotonic and always valid; it can be used for - * presentation but cannot be used for synchronization between - * multiple streams */ - uint64_t ntp_raw_timestamp; - - /* Unskewed raw NTP timestamp in microseconds; same as - * ntp_raw_timestamp but taking into account the clock skew between - * the sender and the receiver on a stream; for records (MP4) the value - * is identical to ntp_timestamp; this timestamp is always valid */ - uint64_t ntp_raw_unskewed_timestamp; - - /* Frame play timestamp in microseconds; this timestamp is always - * valid but it is not monotonic; on a record this timestamp - * corresponds to the sample decoding timestamp, i.e. the time - * between 0 and the record duration; on a streamed replay this - * timestamp corresponds to the normal play time (NPT) and has the - * same meaning as on a record; on a live stream this is the time - * since the beginning of the stream */ - uint64_t play_timestamp; - - /* Frame capture timestamp in microseconds; this timestamp is - * monotonic; a 0 value can mean the timestamp is not available; - * this timestamp corresponds to the frame acquisition time on the - * drone using the monotonic clock, it is primarily used for - * synchronization purposes with other monotonic-clock-based data - * such as telemetry, events or logs */ - uint64_t capture_timestamp; - - /* Local timestamp in microseconds; this timestamp is an estimation - * of the capture_timestamp translated on the local monotonic clock; - * on streams it is an estimation with a precision equal to the - * round-trip-delay / 2; on records (MP4) it is the sample demux time; - * a 0 value means the timestamp is not available; this timestamp - * should be used only for statistics or debugging purposes and - * should not be used for presentation */ - uint64_t local_timestamp; -}; - - -/* Frame extra information */ -struct pdraw_video_frame_extra { - /* Frame play timestamp in microseconds; this timestamp is always - * valid but it is not monotonic; on a record this timestamp - * corresponds to the sample decoding timestamp, i.e. the time - * between 0 and the record duration; on a streamed replay this - * timestamp corresponds to the normal play time (NPT) and has the - * same meaning as on a record; on a live stream this is the time - * since the beginning of the stream */ - uint64_t play_timestamp; - - /* Picture histograms arrays by channel */ - float *histogram[PDRAW_HISTOGRAM_CHANNEL_MAX]; - - /* Picture histograms sizes by channel; should be 256 for 8 bits - * formats; a 0 value means the histogram is not available */ - size_t histogram_len[PDRAW_HISTOGRAM_CHANNEL_MAX]; -}; - - -/* Rectangle */ -struct pdraw_rect { - /* Lower-left corner horizontal coordinate in pixels */ - int x; - - /* Lower-left corner vertical coordinate in pixels */ - int y; - - /* Rectangle width in pixels */ - unsigned int width; - - /* Rectangle height in pixels */ - unsigned int height; -}; - - -/* Video renderer parameters */ -struct pdraw_video_renderer_params { - /* Renderer frame scheduling mode */ - enum pdraw_video_renderer_scheduling_mode scheduling_mode; - - /* Renderer fill mode */ - enum pdraw_video_renderer_fill_mode fill_mode; - - /* Bitfield of enum pdraw_video_renderer_transition_flag to enable - * video transitions in the renderer (can be set to - * PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_ALL in order to enable all - * transitions) */ - uint32_t enable_transition_flags; - - /* Enable pincushion distortion correction for head-mounted display - * (HMD). The HMD model and the screen parameters must be specified - * through the settings API. */ - int enable_hmd_distortion_correction; - - /* Inter-pupillary distance offset in millimeters, used with the HMD - * distortion correction. The standard IPD for a given HMD model is - * already applied; this is an additional offset for user settings; - * the default value is 0.0. */ - float hmd_ipd_offset; - - /* Horizontal offset in millimeters, used with the HMD distortion - * correction. The base horizontal offset should be set using the - * left/right device margin in the screen settings API; this is an - * additional offset for user settings; the default value is 0.0. */ - float hmd_x_offset; - - /* Vertical offset in millimeters, used with the HMD distortion - * correction. The base vertical offset should be set using the - * top/bottom device margin in the screen settings API; this is an - * additional offset for user settings; the default value is 0.0. */ - float hmd_y_offset; - - /* Scaling factor applied to the video view. This is mostly useful - * with the HMD distortion correction. For values < 1.0 the video is - * zoomed out; for values > 1.0 the video is zoomed in; the default - * value is 1.0. */ - float video_scale_factor; - - /* If non-null, enable the overexposure zebras on the video */ - int enable_overexposure_zebras; - - /* Overexposure zebras threshold (0.0 to 1.0); normalized pixel values - * above the threshold will be highlighted */ - float overexposure_zebras_threshold; - - /* If non-null, enable the picture histograms computation */ - int enable_histograms; - - /* Optional texture width in pixels to be used with the optional - * external texture loading callback function (ignored if no texture - * loading function is defined). - * - if video_texture_width is specified: - * - if the DAR is specified (see below) the texture height is - * computed according to the texture width and the DAR - * - if the DAR in not specified the texture height is computed - * according to the source video aspect ratio (taking the pixel - * aspect ratio - SAR - into account) - * - if video_texture_width is not specified: - * - if the DAR is specified (see below) the texture width and height - * are computed according to the source video dimensions and the - * DAR, so that the video texture dimensions are inscribed in the - * source video dimensions - * - if the DAR in not specified the texture width and height are - * computed according to the source video dimensions (taking the - * pixel aspect ratio - SAR - into account) */ - unsigned int video_texture_width; - - /* Optional display aspect ratio width to be used with the optional - * external texture loading callback function; if left null, the source - * video aspect ratio is used (ignored if no texture loading function - * is defined) */ - unsigned int video_texture_dar_width; - - /* Optional display aspect ratio height to be used with the optional - * external texture loading callback function; if left null, the source - * video aspect ratio is used (ignored if no texture loading function - * is defined) */ - unsigned int video_texture_dar_height; -}; - - -/* Video sink parameters */ -struct pdraw_video_sink_params { - /* Frame queue maximum count; optional, can be 0 which means - * frames are never dropped from the queue; when not 0, older frames - * will be automatically dropped when the queue is full to make room for - * new frames. */ - unsigned int queue_max_count; - - /* Required format for raw or coded video sinks; the default - * value is VDEF_CODED_FORMAT_UNKNOWN for a coded video sink or - * VDEF_RAW_FORMAT_UNKNOWN for a raw video sink which means no - * preference */ - union { - struct vdef_raw_format required_raw_format; - struct vdef_coded_format required_coded_format; - }; - - /* Enable the fake frame_num generation for H.264 medias */ - int fake_frame_num; -}; - - -/* Muxer video media parameters */ -struct pdraw_muxer_video_media_params { - /* Scaling resolution; null values mean no scaling (keep the - * original dimensions) */ - struct vdef_dim resolution; - - /* Transcoding format; VDEF_ENCODING_UNKNOWN means no transcoding - * (then scaling must be disabled with null resolution) */ - enum vdef_encoding encoding; - - /* Target bitrate in bits per second */ - unsigned int target_bitrate; - - /* GOP length in seconds (if 0.0, defaults to 1.0) */ - float gop_length_sec; -}; - - -/* Demuxer media information */ -struct pdraw_demuxer_media { - /* Media name */ - const char *name; - - /* Media id; this is the value which must be returned by the - * demuxer media selection callback function */ - int media_id; - - /* 1 for default media, 0 otherwise; the default medias will be - * selected if the demuxer media selection callback function returns - * either 0 or -ENOSYS */ - int is_default; - - /* Session metadata */ - struct vmeta_session session_meta; - - /* Internal information (implementation dependant, do not use) */ - int idx; - unsigned int stream_port; - unsigned int control_port; - const char *uri; -}; - - -#endif /* !_PDRAW_DEFS_H_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_DEFS_H_ +#define _PDRAW_DEFS_H_ + +/* To be used for all public API */ +#ifdef PDRAW_API_EXPORTS +# ifdef _WIN32 +# define PDRAW_API __declspec(dllexport) +# else /* !_WIN32 */ +# define PDRAW_API __attribute__((visibility("default"))) +# endif /* !_WIN32 */ +# ifdef __cplusplus +/* codecheck_ignore[STORAGE_CLASS] */ +# define PDRAW_API_VAR extern "C" PDRAW_API +# else /* !__cplusplus */ +# define PDRAW_API_VAR PDRAW_API +# endif +#else /* !PDRAW_API_EXPORTS */ +# define PDRAW_API +/* codecheck_ignore[STORAGE_CLASS] */ +# define PDRAW_API_VAR extern +#endif /* !PDRAW_API_EXPORTS */ + +#include + +#include +#include +#include +#include +#include + + +/* Forward declarations */ +struct egl_display; +struct json_object; +struct mux_ctx; + + +/* Absolute maximum value of playback speed for records; if the requested + * speed is either greater than PDRAW_PLAY_SPEED_MAX of less than + * -PDRAW_PLAY_SPEED_MAX, the video is played as fast as possible */ +#define PDRAW_PLAY_SPEED_MAX 1000.f + +/** + * mbuf ancillary data key for pdraw_video_frame structs + */ +PDRAW_API_VAR const char *PDRAW_ANCILLARY_DATA_KEY_VIDEOFRAME; + + +/* Head-mounted display model */ +enum pdraw_hmd_model { + /* Unknown HMD model */ + PDRAW_HMD_MODEL_UNKNOWN = 0, + + /* Parrot Cockpit Glasses */ + PDRAW_HMD_MODEL_COCKPITGLASSES = 0, + + /* Parrot Cockpit Glasses 2 */ + PDRAW_HMD_MODEL_COCKPITGLASSES_2, +}; + + +/* Pipeline mode */ +enum pdraw_pipeline_mode { + /* Decode all the selected media (required for rendering) */ + PDRAW_PIPELINE_MODE_DECODE_ALL = 0, + + /* Decode none of the selected media (useful to just forward + * the received media outside of libpdraw without rendering) */ + PDRAW_PIPELINE_MODE_DECODE_NONE, +}; + + +/* Playback type */ +enum pdraw_playback_type { + /* Unknown playback type */ + PDRAW_PLAYBACK_TYPE_UNKNOWN = 0, + + /* Live stream */ + PDRAW_PLAYBACK_TYPE_LIVE, + + /* Replay (either streamed or local) */ + PDRAW_PLAYBACK_TYPE_REPLAY, +}; + + +/* Media type */ +enum pdraw_media_type { + /* Unknown media type */ + PDRAW_MEDIA_TYPE_UNKNOWN = 0, + + /* Video media */ + PDRAW_MEDIA_TYPE_VIDEO, +}; + + +/* Video type */ +enum pdraw_video_type { + /* Default camera video */ + PDRAW_VIDEO_TYPE_DEFAULT_CAMERA = 0, + + /* Front camera video */ + PDRAW_VIDEO_TYPE_FRONT_CAMERA = 0, +}; + + +/* Histogram channel */ +enum pdraw_histogram_channel { + /* Red channel */ + PDRAW_HISTOGRAM_CHANNEL_RED = 0, + + /* Green channel */ + PDRAW_HISTOGRAM_CHANNEL_GREEN, + + /* Blue channel */ + PDRAW_HISTOGRAM_CHANNEL_BLUE, + + /* Luminance channel */ + PDRAW_HISTOGRAM_CHANNEL_LUMA, + + /* Enum values count (invalid value) */ + PDRAW_HISTOGRAM_CHANNEL_MAX, +}; + + +/* Video renderer scheduling mode */ +enum pdraw_video_renderer_scheduling_mode { + /* Render frames as soon as possible (minimize the latency) */ + PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ASAP = 0, + + /* Adaptive jitter buffer mode (trade-off between smooth playback + * and minimizing the latency) */ + PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ADAPTIVE, + + /* Enum values count (invalid value) */ + PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_MAX, +}; + + +/* Video renderer fill mode */ +enum pdraw_video_renderer_fill_mode { + /* Fit fill mode (the video fits in the render zone) */ + PDRAW_VIDEO_RENDERER_FILL_MODE_FIT = 0, + + /* Crop fill mode (fills the render zone, cropping the video) */ + PDRAW_VIDEO_RENDERER_FILL_MODE_CROP, + + /* Fit fill mode with blurred crop padding (the padding fills + * the render zone, cropping the video) */ + PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_CROP, + + /* Fit fill mode with blurred extension padding (the padding fills + * the render zone, extending the video sides) */ + PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_EXTEND, + + /* Enum values count (invalid value) */ + PDRAW_VIDEO_RENDERER_FILL_MODE_MAX, +}; + + +/* Video renderer transition flag */ +enum pdraw_video_renderer_transition_flag { + /* Transition on start of stream (live and replay) */ + PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_SOS = (1 << 0), + + /* Transition on end of stream (live and replay) */ + PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_EOS = (1 << 1), + + /* Transition on streaming source reconfiguration (live only) */ + PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_RECONFIGURE = (1 << 2), + + /* Transition on frame reception timeout (live and replay) */ + PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_TIMEOUT = (1 << 3), + + /* Transition on streaming source photo trigger (live only) */ + PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_PHOTO_TRIGGER = (1 << 4), +}; + +/* Enable all video renderer transitions */ +#define PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_ALL UINT32_MAX + + +/* Raw video media information */ +struct pdraw_raw_video_info { + /* Raw video format */ + struct vdef_raw_format format; + + /* Format information */ + struct vdef_format_info info; +}; + + +/* Coded video media information */ +struct pdraw_coded_video_info { + /* Coded video format */ + struct vdef_coded_format format; + + /* Format information */ + struct vdef_format_info info; + + union { + /* H.264 information */ + struct { + /* SPS (raw NALU without start code) */ + uint8_t sps[64]; + + /* SPS size in bytes */ + size_t spslen; + + /* PPS (raw NALU without start code) */ + uint8_t pps[64]; + + /* PPS size in bytes */ + size_t ppslen; + } h264; + + /* H.265 information */ + struct { + /* VPS (raw NALU without start code) */ + uint8_t vps[64]; + + /* VPS size in bytes */ + size_t vpslen; + + /* SPS (raw NALU without start code) */ + uint8_t sps[64]; + + /* SPS size in bytes */ + size_t spslen; + + /* PPS (raw NALU without start code) */ + uint8_t pps[64]; + + /* PPS size in bytes */ + size_t ppslen; + } h265; + }; +}; + + +/* Video media information */ +struct pdraw_video_info { + /* Video media format */ + enum vdef_frame_type format; + + /* Video type */ + enum pdraw_video_type type; + + union { + /* Raw video media information */ + struct pdraw_raw_video_info raw; + + /* Coded video media information */ + struct pdraw_coded_video_info coded; + }; +}; + + +/* Media information */ +struct pdraw_media_info { + /* Media type */ + enum pdraw_media_type type; + + /* Media identifier (unique within a same libpdraw instance) */ + unsigned int id; + + /* Media name (unique within a same libpdraw instance) */ + const char *name; + + /* Media path (unique within a same libpdraw instance) */ + const char *path; + + /* Playback type */ + enum pdraw_playback_type playback_type; + + /* Playback duration in microseconds (replay only, 0 otherwise) */ + uint64_t duration; + + /* Session metadata */ + const struct vmeta_session *session_meta; + + union { + /* Video media information */ + struct pdraw_video_info video; + }; +}; + + +/* Video frame information */ +struct pdraw_video_frame { + /* Video media format */ + enum vdef_frame_type format; + + union { + /* Raw video frame information */ + struct vdef_raw_frame raw; + + /* Coded video frame information */ + struct vdef_coded_frame coded; + }; + + /* 1 if the frame is a synchronization sample (IDR frame), + * 0 otherwise. + * Only meaningful for coded frames */ + int is_sync; + + /* 1 if the frame is a reference frame, 0 otherwise. + * Only meaningful for coded frames */ + int is_ref; + + /* NTP timestamp in microseconds; this timestamp is monotonic; + * a 0 value can mean the timestamp is not available; for records (MP4) + * this is a monotonic timestamp increasing by the samples duration + * (i.e. it is monotonic even when seeking); when ntp_timestamp and + * ntp_unskewed_timestamp are not available ntp_raw_timestamp or + * ntp_raw_unskewed_timestamp should be used */ + uint64_t ntp_timestamp; + + /* Unskewed NTP timestamp in microseconds; same as ntp_timestamp + * but taking into account the clock skew between the sender and + * the receiver on a stream; for records (MP4) the value is identical + * to ntp_timestamp; a 0 value means the timestamp is not available */ + uint64_t ntp_unskewed_timestamp; + + /* Raw NTP timestamp in microseconds computed from RTP timestamp on + * streams; for records (MP4) the value is identical to ntp_timestamp; + * this timestamp is monotonic and always valid; it can be used for + * presentation but cannot be used for synchronization between + * multiple streams */ + uint64_t ntp_raw_timestamp; + + /* Unskewed raw NTP timestamp in microseconds; same as + * ntp_raw_timestamp but taking into account the clock skew between + * the sender and the receiver on a stream; for records (MP4) the value + * is identical to ntp_timestamp; this timestamp is always valid */ + uint64_t ntp_raw_unskewed_timestamp; + + /* Frame play timestamp in microseconds; this timestamp is always + * valid but it is not monotonic; on a record this timestamp + * corresponds to the sample decoding timestamp, i.e. the time + * between 0 and the record duration; on a streamed replay this + * timestamp corresponds to the normal play time (NPT) and has the + * same meaning as on a record; on a live stream this is the time + * since the beginning of the stream */ + uint64_t play_timestamp; + + /* Frame capture timestamp in microseconds; this timestamp is + * monotonic; a 0 value can mean the timestamp is not available; + * this timestamp corresponds to the frame acquisition time on the + * drone using the monotonic clock, it is primarily used for + * synchronization purposes with other monotonic-clock-based data + * such as telemetry, events or logs */ + uint64_t capture_timestamp; + + /* Local timestamp in microseconds; this timestamp is an estimation + * of the capture_timestamp translated on the local monotonic clock; + * on streams it is an estimation with a precision equal to the + * round-trip-delay / 2; on records (MP4) it is the sample demux time; + * a 0 value means the timestamp is not available; this timestamp + * should be used only for statistics or debugging purposes and + * should not be used for presentation */ + uint64_t local_timestamp; +}; + + +/* Frame extra information */ +struct pdraw_video_frame_extra { + /* Frame play timestamp in microseconds; this timestamp is always + * valid but it is not monotonic; on a record this timestamp + * corresponds to the sample decoding timestamp, i.e. the time + * between 0 and the record duration; on a streamed replay this + * timestamp corresponds to the normal play time (NPT) and has the + * same meaning as on a record; on a live stream this is the time + * since the beginning of the stream */ + uint64_t play_timestamp; + + /* Picture histograms arrays by channel */ + float *histogram[PDRAW_HISTOGRAM_CHANNEL_MAX]; + + /* Picture histograms sizes by channel; should be 256 for 8 bits + * formats; a 0 value means the histogram is not available */ + size_t histogram_len[PDRAW_HISTOGRAM_CHANNEL_MAX]; +}; + + +/* Rectangle */ +struct pdraw_rect { + /* Lower-left corner horizontal coordinate in pixels */ + int x; + + /* Lower-left corner vertical coordinate in pixels */ + int y; + + /* Rectangle width in pixels */ + unsigned int width; + + /* Rectangle height in pixels */ + unsigned int height; +}; + + +/* Video renderer parameters */ +struct pdraw_video_renderer_params { + /* Renderer frame scheduling mode */ + enum pdraw_video_renderer_scheduling_mode scheduling_mode; + + /* Renderer fill mode */ + enum pdraw_video_renderer_fill_mode fill_mode; + + /* Bitfield of enum pdraw_video_renderer_transition_flag to enable + * video transitions in the renderer (can be set to + * PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_ALL in order to enable all + * transitions) */ + uint32_t enable_transition_flags; + + /* Enable pincushion distortion correction for head-mounted display + * (HMD). The HMD model and the screen parameters must be specified + * through the settings API. */ + int enable_hmd_distortion_correction; + + /* Inter-pupillary distance offset in millimeters, used with the HMD + * distortion correction. The standard IPD for a given HMD model is + * already applied; this is an additional offset for user settings; + * the default value is 0.0. */ + float hmd_ipd_offset; + + /* Horizontal offset in millimeters, used with the HMD distortion + * correction. The base horizontal offset should be set using the + * left/right device margin in the screen settings API; this is an + * additional offset for user settings; the default value is 0.0. */ + float hmd_x_offset; + + /* Vertical offset in millimeters, used with the HMD distortion + * correction. The base vertical offset should be set using the + * top/bottom device margin in the screen settings API; this is an + * additional offset for user settings; the default value is 0.0. */ + float hmd_y_offset; + + /* Scaling factor applied to the video view. This is mostly useful + * with the HMD distortion correction. For values < 1.0 the video is + * zoomed out; for values > 1.0 the video is zoomed in; the default + * value is 1.0. */ + float video_scale_factor; + + /* If non-null, enable the overexposure zebras on the video */ + int enable_overexposure_zebras; + + /* Overexposure zebras threshold (0.0 to 1.0); normalized pixel values + * above the threshold will be highlighted */ + float overexposure_zebras_threshold; + + /* If non-null, enable the picture histograms computation */ + int enable_histograms; + + /* Optional texture width in pixels to be used with the optional + * external texture loading callback function (ignored if no texture + * loading function is defined). + * - if video_texture_width is specified: + * - if the DAR is specified (see below) the texture height is + * computed according to the texture width and the DAR + * - if the DAR in not specified the texture height is computed + * according to the source video aspect ratio (taking the pixel + * aspect ratio - SAR - into account) + * - if video_texture_width is not specified: + * - if the DAR is specified (see below) the texture width and height + * are computed according to the source video dimensions and the + * DAR, so that the video texture dimensions are inscribed in the + * source video dimensions + * - if the DAR in not specified the texture width and height are + * computed according to the source video dimensions (taking the + * pixel aspect ratio - SAR - into account) */ + unsigned int video_texture_width; + + /* Optional display aspect ratio width to be used with the optional + * external texture loading callback function; if left null, the source + * video aspect ratio is used (ignored if no texture loading function + * is defined) */ + unsigned int video_texture_dar_width; + + /* Optional display aspect ratio height to be used with the optional + * external texture loading callback function; if left null, the source + * video aspect ratio is used (ignored if no texture loading function + * is defined) */ + unsigned int video_texture_dar_height; +}; + + +/* Video sink parameters */ +struct pdraw_video_sink_params { + /* Frame queue maximum count; optional, can be 0 which means + * frames are never dropped from the queue; when not 0, older frames + * will be automatically dropped when the queue is full to make room for + * new frames. */ + unsigned int queue_max_count; + + /* Required format for raw or coded video sinks; the default + * value is VDEF_CODED_FORMAT_UNKNOWN for a coded video sink or + * VDEF_RAW_FORMAT_UNKNOWN for a raw video sink which means no + * preference */ + union { + struct vdef_raw_format required_raw_format; + struct vdef_coded_format required_coded_format; + }; + + /* Enable the fake frame_num generation for H.264 medias */ + int fake_frame_num; +}; + + +/* Muxer video media parameters */ +struct pdraw_muxer_video_media_params { + /* Scaling resolution; null values mean no scaling (keep the + * original dimensions) */ + struct vdef_dim resolution; + + /* Transcoding format; VDEF_ENCODING_UNKNOWN means no transcoding + * (then scaling must be disabled with null resolution) */ + enum vdef_encoding encoding; + + /* Target bitrate in bits per second */ + unsigned int target_bitrate; + + /* GOP length in seconds (if 0.0, defaults to 1.0) */ + float gop_length_sec; +}; + + +/* Demuxer media information */ +struct pdraw_demuxer_media { + /* Media name */ + const char *name; + + /* Media id; this is the value which must be returned by the + * demuxer media selection callback function */ + int media_id; + + /* 1 for default media, 0 otherwise; the default medias will be + * selected if the demuxer media selection callback function returns + * either 0 or -ENOSYS */ + int is_default; + + /* Session metadata */ + struct vmeta_session session_meta; + + /* Internal information (implementation dependant, do not use) */ + int idx; + unsigned int stream_port; + unsigned int control_port; + const char *uri; +}; + + +#endif /* !_PDRAW_DEFS_H_ */ diff --git a/libpdraw/src/pdraw_channel_coded_video.cpp b/libpdraw/src/pdraw_channel_coded_video.cpp index a7a4ed1..7bb598e 100644 --- a/libpdraw/src/pdraw_channel_coded_video.cpp +++ b/libpdraw/src/pdraw_channel_coded_video.cpp @@ -1,325 +1,325 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Pipeline source to sink channel - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_channel_coded_video -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_channel_coded_video.hpp" -#include "pdraw_media.hpp" - -#include - -namespace Pdraw { - - -CodedChannel::CodedChannel(SinkListener *sinkListener) : - mSinkListener(sinkListener), mSourceListener(nullptr), - mKey(nullptr), mCodedVideoMediaFormatCaps(nullptr), - mCodedVideoMediaFormatCapsCount(0), mQueue(nullptr), - mPool(nullptr), mFlushPending(false) -{ -} - - -int CodedChannel::queue(mbuf_coded_video_frame *frame) -{ - if (frame == nullptr) - return -EINVAL; - if (mSinkListener == nullptr) { - ULOGE("invalid sink listener"); - return -EPROTO; - } - - mSinkListener->onChannelQueue(this, frame); - return 0; -} - - -int CodedChannel::flush(void) -{ - int res; - - if (mSinkListener == nullptr) { - ULOGE("invalid sink listener"); - return -EPROTO; - } - - struct pomp_msg *event = pomp_msg_new(); - if (event == nullptr) { - ULOG_ERRNO("pomp_msg_new", ENOMEM); - return -ENOMEM; - } - - res = pomp_msg_write(event, DownstreamEvent::FLUSH, nullptr); - if (res < 0) { - ULOG_ERRNO("pomp_msg_write", -res); - return res; - } - - mFlushPending = true; - mSinkListener->onChannelDownstreamEvent(this, event); - - res = pomp_msg_destroy(event); - if (res < 0) - ULOG_ERRNO("pomp_msg_destroy", -res); - - return 0; -} - - -int CodedChannel::flushDone(void) -{ - int res; - - if (!mFlushPending) - return 0; - - mFlushPending = false; - if (mSourceListener == nullptr) - return 0; - - struct pomp_msg *event = pomp_msg_new(); - if (event == nullptr) { - ULOG_ERRNO("pomp_msg_new", ENOMEM); - return -ENOMEM; - } - - res = pomp_msg_write(event, UpstreamEvent::FLUSHED, nullptr); - if (res < 0) { - ULOG_ERRNO("pomp_msg_write", -res); - return res; - } - - mSourceListener->onChannelUpstreamEvent(this, event); - - res = pomp_msg_destroy(event); - if (res < 0) - ULOG_ERRNO("pomp_msg_destroy", -res); - - return 0; -} - - -int CodedChannel::resync(void) -{ - int res; - - if (mSourceListener == nullptr) - return 0; - - struct pomp_msg *event = pomp_msg_new(); - if (event == nullptr) { - ULOG_ERRNO("pomp_msg_new", ENOMEM); - return -ENOMEM; - } - - res = pomp_msg_write(event, UpstreamEvent::RESYNC, nullptr); - if (res < 0) { - ULOG_ERRNO("pomp_msg_write", -res); - return res; - } - - mSourceListener->onChannelUpstreamEvent(this, event); - - res = pomp_msg_destroy(event); - if (res < 0) - ULOG_ERRNO("pomp_msg_destroy", -res); - - return 0; -} - - -int CodedChannel::teardown(void) -{ - int res; - - if (mSinkListener == nullptr) { - ULOGE("invalid sink listener"); - return -EPROTO; - } - - struct pomp_msg *event = pomp_msg_new(); - if (event == nullptr) { - ULOG_ERRNO("pomp_msg_new", ENOMEM); - return -ENOMEM; - } - - res = pomp_msg_write(event, DownstreamEvent::TEARDOWN, nullptr); - if (res < 0) { - ULOG_ERRNO("pomp_msg_write", -res); - return res; - } - - mSinkListener->onChannelDownstreamEvent(this, event); - - res = pomp_msg_destroy(event); - if (res < 0) - ULOG_ERRNO("pomp_msg_destroy", -res); - - return 0; -} - - -int CodedChannel::unlink(void) -{ - int res; - - if (mSourceListener == nullptr) - return 0; - - struct pomp_msg *event = pomp_msg_new(); - if (event == nullptr) { - ULOG_ERRNO("pomp_msg_new", ENOMEM); - return -ENOMEM; - } - - res = pomp_msg_write(event, UpstreamEvent::UNLINK, nullptr); - if (res < 0) { - ULOG_ERRNO("pomp_msg_write", -res); - return res; - } - - mSourceListener->onChannelUpstreamEvent(this, event); - - res = pomp_msg_destroy(event); - if (res < 0) - ULOG_ERRNO("pomp_msg_destroy", -res); - - return 0; -} - - -int CodedChannel::sendVideoPresStats(VideoPresStats *stats) -{ - int res; - - if (mSourceListener == nullptr) - return 0; - - struct pomp_msg *event = pomp_msg_new(); - if (event == nullptr) { - ULOG_ERRNO("pomp_msg_new", ENOMEM); - return -ENOMEM; - } - - res = stats->writeMsg(event, UpstreamEvent::VIDEO_PRES_STATS); - if (res < 0) { - ULOG_ERRNO("stats->writeMsg", -res); - goto out; - } - - mSourceListener->onChannelUpstreamEvent(this, event); - -out: - int err = pomp_msg_destroy(event); - if (err < 0) - ULOG_ERRNO("pomp_msg_destroy", -err); - - return res; -} - - -int CodedChannel::sendDownstreamEvent(DownstreamEvent downstreamEvent) -{ - int res; - - if ((downstreamEvent == DownstreamEvent::FLUSH) || - (downstreamEvent == DownstreamEvent::TEARDOWN)) { - ULOGE("invalid event"); - return -EPROTO; - } - if (mSinkListener == nullptr) { - ULOGE("invalid sink listener"); - return -EPROTO; - } - - struct pomp_msg *event = pomp_msg_new(); - if (event == nullptr) { - ULOG_ERRNO("pomp_msg_new", ENOMEM); - return -ENOMEM; - } - - res = pomp_msg_write(event, downstreamEvent, nullptr); - if (res < 0) { - ULOG_ERRNO("pomp_msg_write", -res); - return res; - } - - mSinkListener->onChannelDownstreamEvent(this, event); - - res = pomp_msg_destroy(event); - if (res < 0) - ULOG_ERRNO("pomp_msg_destroy", -res); - - return 0; -} - - -const char *CodedChannel::getDownstreamEventStr(DownstreamEvent val) -{ - switch (val) { - case FLUSH: - return "FLUSH"; - case TEARDOWN: - return "TEARDOWN"; - case SOS: - return "SOS"; - case EOS: - return "EOS"; - case RECONFIGURE: - return "RECONFIGURE"; - case TIMEOUT: - return "TIMEOUT"; - case PHOTO_TRIGGER: - return "PHOTO_TRIGGER"; - default: - return nullptr; - } -} - - -const char *CodedChannel::getUpstreamEventStr(UpstreamEvent val) -{ - switch (val) { - case UNLINK: - return "UNLINK"; - case FLUSHED: - return "FLUSHED"; - case RESYNC: - return "RESYNC"; - case VIDEO_PRES_STATS: - return "VIDEO_PRES_STATS"; - default: - return nullptr; - } -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Pipeline source to sink channel + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_channel_coded_video +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_channel_coded_video.hpp" +#include "pdraw_media.hpp" + +#include + +namespace Pdraw { + + +CodedChannel::CodedChannel(SinkListener *sinkListener) : + mSinkListener(sinkListener), mSourceListener(nullptr), + mKey(nullptr), mCodedVideoMediaFormatCaps(nullptr), + mCodedVideoMediaFormatCapsCount(0), mQueue(nullptr), + mPool(nullptr), mFlushPending(false) +{ +} + + +int CodedChannel::queue(mbuf_coded_video_frame *frame) +{ + if (frame == nullptr) + return -EINVAL; + if (mSinkListener == nullptr) { + ULOGE("invalid sink listener"); + return -EPROTO; + } + + mSinkListener->onChannelQueue(this, frame); + return 0; +} + + +int CodedChannel::flush(void) +{ + int res; + + if (mSinkListener == nullptr) { + ULOGE("invalid sink listener"); + return -EPROTO; + } + + struct pomp_msg *event = pomp_msg_new(); + if (event == nullptr) { + ULOG_ERRNO("pomp_msg_new", ENOMEM); + return -ENOMEM; + } + + res = pomp_msg_write(event, DownstreamEvent::FLUSH, nullptr); + if (res < 0) { + ULOG_ERRNO("pomp_msg_write", -res); + return res; + } + + mFlushPending = true; + mSinkListener->onChannelDownstreamEvent(this, event); + + res = pomp_msg_destroy(event); + if (res < 0) + ULOG_ERRNO("pomp_msg_destroy", -res); + + return 0; +} + + +int CodedChannel::flushDone(void) +{ + int res; + + if (!mFlushPending) + return 0; + + mFlushPending = false; + if (mSourceListener == nullptr) + return 0; + + struct pomp_msg *event = pomp_msg_new(); + if (event == nullptr) { + ULOG_ERRNO("pomp_msg_new", ENOMEM); + return -ENOMEM; + } + + res = pomp_msg_write(event, UpstreamEvent::FLUSHED, nullptr); + if (res < 0) { + ULOG_ERRNO("pomp_msg_write", -res); + return res; + } + + mSourceListener->onChannelUpstreamEvent(this, event); + + res = pomp_msg_destroy(event); + if (res < 0) + ULOG_ERRNO("pomp_msg_destroy", -res); + + return 0; +} + + +int CodedChannel::resync(void) +{ + int res; + + if (mSourceListener == nullptr) + return 0; + + struct pomp_msg *event = pomp_msg_new(); + if (event == nullptr) { + ULOG_ERRNO("pomp_msg_new", ENOMEM); + return -ENOMEM; + } + + res = pomp_msg_write(event, UpstreamEvent::RESYNC, nullptr); + if (res < 0) { + ULOG_ERRNO("pomp_msg_write", -res); + return res; + } + + mSourceListener->onChannelUpstreamEvent(this, event); + + res = pomp_msg_destroy(event); + if (res < 0) + ULOG_ERRNO("pomp_msg_destroy", -res); + + return 0; +} + + +int CodedChannel::teardown(void) +{ + int res; + + if (mSinkListener == nullptr) { + ULOGE("invalid sink listener"); + return -EPROTO; + } + + struct pomp_msg *event = pomp_msg_new(); + if (event == nullptr) { + ULOG_ERRNO("pomp_msg_new", ENOMEM); + return -ENOMEM; + } + + res = pomp_msg_write(event, DownstreamEvent::TEARDOWN, nullptr); + if (res < 0) { + ULOG_ERRNO("pomp_msg_write", -res); + return res; + } + + mSinkListener->onChannelDownstreamEvent(this, event); + + res = pomp_msg_destroy(event); + if (res < 0) + ULOG_ERRNO("pomp_msg_destroy", -res); + + return 0; +} + + +int CodedChannel::unlink(void) +{ + int res; + + if (mSourceListener == nullptr) + return 0; + + struct pomp_msg *event = pomp_msg_new(); + if (event == nullptr) { + ULOG_ERRNO("pomp_msg_new", ENOMEM); + return -ENOMEM; + } + + res = pomp_msg_write(event, UpstreamEvent::UNLINK, nullptr); + if (res < 0) { + ULOG_ERRNO("pomp_msg_write", -res); + return res; + } + + mSourceListener->onChannelUpstreamEvent(this, event); + + res = pomp_msg_destroy(event); + if (res < 0) + ULOG_ERRNO("pomp_msg_destroy", -res); + + return 0; +} + + +int CodedChannel::sendVideoPresStats(VideoPresStats *stats) +{ + int res; + + if (mSourceListener == nullptr) + return 0; + + struct pomp_msg *event = pomp_msg_new(); + if (event == nullptr) { + ULOG_ERRNO("pomp_msg_new", ENOMEM); + return -ENOMEM; + } + + res = stats->writeMsg(event, UpstreamEvent::VIDEO_PRES_STATS); + if (res < 0) { + ULOG_ERRNO("stats->writeMsg", -res); + goto out; + } + + mSourceListener->onChannelUpstreamEvent(this, event); + +out: + int err = pomp_msg_destroy(event); + if (err < 0) + ULOG_ERRNO("pomp_msg_destroy", -err); + + return res; +} + + +int CodedChannel::sendDownstreamEvent(DownstreamEvent downstreamEvent) +{ + int res; + + if ((downstreamEvent == DownstreamEvent::FLUSH) || + (downstreamEvent == DownstreamEvent::TEARDOWN)) { + ULOGE("invalid event"); + return -EPROTO; + } + if (mSinkListener == nullptr) { + ULOGE("invalid sink listener"); + return -EPROTO; + } + + struct pomp_msg *event = pomp_msg_new(); + if (event == nullptr) { + ULOG_ERRNO("pomp_msg_new", ENOMEM); + return -ENOMEM; + } + + res = pomp_msg_write(event, downstreamEvent, nullptr); + if (res < 0) { + ULOG_ERRNO("pomp_msg_write", -res); + return res; + } + + mSinkListener->onChannelDownstreamEvent(this, event); + + res = pomp_msg_destroy(event); + if (res < 0) + ULOG_ERRNO("pomp_msg_destroy", -res); + + return 0; +} + + +const char *CodedChannel::getDownstreamEventStr(DownstreamEvent val) +{ + switch (val) { + case FLUSH: + return "FLUSH"; + case TEARDOWN: + return "TEARDOWN"; + case SOS: + return "SOS"; + case EOS: + return "EOS"; + case RECONFIGURE: + return "RECONFIGURE"; + case TIMEOUT: + return "TIMEOUT"; + case PHOTO_TRIGGER: + return "PHOTO_TRIGGER"; + default: + return nullptr; + } +} + + +const char *CodedChannel::getUpstreamEventStr(UpstreamEvent val) +{ + switch (val) { + case UNLINK: + return "UNLINK"; + case FLUSHED: + return "FLUSHED"; + case RESYNC: + return "RESYNC"; + case VIDEO_PRES_STATS: + return "VIDEO_PRES_STATS"; + default: + return nullptr; + } +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_channel_coded_video.hpp b/libpdraw/src/pdraw_channel_coded_video.hpp index 5888987..64409ab 100644 --- a/libpdraw/src/pdraw_channel_coded_video.hpp +++ b/libpdraw/src/pdraw_channel_coded_video.hpp @@ -1,223 +1,223 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Pipeline source to sink channel for coded video - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_CHANNEL_CODED_VIDEO_HPP_ -#define _PDRAW_CHANNEL_CODED_VIDEO_HPP_ - -#include - -#include -#include -#include - -#include "pdraw_video_pres_stats.hpp" - -namespace Pdraw { - -class CodedChannel { -public: - friend class CodedSource; - friend class CodedSink; - friend class VideoDecoder; - friend class VideoEncoder; - friend class RecordDemuxer; - friend class StreamDemuxer; - friend class Muxer; - friend class RecordMuxer; - friend class RtmpStreamMuxer; - friend class ExternalCodedVideoSink; - - enum DownstreamEvent { - /* flush required */ - FLUSH, - - /* teardown required */ - TEARDOWN, - - /* start of stream */ - SOS, - - /* end of stream */ - EOS, - - /* reconfiguration in progress */ - RECONFIGURE, - - /* reception timeout */ - TIMEOUT, - - /* photo trigger */ - PHOTO_TRIGGER, - }; - - enum UpstreamEvent { - /* unlink required */ - UNLINK, - - /* flush completed */ - FLUSHED, - - /* resynchronization required */ - RESYNC, - - /* video presentation statistics */ - VIDEO_PRES_STATS, - }; - - class SinkListener { - public: - virtual ~SinkListener(void) {} - - virtual void onChannelQueue(CodedChannel *channel, - mbuf_coded_video_frame *frame) = 0; - - virtual void - onChannelDownstreamEvent(CodedChannel *channel, - const struct pomp_msg *event) = 0; - }; - - class SourceListener { - public: - virtual ~SourceListener(void) {} - - virtual void - onChannelUpstreamEvent(CodedChannel *channel, - const struct pomp_msg *event) = 0; - }; - - static const char *getDownstreamEventStr(DownstreamEvent val); - - static const char *getUpstreamEventStr(UpstreamEvent val); - - CodedChannel(SinkListener *sinkListener); - - ~CodedChannel(void) {} - - int queue(mbuf_coded_video_frame *frame); - - int flush(void); - - bool isFlushPending(void) - { - return mFlushPending; - } - - int flushDone(void); - - int resync(void); - - int unlink(void); - - int teardown(void); - - int sendVideoPresStats(VideoPresStats *stats); - - int sendDownstreamEvent(DownstreamEvent downstreamEvent); - - SourceListener *getSourceListener(void) - { - return mSourceListener; - } - - void setSourceListener(SourceListener *sourceListener) - { - mSourceListener = sourceListener; - } - - void *getKey(void) - { - return mKey; - } - - int getCodedVideoMediaFormatCaps(const struct vdef_coded_format **caps) - { - if (!caps) - return -EINVAL; - *caps = mCodedVideoMediaFormatCaps; - return mCodedVideoMediaFormatCapsCount; - } - - bool onlySupportsByteStream() - { - for (int i = 0; i < mCodedVideoMediaFormatCapsCount; i++) { - if (mCodedVideoMediaFormatCaps[i].data_format != - VDEF_CODED_DATA_FORMAT_BYTE_STREAM) - return false; - } - return true; - } - -protected: - void setKey(void *key) - { - mKey = key; - } - - void setCodedVideoMediaFormatCaps(const struct vdef_coded_format *caps, - int count) - { - mCodedVideoMediaFormatCaps = caps; - mCodedVideoMediaFormatCapsCount = count; - } - - struct mbuf_coded_video_frame_queue *getQueue(void) - { - return mQueue; - } - - void setQueue(struct mbuf_coded_video_frame_queue *queue) - { - mQueue = queue; - } - - struct mbuf_pool *getPool(void) - { - return mPool; - } - - void setPool(struct mbuf_pool *pool) - { - mPool = pool; - } - -private: - SinkListener *mSinkListener; - SourceListener *mSourceListener; - void *mKey; - const struct vdef_coded_format *mCodedVideoMediaFormatCaps; - int mCodedVideoMediaFormatCapsCount; - struct mbuf_coded_video_frame_queue *mQueue; - struct mbuf_pool *mPool; - bool mFlushPending; -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_CHANNEL_CODED_VIDEO_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Pipeline source to sink channel for coded video + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_CHANNEL_CODED_VIDEO_HPP_ +#define _PDRAW_CHANNEL_CODED_VIDEO_HPP_ + +#include + +#include +#include +#include + +#include "pdraw_video_pres_stats.hpp" + +namespace Pdraw { + +class CodedChannel { +public: + friend class CodedSource; + friend class CodedSink; + friend class VideoDecoder; + friend class VideoEncoder; + friend class RecordDemuxer; + friend class StreamDemuxer; + friend class Muxer; + friend class RecordMuxer; + friend class RtmpStreamMuxer; + friend class ExternalCodedVideoSink; + + enum DownstreamEvent { + /* flush required */ + FLUSH, + + /* teardown required */ + TEARDOWN, + + /* start of stream */ + SOS, + + /* end of stream */ + EOS, + + /* reconfiguration in progress */ + RECONFIGURE, + + /* reception timeout */ + TIMEOUT, + + /* photo trigger */ + PHOTO_TRIGGER, + }; + + enum UpstreamEvent { + /* unlink required */ + UNLINK, + + /* flush completed */ + FLUSHED, + + /* resynchronization required */ + RESYNC, + + /* video presentation statistics */ + VIDEO_PRES_STATS, + }; + + class SinkListener { + public: + virtual ~SinkListener(void) {} + + virtual void onChannelQueue(CodedChannel *channel, + mbuf_coded_video_frame *frame) = 0; + + virtual void + onChannelDownstreamEvent(CodedChannel *channel, + const struct pomp_msg *event) = 0; + }; + + class SourceListener { + public: + virtual ~SourceListener(void) {} + + virtual void + onChannelUpstreamEvent(CodedChannel *channel, + const struct pomp_msg *event) = 0; + }; + + static const char *getDownstreamEventStr(DownstreamEvent val); + + static const char *getUpstreamEventStr(UpstreamEvent val); + + CodedChannel(SinkListener *sinkListener); + + ~CodedChannel(void) {} + + int queue(mbuf_coded_video_frame *frame); + + int flush(void); + + bool isFlushPending(void) + { + return mFlushPending; + } + + int flushDone(void); + + int resync(void); + + int unlink(void); + + int teardown(void); + + int sendVideoPresStats(VideoPresStats *stats); + + int sendDownstreamEvent(DownstreamEvent downstreamEvent); + + SourceListener *getSourceListener(void) + { + return mSourceListener; + } + + void setSourceListener(SourceListener *sourceListener) + { + mSourceListener = sourceListener; + } + + void *getKey(void) + { + return mKey; + } + + int getCodedVideoMediaFormatCaps(const struct vdef_coded_format **caps) + { + if (!caps) + return -EINVAL; + *caps = mCodedVideoMediaFormatCaps; + return mCodedVideoMediaFormatCapsCount; + } + + bool onlySupportsByteStream() + { + for (int i = 0; i < mCodedVideoMediaFormatCapsCount; i++) { + if (mCodedVideoMediaFormatCaps[i].data_format != + VDEF_CODED_DATA_FORMAT_BYTE_STREAM) + return false; + } + return true; + } + +protected: + void setKey(void *key) + { + mKey = key; + } + + void setCodedVideoMediaFormatCaps(const struct vdef_coded_format *caps, + int count) + { + mCodedVideoMediaFormatCaps = caps; + mCodedVideoMediaFormatCapsCount = count; + } + + struct mbuf_coded_video_frame_queue *getQueue(void) + { + return mQueue; + } + + void setQueue(struct mbuf_coded_video_frame_queue *queue) + { + mQueue = queue; + } + + struct mbuf_pool *getPool(void) + { + return mPool; + } + + void setPool(struct mbuf_pool *pool) + { + mPool = pool; + } + +private: + SinkListener *mSinkListener; + SourceListener *mSourceListener; + void *mKey; + const struct vdef_coded_format *mCodedVideoMediaFormatCaps; + int mCodedVideoMediaFormatCapsCount; + struct mbuf_coded_video_frame_queue *mQueue; + struct mbuf_pool *mPool; + bool mFlushPending; +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_CHANNEL_CODED_VIDEO_HPP_ */ diff --git a/libpdraw/src/pdraw_channel_raw_video.cpp b/libpdraw/src/pdraw_channel_raw_video.cpp index 6e88264..7d83c29 100644 --- a/libpdraw/src/pdraw_channel_raw_video.cpp +++ b/libpdraw/src/pdraw_channel_raw_video.cpp @@ -1,325 +1,325 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Pipeline source to sink channel - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_channel_raw_video -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_channel_raw_video.hpp" -#include "pdraw_media.hpp" - -#include - -namespace Pdraw { - - -RawChannel::RawChannel(SinkListener *sinkListener) : - mSinkListener(sinkListener), mSourceListener(nullptr), - mKey(nullptr), mRawVideoMediaFormatCaps(nullptr), - mRawVideoMediaFormatCapsCount(0), mQueue(nullptr), - mPool(nullptr), mFlushPending(false) -{ -} - - -int RawChannel::queue(mbuf_raw_video_frame *frame) -{ - if (frame == nullptr) - return -EINVAL; - if (mSinkListener == nullptr) { - ULOGE("invalid sink listener"); - return -EPROTO; - } - - mSinkListener->onChannelQueue(this, frame); - return 0; -} - - -int RawChannel::flush(void) -{ - int res; - - if (mSinkListener == nullptr) { - ULOGE("invalid sink listener"); - return -EPROTO; - } - - struct pomp_msg *event = pomp_msg_new(); - if (event == nullptr) { - ULOG_ERRNO("pomp_msg_new", ENOMEM); - return -ENOMEM; - } - - res = pomp_msg_write(event, DownstreamEvent::FLUSH, nullptr); - if (res < 0) { - ULOG_ERRNO("pomp_msg_write", -res); - return res; - } - - mFlushPending = true; - mSinkListener->onChannelDownstreamEvent(this, event); - - res = pomp_msg_destroy(event); - if (res < 0) - ULOG_ERRNO("pomp_msg_destroy", -res); - - return 0; -} - - -int RawChannel::flushDone(void) -{ - int res; - - if (!mFlushPending) - return 0; - - mFlushPending = false; - if (mSourceListener == nullptr) - return 0; - - struct pomp_msg *event = pomp_msg_new(); - if (event == nullptr) { - ULOG_ERRNO("pomp_msg_new", ENOMEM); - return -ENOMEM; - } - - res = pomp_msg_write(event, UpstreamEvent::FLUSHED, nullptr); - if (res < 0) { - ULOG_ERRNO("pomp_msg_write", -res); - return res; - } - - mSourceListener->onChannelUpstreamEvent(this, event); - - res = pomp_msg_destroy(event); - if (res < 0) - ULOG_ERRNO("pomp_msg_destroy", -res); - - return 0; -} - - -int RawChannel::resync(void) -{ - int res; - - if (mSourceListener == nullptr) - return 0; - - struct pomp_msg *event = pomp_msg_new(); - if (event == nullptr) { - ULOG_ERRNO("pomp_msg_new", ENOMEM); - return -ENOMEM; - } - - res = pomp_msg_write(event, UpstreamEvent::RESYNC, nullptr); - if (res < 0) { - ULOG_ERRNO("pomp_msg_write", -res); - return res; - } - - mSourceListener->onChannelUpstreamEvent(this, event); - - res = pomp_msg_destroy(event); - if (res < 0) - ULOG_ERRNO("pomp_msg_destroy", -res); - - return 0; -} - - -int RawChannel::teardown(void) -{ - int res; - - if (mSinkListener == nullptr) { - ULOGE("invalid sink listener"); - return -EPROTO; - } - - struct pomp_msg *event = pomp_msg_new(); - if (event == nullptr) { - ULOG_ERRNO("pomp_msg_new", ENOMEM); - return -ENOMEM; - } - - res = pomp_msg_write(event, DownstreamEvent::TEARDOWN, nullptr); - if (res < 0) { - ULOG_ERRNO("pomp_msg_write", -res); - return res; - } - - mSinkListener->onChannelDownstreamEvent(this, event); - - res = pomp_msg_destroy(event); - if (res < 0) - ULOG_ERRNO("pomp_msg_destroy", -res); - - return 0; -} - - -int RawChannel::unlink(void) -{ - int res; - - if (mSourceListener == nullptr) - return 0; - - struct pomp_msg *event = pomp_msg_new(); - if (event == nullptr) { - ULOG_ERRNO("pomp_msg_new", ENOMEM); - return -ENOMEM; - } - - res = pomp_msg_write(event, UpstreamEvent::UNLINK, nullptr); - if (res < 0) { - ULOG_ERRNO("pomp_msg_write", -res); - return res; - } - - mSourceListener->onChannelUpstreamEvent(this, event); - - res = pomp_msg_destroy(event); - if (res < 0) - ULOG_ERRNO("pomp_msg_destroy", -res); - - return 0; -} - - -int RawChannel::sendVideoPresStats(VideoPresStats *stats) -{ - int res; - - if (mSourceListener == nullptr) - return 0; - - struct pomp_msg *event = pomp_msg_new(); - if (event == nullptr) { - ULOG_ERRNO("pomp_msg_new", ENOMEM); - return -ENOMEM; - } - - res = stats->writeMsg(event, UpstreamEvent::VIDEO_PRES_STATS); - if (res < 0) { - ULOG_ERRNO("stats->writeMsg", -res); - goto out; - } - - mSourceListener->onChannelUpstreamEvent(this, event); - -out: - int err = pomp_msg_destroy(event); - if (err < 0) - ULOG_ERRNO("pomp_msg_destroy", -err); - - return res; -} - - -int RawChannel::sendDownstreamEvent(DownstreamEvent downstreamEvent) -{ - int res; - - if ((downstreamEvent == DownstreamEvent::FLUSH) || - (downstreamEvent == DownstreamEvent::TEARDOWN)) { - ULOGE("invalid event"); - return -EPROTO; - } - if (mSinkListener == nullptr) { - ULOGE("invalid sink listener"); - return -EPROTO; - } - - struct pomp_msg *event = pomp_msg_new(); - if (event == nullptr) { - ULOG_ERRNO("pomp_msg_new", ENOMEM); - return -ENOMEM; - } - - res = pomp_msg_write(event, downstreamEvent, nullptr); - if (res < 0) { - ULOG_ERRNO("pomp_msg_write", -res); - return res; - } - - mSinkListener->onChannelDownstreamEvent(this, event); - - res = pomp_msg_destroy(event); - if (res < 0) - ULOG_ERRNO("pomp_msg_destroy", -res); - - return 0; -} - - -const char *RawChannel::getDownstreamEventStr(DownstreamEvent val) -{ - switch (val) { - case FLUSH: - return "FLUSH"; - case TEARDOWN: - return "TEARDOWN"; - case SOS: - return "SOS"; - case EOS: - return "EOS"; - case RECONFIGURE: - return "RECONFIGURE"; - case TIMEOUT: - return "TIMEOUT"; - case PHOTO_TRIGGER: - return "PHOTO_TRIGGER"; - default: - return nullptr; - } -} - - -const char *RawChannel::getUpstreamEventStr(UpstreamEvent val) -{ - switch (val) { - case UNLINK: - return "UNLINK"; - case FLUSHED: - return "FLUSHED"; - case RESYNC: - return "RESYNC"; - case VIDEO_PRES_STATS: - return "VIDEO_PRES_STATS"; - default: - return nullptr; - } -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Pipeline source to sink channel + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_channel_raw_video +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_channel_raw_video.hpp" +#include "pdraw_media.hpp" + +#include + +namespace Pdraw { + + +RawChannel::RawChannel(SinkListener *sinkListener) : + mSinkListener(sinkListener), mSourceListener(nullptr), + mKey(nullptr), mRawVideoMediaFormatCaps(nullptr), + mRawVideoMediaFormatCapsCount(0), mQueue(nullptr), + mPool(nullptr), mFlushPending(false) +{ +} + + +int RawChannel::queue(mbuf_raw_video_frame *frame) +{ + if (frame == nullptr) + return -EINVAL; + if (mSinkListener == nullptr) { + ULOGE("invalid sink listener"); + return -EPROTO; + } + + mSinkListener->onChannelQueue(this, frame); + return 0; +} + + +int RawChannel::flush(void) +{ + int res; + + if (mSinkListener == nullptr) { + ULOGE("invalid sink listener"); + return -EPROTO; + } + + struct pomp_msg *event = pomp_msg_new(); + if (event == nullptr) { + ULOG_ERRNO("pomp_msg_new", ENOMEM); + return -ENOMEM; + } + + res = pomp_msg_write(event, DownstreamEvent::FLUSH, nullptr); + if (res < 0) { + ULOG_ERRNO("pomp_msg_write", -res); + return res; + } + + mFlushPending = true; + mSinkListener->onChannelDownstreamEvent(this, event); + + res = pomp_msg_destroy(event); + if (res < 0) + ULOG_ERRNO("pomp_msg_destroy", -res); + + return 0; +} + + +int RawChannel::flushDone(void) +{ + int res; + + if (!mFlushPending) + return 0; + + mFlushPending = false; + if (mSourceListener == nullptr) + return 0; + + struct pomp_msg *event = pomp_msg_new(); + if (event == nullptr) { + ULOG_ERRNO("pomp_msg_new", ENOMEM); + return -ENOMEM; + } + + res = pomp_msg_write(event, UpstreamEvent::FLUSHED, nullptr); + if (res < 0) { + ULOG_ERRNO("pomp_msg_write", -res); + return res; + } + + mSourceListener->onChannelUpstreamEvent(this, event); + + res = pomp_msg_destroy(event); + if (res < 0) + ULOG_ERRNO("pomp_msg_destroy", -res); + + return 0; +} + + +int RawChannel::resync(void) +{ + int res; + + if (mSourceListener == nullptr) + return 0; + + struct pomp_msg *event = pomp_msg_new(); + if (event == nullptr) { + ULOG_ERRNO("pomp_msg_new", ENOMEM); + return -ENOMEM; + } + + res = pomp_msg_write(event, UpstreamEvent::RESYNC, nullptr); + if (res < 0) { + ULOG_ERRNO("pomp_msg_write", -res); + return res; + } + + mSourceListener->onChannelUpstreamEvent(this, event); + + res = pomp_msg_destroy(event); + if (res < 0) + ULOG_ERRNO("pomp_msg_destroy", -res); + + return 0; +} + + +int RawChannel::teardown(void) +{ + int res; + + if (mSinkListener == nullptr) { + ULOGE("invalid sink listener"); + return -EPROTO; + } + + struct pomp_msg *event = pomp_msg_new(); + if (event == nullptr) { + ULOG_ERRNO("pomp_msg_new", ENOMEM); + return -ENOMEM; + } + + res = pomp_msg_write(event, DownstreamEvent::TEARDOWN, nullptr); + if (res < 0) { + ULOG_ERRNO("pomp_msg_write", -res); + return res; + } + + mSinkListener->onChannelDownstreamEvent(this, event); + + res = pomp_msg_destroy(event); + if (res < 0) + ULOG_ERRNO("pomp_msg_destroy", -res); + + return 0; +} + + +int RawChannel::unlink(void) +{ + int res; + + if (mSourceListener == nullptr) + return 0; + + struct pomp_msg *event = pomp_msg_new(); + if (event == nullptr) { + ULOG_ERRNO("pomp_msg_new", ENOMEM); + return -ENOMEM; + } + + res = pomp_msg_write(event, UpstreamEvent::UNLINK, nullptr); + if (res < 0) { + ULOG_ERRNO("pomp_msg_write", -res); + return res; + } + + mSourceListener->onChannelUpstreamEvent(this, event); + + res = pomp_msg_destroy(event); + if (res < 0) + ULOG_ERRNO("pomp_msg_destroy", -res); + + return 0; +} + + +int RawChannel::sendVideoPresStats(VideoPresStats *stats) +{ + int res; + + if (mSourceListener == nullptr) + return 0; + + struct pomp_msg *event = pomp_msg_new(); + if (event == nullptr) { + ULOG_ERRNO("pomp_msg_new", ENOMEM); + return -ENOMEM; + } + + res = stats->writeMsg(event, UpstreamEvent::VIDEO_PRES_STATS); + if (res < 0) { + ULOG_ERRNO("stats->writeMsg", -res); + goto out; + } + + mSourceListener->onChannelUpstreamEvent(this, event); + +out: + int err = pomp_msg_destroy(event); + if (err < 0) + ULOG_ERRNO("pomp_msg_destroy", -err); + + return res; +} + + +int RawChannel::sendDownstreamEvent(DownstreamEvent downstreamEvent) +{ + int res; + + if ((downstreamEvent == DownstreamEvent::FLUSH) || + (downstreamEvent == DownstreamEvent::TEARDOWN)) { + ULOGE("invalid event"); + return -EPROTO; + } + if (mSinkListener == nullptr) { + ULOGE("invalid sink listener"); + return -EPROTO; + } + + struct pomp_msg *event = pomp_msg_new(); + if (event == nullptr) { + ULOG_ERRNO("pomp_msg_new", ENOMEM); + return -ENOMEM; + } + + res = pomp_msg_write(event, downstreamEvent, nullptr); + if (res < 0) { + ULOG_ERRNO("pomp_msg_write", -res); + return res; + } + + mSinkListener->onChannelDownstreamEvent(this, event); + + res = pomp_msg_destroy(event); + if (res < 0) + ULOG_ERRNO("pomp_msg_destroy", -res); + + return 0; +} + + +const char *RawChannel::getDownstreamEventStr(DownstreamEvent val) +{ + switch (val) { + case FLUSH: + return "FLUSH"; + case TEARDOWN: + return "TEARDOWN"; + case SOS: + return "SOS"; + case EOS: + return "EOS"; + case RECONFIGURE: + return "RECONFIGURE"; + case TIMEOUT: + return "TIMEOUT"; + case PHOTO_TRIGGER: + return "PHOTO_TRIGGER"; + default: + return nullptr; + } +} + + +const char *RawChannel::getUpstreamEventStr(UpstreamEvent val) +{ + switch (val) { + case UNLINK: + return "UNLINK"; + case FLUSHED: + return "FLUSHED"; + case RESYNC: + return "RESYNC"; + case VIDEO_PRES_STATS: + return "VIDEO_PRES_STATS"; + default: + return nullptr; + } +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_channel_raw_video.hpp b/libpdraw/src/pdraw_channel_raw_video.hpp index 0f248cd..8d915ef 100644 --- a/libpdraw/src/pdraw_channel_raw_video.hpp +++ b/libpdraw/src/pdraw_channel_raw_video.hpp @@ -1,211 +1,211 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Pipeline source to sink channel for raw video - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_CHANNEL_RAW_VIDEO_HPP_ -#define _PDRAW_CHANNEL_RAW_VIDEO_HPP_ - -#include - -#include -#include -#include - -#include "pdraw_video_pres_stats.hpp" - -namespace Pdraw { - -class RawChannel { -public: - friend class RawSource; - friend class RawSink; - friend class VideoDecoder; - friend class VideoEncoder; - friend class VideoScaler; - friend class Gles2Renderer; - friend class ExternalRawVideoSink; - - enum DownstreamEvent { - /* flush required */ - FLUSH, - - /* teardown required */ - TEARDOWN, - - /* start of stream */ - SOS, - - /* end of stream */ - EOS, - - /* reconfiguration in progress */ - RECONFIGURE, - - /* reception timeout */ - TIMEOUT, - - /* photo trigger */ - PHOTO_TRIGGER, - }; - - enum UpstreamEvent { - /* unlink required */ - UNLINK, - - /* flush completed */ - FLUSHED, - - /* resynchronization required */ - RESYNC, - - /* video presentation statistics */ - VIDEO_PRES_STATS, - }; - - class SinkListener { - public: - virtual ~SinkListener(void) {} - - virtual void - onChannelQueue(RawChannel *channel, - struct mbuf_raw_video_frame *frame) = 0; - - virtual void - onChannelDownstreamEvent(RawChannel *channel, - const struct pomp_msg *event) = 0; - }; - - class SourceListener { - public: - virtual ~SourceListener(void) {} - - virtual void - onChannelUpstreamEvent(RawChannel *channel, - const struct pomp_msg *event) = 0; - }; - - static const char *getDownstreamEventStr(DownstreamEvent val); - - static const char *getUpstreamEventStr(UpstreamEvent val); - - RawChannel(SinkListener *sinkListener); - - ~RawChannel(void) {} - - int queue(mbuf_raw_video_frame *frame); - - int flush(void); - - bool isFlushPending(void) - { - return mFlushPending; - } - - int flushDone(void); - - int resync(void); - - int unlink(void); - - int teardown(void); - - int sendVideoPresStats(VideoPresStats *stats); - - int sendDownstreamEvent(DownstreamEvent downstreamEvent); - - SourceListener *getSourceListener(void) - { - return mSourceListener; - } - - void setSourceListener(SourceListener *sourceListener) - { - mSourceListener = sourceListener; - } - - void *getKey(void) - { - return mKey; - } - - int getRawVideoMediaFormatCaps(const struct vdef_raw_format **caps) - { - if (!caps) - return -EINVAL; - *caps = mRawVideoMediaFormatCaps; - return mRawVideoMediaFormatCapsCount; - } - -protected: - void setKey(void *key) - { - mKey = key; - } - - void setRawVideoMediaFormatCaps(const struct vdef_raw_format *caps, - int count) - { - mRawVideoMediaFormatCaps = caps; - mRawVideoMediaFormatCapsCount = count; - } - - struct mbuf_raw_video_frame_queue *getQueue(void) - { - return mQueue; - } - - void setQueue(struct mbuf_raw_video_frame_queue *queue) - { - mQueue = queue; - } - - struct mbuf_pool *getPool(void) - { - return mPool; - } - - void setPool(struct mbuf_pool *pool) - { - mPool = pool; - } - -private: - SinkListener *mSinkListener; - SourceListener *mSourceListener; - void *mKey; - const struct vdef_raw_format *mRawVideoMediaFormatCaps; - int mRawVideoMediaFormatCapsCount; - struct mbuf_raw_video_frame_queue *mQueue; - struct mbuf_pool *mPool; - bool mFlushPending; -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_CHANNEL_RAW_VIDEO_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Pipeline source to sink channel for raw video + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_CHANNEL_RAW_VIDEO_HPP_ +#define _PDRAW_CHANNEL_RAW_VIDEO_HPP_ + +#include + +#include +#include +#include + +#include "pdraw_video_pres_stats.hpp" + +namespace Pdraw { + +class RawChannel { +public: + friend class RawSource; + friend class RawSink; + friend class VideoDecoder; + friend class VideoEncoder; + friend class VideoScaler; + friend class Gles2Renderer; + friend class ExternalRawVideoSink; + + enum DownstreamEvent { + /* flush required */ + FLUSH, + + /* teardown required */ + TEARDOWN, + + /* start of stream */ + SOS, + + /* end of stream */ + EOS, + + /* reconfiguration in progress */ + RECONFIGURE, + + /* reception timeout */ + TIMEOUT, + + /* photo trigger */ + PHOTO_TRIGGER, + }; + + enum UpstreamEvent { + /* unlink required */ + UNLINK, + + /* flush completed */ + FLUSHED, + + /* resynchronization required */ + RESYNC, + + /* video presentation statistics */ + VIDEO_PRES_STATS, + }; + + class SinkListener { + public: + virtual ~SinkListener(void) {} + + virtual void + onChannelQueue(RawChannel *channel, + struct mbuf_raw_video_frame *frame) = 0; + + virtual void + onChannelDownstreamEvent(RawChannel *channel, + const struct pomp_msg *event) = 0; + }; + + class SourceListener { + public: + virtual ~SourceListener(void) {} + + virtual void + onChannelUpstreamEvent(RawChannel *channel, + const struct pomp_msg *event) = 0; + }; + + static const char *getDownstreamEventStr(DownstreamEvent val); + + static const char *getUpstreamEventStr(UpstreamEvent val); + + RawChannel(SinkListener *sinkListener); + + ~RawChannel(void) {} + + int queue(mbuf_raw_video_frame *frame); + + int flush(void); + + bool isFlushPending(void) + { + return mFlushPending; + } + + int flushDone(void); + + int resync(void); + + int unlink(void); + + int teardown(void); + + int sendVideoPresStats(VideoPresStats *stats); + + int sendDownstreamEvent(DownstreamEvent downstreamEvent); + + SourceListener *getSourceListener(void) + { + return mSourceListener; + } + + void setSourceListener(SourceListener *sourceListener) + { + mSourceListener = sourceListener; + } + + void *getKey(void) + { + return mKey; + } + + int getRawVideoMediaFormatCaps(const struct vdef_raw_format **caps) + { + if (!caps) + return -EINVAL; + *caps = mRawVideoMediaFormatCaps; + return mRawVideoMediaFormatCapsCount; + } + +protected: + void setKey(void *key) + { + mKey = key; + } + + void setRawVideoMediaFormatCaps(const struct vdef_raw_format *caps, + int count) + { + mRawVideoMediaFormatCaps = caps; + mRawVideoMediaFormatCapsCount = count; + } + + struct mbuf_raw_video_frame_queue *getQueue(void) + { + return mQueue; + } + + void setQueue(struct mbuf_raw_video_frame_queue *queue) + { + mQueue = queue; + } + + struct mbuf_pool *getPool(void) + { + return mPool; + } + + void setPool(struct mbuf_pool *pool) + { + mPool = pool; + } + +private: + SinkListener *mSinkListener; + SourceListener *mSourceListener; + void *mKey; + const struct vdef_raw_format *mRawVideoMediaFormatCaps; + int mRawVideoMediaFormatCapsCount; + struct mbuf_raw_video_frame_queue *mQueue; + struct mbuf_pool *mPool; + bool mFlushPending; +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_CHANNEL_RAW_VIDEO_HPP_ */ diff --git a/libpdraw/src/pdraw_decoder_video.cpp b/libpdraw/src/pdraw_decoder_video.cpp index b40f6bd..669ec8a 100644 --- a/libpdraw/src/pdraw_decoder_video.cpp +++ b/libpdraw/src/pdraw_decoder_video.cpp @@ -1,908 +1,908 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Video decoder element - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_vdec -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_decoder_video.hpp" -#include "pdraw_session.hpp" -#include "pdraw_utils.hpp" -#if BUILD_LIBVIDEO_DECODE_MEDIACODEC -# include -#endif - -#include -#include - -#include - -namespace Pdraw { - - -const struct vdec_cbs VideoDecoder::mDecoderCbs = { - .frame_output = &VideoDecoder::frameOutputCb, - .flush = &VideoDecoder::flushCb, - .stop = &VideoDecoder::stopCb, -}; - - -VideoDecoder::VideoDecoder(Session *session, - Element::Listener *elementListener, - RawSource::Listener *sourceListener) : - CodedToRawFilterElement(session, - elementListener, - 1, - nullptr, - 0, - 1, - sourceListener), - mInputMedia(nullptr), mOutputMedia(nullptr), - mInputBufferPool(nullptr), mInputBufferQueue(nullptr), - mVdec(nullptr), mIsFlushed(true), - mInputChannelFlushPending(false), mResyncPending(false), - mVdecFlushPending(false), mVdecStopPending(false), - mCompleteStopPendingCount(0) -{ - const struct vdef_coded_format *supportedInputFormats; - int supportedInputFormatsCount; - - Element::setClassName(__func__); - - /* Supported input formats */ - supportedInputFormatsCount = vdec_get_supported_input_formats( - VDEC_DECODER_IMPLEM_AUTO, &supportedInputFormats); - if (supportedInputFormatsCount < 0) { - PDRAW_LOG_ERRNO("vdec_get_supported_input_formats", - -supportedInputFormatsCount); - } else { - setCodedVideoMediaFormatCaps(supportedInputFormats, - supportedInputFormatsCount); - } - - setState(CREATED); -} - - -VideoDecoder::~VideoDecoder(void) -{ - int ret; - - if (mState != STOPPED && mState != CREATED) - PDRAW_LOGW("decoder is still running"); - - if (mVdec) { - ret = vdec_destroy(mVdec); - if (ret < 0) - PDRAW_LOG_ERRNO("vdec_destroy", -ret); - } - - if (mOutputMedia != nullptr) - PDRAW_LOGW("output media was not properly removed"); -} - - -int VideoDecoder::start(void) -{ - int ret = 0; - enum vdef_coded_data_format fmt = VDEF_CODED_DATA_FORMAT_UNKNOWN; - - if ((mState == STARTED) || (mState == STARTING)) { - return 0; - } - if (mState != CREATED) { - PDRAW_LOGE("decoder is not created"); - return -EPROTO; - } - setState(STARTING); - - /* Get the input media and port */ - CodedSink::lock(); - unsigned int inputMediaCount = getInputMediaCount(); - if (inputMediaCount != 1) { - CodedSink::unlock(); - PDRAW_LOGE("invalid input media count"); - return -EPROTO; - } - mInputMedia = getInputMedia(0); - if (mInputMedia == nullptr) { - CodedSink::unlock(); - PDRAW_LOGE("invalid input media"); - return -EPROTO; - } - InputPort *port = getInputPort(mInputMedia); - if (port == nullptr) { - CodedSink::unlock(); - PDRAW_LOGE("invalid input port"); - return -EPROTO; - } - - fmt = mInputMedia->format.data_format; - if (fmt == VDEF_CODED_DATA_FORMAT_UNKNOWN) { - CodedSink::unlock(); - PDRAW_LOGE("invalid input data format"); - return -EPROTO; - } - - /* Initialize the decoder */ - struct vdec_config cfg = {}; - cfg.implem = VDEC_DECODER_IMPLEM_AUTO; - cfg.encoding = mInputMedia->format.encoding; - cfg.low_delay = - (mInputMedia->playbackType == PDRAW_PLAYBACK_TYPE_LIVE) ? 1 : 0; -#ifdef BCM_VIDEOCORE - cfg.preferred_output_format = VDEF_BCM_MMAL_OPAQUE; -#endif /* BCM_VIDEOCORE */ - cfg.android_jvm = mSession->getAndroidJvm(); - cfg.gen_grey_idr = 1; -#if BUILD_LIBVIDEO_DECODE_MEDIACODEC - struct vdec_config_mediacodec mediacodec_cfg = {}; - if (vdec_get_auto_implem() == VDEC_DECODER_IMPLEM_MEDIACODEC) { - mediacodec_cfg.implem = VDEC_DECODER_IMPLEM_MEDIACODEC; - mediacodec_cfg.fake_frame_num = true; - cfg.implem_cfg = (struct vdec_config_impl *)&mediacodec_cfg; - } -#endif - ret = vdec_new(mSession->getLoop(), &cfg, &mDecoderCbs, this, &mVdec); - if (ret < 0) { - CodedSink::unlock(); - PDRAW_LOG_ERRNO("vdec_new", -ret); - return ret; - } - - /* Configure the decoder */ - const uint8_t *vps = nullptr, *sps = nullptr, *pps = nullptr; - size_t vpsSize = 0, spsSize = 0, ppsSize = 0; - ret = mInputMedia->getPs( - &vps, &vpsSize, &sps, &spsSize, &pps, &ppsSize); - if (ret < 0) { - CodedSink::unlock(); - PDRAW_LOG_ERRNO("media->getPs", -ret); - return ret; - } - - uint32_t start; - size_t prefix_size = (fmt == VDEF_CODED_DATA_FORMAT_RAW_NALU) ? 0 : 4; - - uint8_t *vpsBuffer = nullptr; - if (mInputMedia->format.encoding == VDEF_ENCODING_H265) { - vpsBuffer = (uint8_t *)malloc(prefix_size + vpsSize); - if (vpsBuffer == nullptr) { - CodedSink::unlock(); - PDRAW_LOG_ERRNO("malloc:VPS", ENOMEM); - return -ENOMEM; - } - - if (fmt != VDEF_CODED_DATA_FORMAT_RAW_NALU) { - start = (fmt == VDEF_CODED_DATA_FORMAT_BYTE_STREAM) - ? htonl(0x00000001) - : htonl(vpsSize); - memcpy(vpsBuffer, &start, sizeof(start)); - } - memcpy(vpsBuffer + prefix_size, vps, vpsSize); - } - - uint8_t *spsBuffer = (uint8_t *)malloc(prefix_size + spsSize); - if (spsBuffer == nullptr) { - CodedSink::unlock(); - PDRAW_LOG_ERRNO("malloc:SPS", ENOMEM); - free(vpsBuffer); - return -ENOMEM; - } - - if (fmt != VDEF_CODED_DATA_FORMAT_RAW_NALU) { - start = (fmt == VDEF_CODED_DATA_FORMAT_BYTE_STREAM) - ? htonl(0x00000001) - : htonl(spsSize); - memcpy(spsBuffer, &start, sizeof(start)); - } - memcpy(spsBuffer + prefix_size, sps, spsSize); - - uint8_t *ppsBuffer = (uint8_t *)malloc(prefix_size + ppsSize); - if (ppsBuffer == nullptr) { - CodedSink::unlock(); - PDRAW_LOG_ERRNO("malloc:PPS", ENOMEM); - free(vpsBuffer); - free(spsBuffer); - return -ENOMEM; - } - - if (fmt != VDEF_CODED_DATA_FORMAT_RAW_NALU) { - start = (fmt == VDEF_CODED_DATA_FORMAT_BYTE_STREAM) - ? htonl(0x00000001) - : htonl(ppsSize); - memcpy(ppsBuffer, &start, sizeof(start)); - } - memcpy(ppsBuffer + prefix_size, pps, ppsSize); - - switch (mInputMedia->format.encoding) { - case VDEF_ENCODING_H264: - ret = vdec_set_h264_ps(mVdec, - spsBuffer, - spsSize + prefix_size, - ppsBuffer, - ppsSize + prefix_size, - &mInputMedia->format); - if (ret < 0) { - CodedSink::unlock(); - PDRAW_LOG_ERRNO("vdec_set_h264_ps", -ret); - free(vpsBuffer); - free(spsBuffer); - free(ppsBuffer); - return ret; - } - break; - case VDEF_ENCODING_H265: - ret = vdec_set_h265_ps(mVdec, - vpsBuffer, - vpsSize + prefix_size, - spsBuffer, - spsSize + prefix_size, - ppsBuffer, - ppsSize + prefix_size, - &mInputMedia->format); - if (ret < 0) { - CodedSink::unlock(); - PDRAW_LOG_ERRNO("vdec_set_h265_ps", -ret); - free(vpsBuffer); - free(spsBuffer); - free(ppsBuffer); - return ret; - } - break; - default: - CodedSink::unlock(); - PDRAW_LOGE("unsupported input media encoding (%s)", - vdef_encoding_to_str(mInputMedia->format.encoding)); - free(vpsBuffer); - free(spsBuffer); - free(ppsBuffer); - return -EPROTO; - } - - free(vpsBuffer); - free(spsBuffer); - free(ppsBuffer); - - /* Setup the input port */ - port->channel->setKey(this); - mInputBufferQueue = vdec_get_input_buffer_queue(mVdec); - port->channel->setQueue(mInputBufferQueue); - mInputBufferPool = vdec_get_input_buffer_pool(mVdec); - port->channel->setPool(mInputBufferPool); - - CodedSink::unlock(); - - setState(STARTED); - - return 0; -} - - -int VideoDecoder::stop(void) -{ - int ret; - - if ((mState == STOPPED) || (mState == STOPPING)) - return 0; - if (mState != STARTED) { - PDRAW_LOGE("decoder is not started"); - return -EPROTO; - } - setState(STOPPING); - mVdecStopPending = true; - - /* Flush everything */ - ret = flush(); - if (ret < 0) - PDRAW_LOG_ERRNO("flush", -ret); - - /* When the flush is complete, stopping will be triggered */ - return ret; -} - - -int VideoDecoder::flush(void) -{ - int ret; - unsigned int outputChannelCount, i; - RawChannel *outputChannel; - - if (mIsFlushed) { - PDRAW_LOGD("decoder is already flushed, nothing to do"); - completeFlush(); - return 0; - } - - mVdecFlushPending = true; - - /* Flush the output channels (async) */ - RawSource::lock(); - if (mOutputMedia != nullptr) { - outputChannelCount = getOutputChannelCount(mOutputMedia); - for (i = 0; i < outputChannelCount; i++) { - outputChannel = getOutputChannel(mOutputMedia, i); - if (outputChannel == nullptr) { - PDRAW_LOGW( - "failed to get output channel " - "at index %d", - i); - continue; - } - ret = outputChannel->flush(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->flush", -ret); - } - } - RawSource::unlock(); - - /* Flush the decoder (async) - * (the input channel queue is flushed by vdec) */ - ret = vdec_flush(mVdec, 1); - if (ret < 0) - PDRAW_LOG_ERRNO("vdec_flush", -ret); - - return ret; -} - - -void VideoDecoder::completeFlush(void) -{ - int ret; - unsigned int outputChannelCount, i; - RawChannel *outputChannel; - bool pending = false; - - if (mVdecFlushPending) - return; - - RawSource::lock(); - if (mOutputMedia != nullptr) { - outputChannelCount = getOutputChannelCount(mOutputMedia); - for (i = 0; i < outputChannelCount; i++) { - outputChannel = getOutputChannel(mOutputMedia, i); - if (outputChannel == nullptr) { - PDRAW_LOGW( - "failed to get output channel " - "at index %d", - i); - continue; - } - if (outputChannel->isFlushPending()) { - pending = true; - break; - } - } - } - RawSource::unlock(); - - if (pending) - return; - - CodedSink::lock(); - if (mInputMedia != nullptr) { - mIsFlushed = true; - if (mInputChannelFlushPending) { - mInputChannelFlushPending = false; - CodedChannel *inputChannel = - getInputChannel(mInputMedia); - if (inputChannel == nullptr) { - PDRAW_LOGE("failed to get input channel"); - } else { - ret = inputChannel->flushDone(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->flushDone", - -ret); - } - } - } - CodedSink::unlock(); - - completeResync(); - - tryStop(); -} - - -int VideoDecoder::tryStop(void) -{ - int ret; - int outputChannelCount = 0, i; - - if (mState != STOPPING) - return 0; - - /* Teardown the output channels - * Note: loop downwards because calling teardown on a channel may or - * may not synchronously remove the channel from the output port */ - RawSource::lock(); - if (mOutputMedia != nullptr) { - outputChannelCount = getOutputChannelCount(mOutputMedia); - - for (i = outputChannelCount - 1; i >= 0; i--) { - RawChannel *channel = getOutputChannel(mOutputMedia, i); - if (channel == nullptr) { - PDRAW_LOGW("failed to get channel at index %d", - i); - continue; - } - ret = channel->teardown(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->teardown", -ret); - } - } - RawSource::unlock(); - - /* Stop the decoder */ - ret = vdec_stop(mVdec); - if (ret < 0) { - PDRAW_LOG_ERRNO("vdec_stop", -ret); - return ret; - } - - /* Remove the input port */ - CodedSink::lock(); - if (mInputMedia != nullptr) { - CodedChannel *channel = getInputChannel(mInputMedia); - if (channel == nullptr) { - PDRAW_LOGE("failed to get channel"); - } else { - channel->setQueue(nullptr); - channel->setPool(nullptr); - } - - ret = removeInputMedia(mInputMedia); - if (ret < 0) - PDRAW_LOG_ERRNO("removeInputMedia", -ret); - else - mInputMedia = nullptr; - } - CodedSink::unlock(); - - return 0; -} - - -void VideoDecoder::completeStop(void) -{ - int ret; - unsigned int outputChannelCount; - - mCompleteStopPendingCount--; - - RawSource::lock(); - if (mOutputMedia == nullptr) { - RawSource::unlock(); - goto exit; - } - outputChannelCount = getOutputChannelCount(mOutputMedia); - if (outputChannelCount > 0) { - RawSource::unlock(); - return; - } - - /* Remove the output port */ - if (RawSource::mListener) { - RawSource::mListener->onOutputMediaRemoved(this, mOutputMedia); - } - ret = removeOutputPort(mOutputMedia); - if (ret < 0) { - PDRAW_LOG_ERRNO("removeOutputPort", -ret); - } else { - delete mOutputMedia; - mOutputMedia = nullptr; - } - - RawSource::unlock(); - -exit: - if ((!mVdecStopPending) && (mCompleteStopPendingCount == 0)) - setState(STOPPED); -} - - -void VideoDecoder::resync(void) -{ - int ret; - - CodedSink::lock(); - - if (mResyncPending) { - CodedSink::unlock(); - PDRAW_LOGD( - "%s: decoder is already synchronizing, nothing to do", - __func__); - return; - } - - if (mIsFlushed) { - CodedSink::unlock(); - PDRAW_LOGD("%s: decoder is already flushed, nothing to do", - __func__); - return; - } - - mResyncPending = true; - - ret = vdec_flush(mVdec, 1); - if (ret < 0) - PDRAW_LOG_ERRNO("vdec_flush", -ret); - - CodedSink::unlock(); -} - - -void VideoDecoder::completeResync(void) -{ - int ret; - - CodedSink::lock(); - - if (!mResyncPending) { - CodedSink::unlock(); - return; - } - - CodedChannel *inputChannel = getInputChannel(mInputMedia); - if (inputChannel == nullptr) { - PDRAW_LOGE("failed to get input channel"); - } else { - ret = inputChannel->resync(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->resync", -ret); - } - - mResyncPending = false; - CodedSink::unlock(); -} - - -int VideoDecoder::createOutputMedia(struct vdef_raw_frame *frameInfo, - RawVideoMedia::Frame &frame) -{ - int ret; - - RawSource::lock(); - - mOutputMedia = new RawVideoMedia(mSession); - if (mOutputMedia == nullptr) { - RawSource::unlock(); - PDRAW_LOGE("output media allocation failed"); - return -ENOMEM; - } - std::string path = mInputMedia->getPath() + ">" + Element::getName() + - "$" + mOutputMedia->getName(); - mOutputMedia->setPath(path); - - ret = addOutputPort(mOutputMedia); - if (ret < 0) { - RawSource::unlock(); - PDRAW_LOG_ERRNO("addOutputPort", -ret); - return ret; - } - - mOutputMedia->format = frameInfo->format; - vdef_frame_to_format_info(&frameInfo->info, &mOutputMedia->info); - mOutputMedia->info.framerate = mInputMedia->info.framerate; - mOutputMedia->sessionMeta = mInputMedia->sessionMeta; - mOutputMedia->playbackType = mInputMedia->playbackType; - mOutputMedia->duration = mInputMedia->duration; - - RawSource::unlock(); - - if (RawSource::mListener) - RawSource::mListener->onOutputMediaAdded(this, mOutputMedia); - - return 0; -} - - -void VideoDecoder::onChannelQueue(CodedChannel *channel, - struct mbuf_coded_video_frame *frame) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - if (frame == nullptr) { - PDRAW_LOG_ERRNO("frame", EINVAL); - return; - } - if (mState != STARTED) { - PDRAW_LOGE("decoder is not started"); - return; - } - if ((mVdecFlushPending) || (mInputChannelFlushPending)) { - PDRAW_LOGI("frame input: flush pending, discard frame"); - return; - } - CodedSink::lock(); - struct mbuf_coded_video_frame_queue *queue = channel->getQueue(); - if (queue == nullptr) { - CodedSink::unlock(); - PDRAW_LOGE("invalid queue"); - return; - } - if (queue != mInputBufferQueue) { - CodedSink::unlock(); - PDRAW_LOGE("invalid input buffer queue"); - return; - } - - CodedSink::onChannelQueue(channel, frame); - mIsFlushed = false; - CodedSink::unlock(); -} - - -void VideoDecoder::onChannelFlush(CodedChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - PDRAW_LOGD("flushing input channel"); - mInputChannelFlushPending = true; - - int ret = flush(); - if (ret < 0) - PDRAW_LOG_ERRNO("flush", -ret); -} - - -void VideoDecoder::onChannelFlushed(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - Media *media = getOutputMediaFromChannel(channel->getKey()); - if (media == nullptr) { - PDRAW_LOGE("media not found"); - return; - } - PDRAW_LOGD("'%s': channel flushed media name=%s (channel key=%p)", - Element::getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - completeFlush(); -} - - -void VideoDecoder::onChannelTeardown(CodedChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - PDRAW_LOGD("tearing down input channel"); - - int ret = stop(); - if (ret < 0) - PDRAW_LOG_ERRNO("stop", -ret); -} - - -void VideoDecoder::onChannelUnlink(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSource::onChannelUnlink(channel); - - if (mState == STOPPING) { - mCompleteStopPendingCount++; - completeStop(); - } -} - - -void VideoDecoder::frameOutputCb(struct vdec_decoder *dec, - int status, - struct mbuf_raw_video_frame *out_frame, - void *userdata) -{ - int ret; - VideoDecoder *self = (VideoDecoder *)userdata; - struct vdef_raw_frame info; - struct mbuf_ancillary_data *ancillaryData; - CodedVideoMedia::Frame *in_meta; - RawVideoMedia::Frame out_meta; - unsigned int outputChannelCount, i; - RawChannel *channel; - - if (status != 0) { - PDRAW_LOGE("decoder error %d(%s), resync required", - -status, - strerror(-status)); - self->resync(); - return; - } - - if (userdata == nullptr) { - PDRAW_LOG_ERRNO("userdata", EINVAL); - return; - } - if (out_frame == nullptr) { - PDRAW_LOG_ERRNO("out_frame", EINVAL); - return; - } - if (self->mState != STARTED) { - PDRAW_LOGE("decoder is not started"); - return; - } - if ((self->mVdecFlushPending) || (self->mInputChannelFlushPending)) { - PDRAW_LOGI("frame output: flush pending, discard frame"); - return; - } - - self->CodedSink::lock(); - if (self->mInputMedia == nullptr) { - self->CodedSink::unlock(); - PDRAW_LOG_ERRNO("invalid input media", EPROTO); - return; - } - - ret = mbuf_raw_video_frame_get_frame_info(out_frame, &info); - if (ret < 0) { - self->CodedSink::unlock(); - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_get_frame_info", -ret); - return; - } - ret = mbuf_raw_video_frame_get_ancillary_data( - out_frame, - PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME, - &ancillaryData); - if (ret < 0) { - self->CodedSink::unlock(); - PDRAW_LOG_ERRNO( - "mbuf_raw_video_frame_get_ancillary_data:pdraw_in", - -ret); - return; - } - in_meta = (CodedVideoMedia::Frame *)mbuf_ancillary_data_get_buffer( - ancillaryData, NULL); - memset(&out_meta, 0, sizeof(out_meta)); - out_meta.ntpTimestamp = in_meta->ntpTimestamp; - out_meta.ntpUnskewedTimestamp = in_meta->ntpUnskewedTimestamp; - out_meta.ntpRawTimestamp = in_meta->ntpRawTimestamp; - out_meta.ntpRawUnskewedTimestamp = in_meta->ntpRawUnskewedTimestamp; - out_meta.playTimestamp = in_meta->playTimestamp; - out_meta.captureTimestamp = in_meta->captureTimestamp; - out_meta.localTimestamp = in_meta->localTimestamp; - out_meta.localTimestampPrecision = in_meta->localTimestampPrecision; - out_meta.recvStartTimestamp = in_meta->recvStartTimestamp; - out_meta.recvEndTimestamp = in_meta->recvEndTimestamp; - out_meta.demuxOutputTimestamp = in_meta->demuxOutputTimestamp; - out_meta.decoderOutputTimestamp = pdraw_getTimestampFromMbufFrame( - out_frame, VDEC_ANCILLARY_KEY_OUTPUT_TIME); - ret = mbuf_ancillary_data_unref(ancillaryData); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_ancillary_data_unref", -ret); - - /* Remove the PDrAW input ancillary data */ - ret = mbuf_raw_video_frame_remove_ancillary_data( - out_frame, PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_remove_ancillary_data", - -ret); - - self->CodedSink::unlock(); - self->RawSource::lock(); - - if (self->mOutputMedia == nullptr) { - /* Create the output media now that the format is known */ - ret = self->createOutputMedia(&info, out_meta); - if (ret < 0) { - self->RawSource::unlock(); - PDRAW_LOG_ERRNO("createOutputMedia", -ret); - return; - } - } else { - /* TODO: This should be generic for every filter element */ - /* Update the output media metadata */ - self->mOutputMedia->sessionMeta = - self->mInputMedia->sessionMeta; - } - - ret = mbuf_raw_video_frame_add_ancillary_buffer( - out_frame, - PDRAW_ANCILLARY_DATA_KEY_RAWVIDEOFRAME, - &out_meta, - sizeof(out_meta)); - if (ret < 0) { - self->RawSource::unlock(); - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_add_ancillary_buffer", - -ret); - return; - } - - /* Push the frame (unless it is silent) */ - if (!(info.info.flags & VDEF_FRAME_FLAG_SILENT)) { - outputChannelCount = - self->getOutputChannelCount(self->mOutputMedia); - for (i = 0; i < outputChannelCount; i++) { - channel = self->getOutputChannel(self->mOutputMedia, i); - if (channel == nullptr) { - PDRAW_LOGE("failed to get channel at index %d", - i); - continue; - } - ret = channel->queue(out_frame); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->queue", -ret); - } - } else { - PDRAW_LOGD("silent frame (ignored)"); - } - - self->RawSource::unlock(); -} - - -void VideoDecoder::flushCb(struct vdec_decoder *dec, void *userdata) -{ - VideoDecoder *self = (VideoDecoder *)userdata; - - if (userdata == nullptr) { - PDRAW_LOG_ERRNO("userdata", EINVAL); - return; - } - - PDRAW_LOGD("decoder is flushed"); - self->mVdecFlushPending = false; - - self->completeFlush(); -} - - -void VideoDecoder::stopCb(struct vdec_decoder *dec, void *userdata) -{ - VideoDecoder *self = (VideoDecoder *)userdata; - - if (userdata == nullptr) { - PDRAW_LOG_ERRNO("userdata", EINVAL); - return; - } - - PDRAW_LOGD("decoder is stopped"); - self->mVdecStopPending = false; - - self->mCompleteStopPendingCount++; - self->completeStop(); -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Video decoder element + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_vdec +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_decoder_video.hpp" +#include "pdraw_session.hpp" +#include "pdraw_utils.hpp" +#if BUILD_LIBVIDEO_DECODE_MEDIACODEC +# include +#endif + +#include +#include + +#include + +namespace Pdraw { + + +const struct vdec_cbs VideoDecoder::mDecoderCbs = { + .frame_output = &VideoDecoder::frameOutputCb, + .flush = &VideoDecoder::flushCb, + .stop = &VideoDecoder::stopCb, +}; + + +VideoDecoder::VideoDecoder(Session *session, + Element::Listener *elementListener, + RawSource::Listener *sourceListener) : + CodedToRawFilterElement(session, + elementListener, + 1, + nullptr, + 0, + 1, + sourceListener), + mInputMedia(nullptr), mOutputMedia(nullptr), + mInputBufferPool(nullptr), mInputBufferQueue(nullptr), + mVdec(nullptr), mIsFlushed(true), + mInputChannelFlushPending(false), mResyncPending(false), + mVdecFlushPending(false), mVdecStopPending(false), + mCompleteStopPendingCount(0) +{ + const struct vdef_coded_format *supportedInputFormats; + int supportedInputFormatsCount; + + Element::setClassName(__func__); + + /* Supported input formats */ + supportedInputFormatsCount = vdec_get_supported_input_formats( + VDEC_DECODER_IMPLEM_AUTO, &supportedInputFormats); + if (supportedInputFormatsCount < 0) { + PDRAW_LOG_ERRNO("vdec_get_supported_input_formats", + -supportedInputFormatsCount); + } else { + setCodedVideoMediaFormatCaps(supportedInputFormats, + supportedInputFormatsCount); + } + + setState(CREATED); +} + + +VideoDecoder::~VideoDecoder(void) +{ + int ret; + + if (mState != STOPPED && mState != CREATED) + PDRAW_LOGW("decoder is still running"); + + if (mVdec) { + ret = vdec_destroy(mVdec); + if (ret < 0) + PDRAW_LOG_ERRNO("vdec_destroy", -ret); + } + + if (mOutputMedia != nullptr) + PDRAW_LOGW("output media was not properly removed"); +} + + +int VideoDecoder::start(void) +{ + int ret = 0; + enum vdef_coded_data_format fmt = VDEF_CODED_DATA_FORMAT_UNKNOWN; + + if ((mState == STARTED) || (mState == STARTING)) { + return 0; + } + if (mState != CREATED) { + PDRAW_LOGE("decoder is not created"); + return -EPROTO; + } + setState(STARTING); + + /* Get the input media and port */ + CodedSink::lock(); + unsigned int inputMediaCount = getInputMediaCount(); + if (inputMediaCount != 1) { + CodedSink::unlock(); + PDRAW_LOGE("invalid input media count"); + return -EPROTO; + } + mInputMedia = getInputMedia(0); + if (mInputMedia == nullptr) { + CodedSink::unlock(); + PDRAW_LOGE("invalid input media"); + return -EPROTO; + } + InputPort *port = getInputPort(mInputMedia); + if (port == nullptr) { + CodedSink::unlock(); + PDRAW_LOGE("invalid input port"); + return -EPROTO; + } + + fmt = mInputMedia->format.data_format; + if (fmt == VDEF_CODED_DATA_FORMAT_UNKNOWN) { + CodedSink::unlock(); + PDRAW_LOGE("invalid input data format"); + return -EPROTO; + } + + /* Initialize the decoder */ + struct vdec_config cfg = {}; + cfg.implem = VDEC_DECODER_IMPLEM_AUTO; + cfg.encoding = mInputMedia->format.encoding; + cfg.low_delay = + (mInputMedia->playbackType == PDRAW_PLAYBACK_TYPE_LIVE) ? 1 : 0; +#ifdef BCM_VIDEOCORE + cfg.preferred_output_format = VDEF_BCM_MMAL_OPAQUE; +#endif /* BCM_VIDEOCORE */ + cfg.android_jvm = mSession->getAndroidJvm(); + cfg.gen_grey_idr = 1; +#if BUILD_LIBVIDEO_DECODE_MEDIACODEC + struct vdec_config_mediacodec mediacodec_cfg = {}; + if (vdec_get_auto_implem() == VDEC_DECODER_IMPLEM_MEDIACODEC) { + mediacodec_cfg.implem = VDEC_DECODER_IMPLEM_MEDIACODEC; + mediacodec_cfg.fake_frame_num = true; + cfg.implem_cfg = (struct vdec_config_impl *)&mediacodec_cfg; + } +#endif + ret = vdec_new(mSession->getLoop(), &cfg, &mDecoderCbs, this, &mVdec); + if (ret < 0) { + CodedSink::unlock(); + PDRAW_LOG_ERRNO("vdec_new", -ret); + return ret; + } + + /* Configure the decoder */ + const uint8_t *vps = nullptr, *sps = nullptr, *pps = nullptr; + size_t vpsSize = 0, spsSize = 0, ppsSize = 0; + ret = mInputMedia->getPs( + &vps, &vpsSize, &sps, &spsSize, &pps, &ppsSize); + if (ret < 0) { + CodedSink::unlock(); + PDRAW_LOG_ERRNO("media->getPs", -ret); + return ret; + } + + uint32_t start; + size_t prefix_size = (fmt == VDEF_CODED_DATA_FORMAT_RAW_NALU) ? 0 : 4; + + uint8_t *vpsBuffer = nullptr; + if (mInputMedia->format.encoding == VDEF_ENCODING_H265) { + vpsBuffer = (uint8_t *)malloc(prefix_size + vpsSize); + if (vpsBuffer == nullptr) { + CodedSink::unlock(); + PDRAW_LOG_ERRNO("malloc:VPS", ENOMEM); + return -ENOMEM; + } + + if (fmt != VDEF_CODED_DATA_FORMAT_RAW_NALU) { + start = (fmt == VDEF_CODED_DATA_FORMAT_BYTE_STREAM) + ? htonl(0x00000001) + : htonl(vpsSize); + memcpy(vpsBuffer, &start, sizeof(start)); + } + memcpy(vpsBuffer + prefix_size, vps, vpsSize); + } + + uint8_t *spsBuffer = (uint8_t *)malloc(prefix_size + spsSize); + if (spsBuffer == nullptr) { + CodedSink::unlock(); + PDRAW_LOG_ERRNO("malloc:SPS", ENOMEM); + free(vpsBuffer); + return -ENOMEM; + } + + if (fmt != VDEF_CODED_DATA_FORMAT_RAW_NALU) { + start = (fmt == VDEF_CODED_DATA_FORMAT_BYTE_STREAM) + ? htonl(0x00000001) + : htonl(spsSize); + memcpy(spsBuffer, &start, sizeof(start)); + } + memcpy(spsBuffer + prefix_size, sps, spsSize); + + uint8_t *ppsBuffer = (uint8_t *)malloc(prefix_size + ppsSize); + if (ppsBuffer == nullptr) { + CodedSink::unlock(); + PDRAW_LOG_ERRNO("malloc:PPS", ENOMEM); + free(vpsBuffer); + free(spsBuffer); + return -ENOMEM; + } + + if (fmt != VDEF_CODED_DATA_FORMAT_RAW_NALU) { + start = (fmt == VDEF_CODED_DATA_FORMAT_BYTE_STREAM) + ? htonl(0x00000001) + : htonl(ppsSize); + memcpy(ppsBuffer, &start, sizeof(start)); + } + memcpy(ppsBuffer + prefix_size, pps, ppsSize); + + switch (mInputMedia->format.encoding) { + case VDEF_ENCODING_H264: + ret = vdec_set_h264_ps(mVdec, + spsBuffer, + spsSize + prefix_size, + ppsBuffer, + ppsSize + prefix_size, + &mInputMedia->format); + if (ret < 0) { + CodedSink::unlock(); + PDRAW_LOG_ERRNO("vdec_set_h264_ps", -ret); + free(vpsBuffer); + free(spsBuffer); + free(ppsBuffer); + return ret; + } + break; + case VDEF_ENCODING_H265: + ret = vdec_set_h265_ps(mVdec, + vpsBuffer, + vpsSize + prefix_size, + spsBuffer, + spsSize + prefix_size, + ppsBuffer, + ppsSize + prefix_size, + &mInputMedia->format); + if (ret < 0) { + CodedSink::unlock(); + PDRAW_LOG_ERRNO("vdec_set_h265_ps", -ret); + free(vpsBuffer); + free(spsBuffer); + free(ppsBuffer); + return ret; + } + break; + default: + CodedSink::unlock(); + PDRAW_LOGE("unsupported input media encoding (%s)", + vdef_encoding_to_str(mInputMedia->format.encoding)); + free(vpsBuffer); + free(spsBuffer); + free(ppsBuffer); + return -EPROTO; + } + + free(vpsBuffer); + free(spsBuffer); + free(ppsBuffer); + + /* Setup the input port */ + port->channel->setKey(this); + mInputBufferQueue = vdec_get_input_buffer_queue(mVdec); + port->channel->setQueue(mInputBufferQueue); + mInputBufferPool = vdec_get_input_buffer_pool(mVdec); + port->channel->setPool(mInputBufferPool); + + CodedSink::unlock(); + + setState(STARTED); + + return 0; +} + + +int VideoDecoder::stop(void) +{ + int ret; + + if ((mState == STOPPED) || (mState == STOPPING)) + return 0; + if (mState != STARTED) { + PDRAW_LOGE("decoder is not started"); + return -EPROTO; + } + setState(STOPPING); + mVdecStopPending = true; + + /* Flush everything */ + ret = flush(); + if (ret < 0) + PDRAW_LOG_ERRNO("flush", -ret); + + /* When the flush is complete, stopping will be triggered */ + return ret; +} + + +int VideoDecoder::flush(void) +{ + int ret; + unsigned int outputChannelCount, i; + RawChannel *outputChannel; + + if (mIsFlushed) { + PDRAW_LOGD("decoder is already flushed, nothing to do"); + completeFlush(); + return 0; + } + + mVdecFlushPending = true; + + /* Flush the output channels (async) */ + RawSource::lock(); + if (mOutputMedia != nullptr) { + outputChannelCount = getOutputChannelCount(mOutputMedia); + for (i = 0; i < outputChannelCount; i++) { + outputChannel = getOutputChannel(mOutputMedia, i); + if (outputChannel == nullptr) { + PDRAW_LOGW( + "failed to get output channel " + "at index %d", + i); + continue; + } + ret = outputChannel->flush(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->flush", -ret); + } + } + RawSource::unlock(); + + /* Flush the decoder (async) + * (the input channel queue is flushed by vdec) */ + ret = vdec_flush(mVdec, 1); + if (ret < 0) + PDRAW_LOG_ERRNO("vdec_flush", -ret); + + return ret; +} + + +void VideoDecoder::completeFlush(void) +{ + int ret; + unsigned int outputChannelCount, i; + RawChannel *outputChannel; + bool pending = false; + + if (mVdecFlushPending) + return; + + RawSource::lock(); + if (mOutputMedia != nullptr) { + outputChannelCount = getOutputChannelCount(mOutputMedia); + for (i = 0; i < outputChannelCount; i++) { + outputChannel = getOutputChannel(mOutputMedia, i); + if (outputChannel == nullptr) { + PDRAW_LOGW( + "failed to get output channel " + "at index %d", + i); + continue; + } + if (outputChannel->isFlushPending()) { + pending = true; + break; + } + } + } + RawSource::unlock(); + + if (pending) + return; + + CodedSink::lock(); + if (mInputMedia != nullptr) { + mIsFlushed = true; + if (mInputChannelFlushPending) { + mInputChannelFlushPending = false; + CodedChannel *inputChannel = + getInputChannel(mInputMedia); + if (inputChannel == nullptr) { + PDRAW_LOGE("failed to get input channel"); + } else { + ret = inputChannel->flushDone(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->flushDone", + -ret); + } + } + } + CodedSink::unlock(); + + completeResync(); + + tryStop(); +} + + +int VideoDecoder::tryStop(void) +{ + int ret; + int outputChannelCount = 0, i; + + if (mState != STOPPING) + return 0; + + /* Teardown the output channels + * Note: loop downwards because calling teardown on a channel may or + * may not synchronously remove the channel from the output port */ + RawSource::lock(); + if (mOutputMedia != nullptr) { + outputChannelCount = getOutputChannelCount(mOutputMedia); + + for (i = outputChannelCount - 1; i >= 0; i--) { + RawChannel *channel = getOutputChannel(mOutputMedia, i); + if (channel == nullptr) { + PDRAW_LOGW("failed to get channel at index %d", + i); + continue; + } + ret = channel->teardown(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->teardown", -ret); + } + } + RawSource::unlock(); + + /* Stop the decoder */ + ret = vdec_stop(mVdec); + if (ret < 0) { + PDRAW_LOG_ERRNO("vdec_stop", -ret); + return ret; + } + + /* Remove the input port */ + CodedSink::lock(); + if (mInputMedia != nullptr) { + CodedChannel *channel = getInputChannel(mInputMedia); + if (channel == nullptr) { + PDRAW_LOGE("failed to get channel"); + } else { + channel->setQueue(nullptr); + channel->setPool(nullptr); + } + + ret = removeInputMedia(mInputMedia); + if (ret < 0) + PDRAW_LOG_ERRNO("removeInputMedia", -ret); + else + mInputMedia = nullptr; + } + CodedSink::unlock(); + + return 0; +} + + +void VideoDecoder::completeStop(void) +{ + int ret; + unsigned int outputChannelCount; + + mCompleteStopPendingCount--; + + RawSource::lock(); + if (mOutputMedia == nullptr) { + RawSource::unlock(); + goto exit; + } + outputChannelCount = getOutputChannelCount(mOutputMedia); + if (outputChannelCount > 0) { + RawSource::unlock(); + return; + } + + /* Remove the output port */ + if (RawSource::mListener) { + RawSource::mListener->onOutputMediaRemoved(this, mOutputMedia); + } + ret = removeOutputPort(mOutputMedia); + if (ret < 0) { + PDRAW_LOG_ERRNO("removeOutputPort", -ret); + } else { + delete mOutputMedia; + mOutputMedia = nullptr; + } + + RawSource::unlock(); + +exit: + if ((!mVdecStopPending) && (mCompleteStopPendingCount == 0)) + setState(STOPPED); +} + + +void VideoDecoder::resync(void) +{ + int ret; + + CodedSink::lock(); + + if (mResyncPending) { + CodedSink::unlock(); + PDRAW_LOGD( + "%s: decoder is already synchronizing, nothing to do", + __func__); + return; + } + + if (mIsFlushed) { + CodedSink::unlock(); + PDRAW_LOGD("%s: decoder is already flushed, nothing to do", + __func__); + return; + } + + mResyncPending = true; + + ret = vdec_flush(mVdec, 1); + if (ret < 0) + PDRAW_LOG_ERRNO("vdec_flush", -ret); + + CodedSink::unlock(); +} + + +void VideoDecoder::completeResync(void) +{ + int ret; + + CodedSink::lock(); + + if (!mResyncPending) { + CodedSink::unlock(); + return; + } + + CodedChannel *inputChannel = getInputChannel(mInputMedia); + if (inputChannel == nullptr) { + PDRAW_LOGE("failed to get input channel"); + } else { + ret = inputChannel->resync(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->resync", -ret); + } + + mResyncPending = false; + CodedSink::unlock(); +} + + +int VideoDecoder::createOutputMedia(struct vdef_raw_frame *frameInfo, + RawVideoMedia::Frame &frame) +{ + int ret; + + RawSource::lock(); + + mOutputMedia = new RawVideoMedia(mSession); + if (mOutputMedia == nullptr) { + RawSource::unlock(); + PDRAW_LOGE("output media allocation failed"); + return -ENOMEM; + } + std::string path = mInputMedia->getPath() + ">" + Element::getName() + + "$" + mOutputMedia->getName(); + mOutputMedia->setPath(path); + + ret = addOutputPort(mOutputMedia); + if (ret < 0) { + RawSource::unlock(); + PDRAW_LOG_ERRNO("addOutputPort", -ret); + return ret; + } + + mOutputMedia->format = frameInfo->format; + vdef_frame_to_format_info(&frameInfo->info, &mOutputMedia->info); + mOutputMedia->info.framerate = mInputMedia->info.framerate; + mOutputMedia->sessionMeta = mInputMedia->sessionMeta; + mOutputMedia->playbackType = mInputMedia->playbackType; + mOutputMedia->duration = mInputMedia->duration; + + RawSource::unlock(); + + if (RawSource::mListener) + RawSource::mListener->onOutputMediaAdded(this, mOutputMedia); + + return 0; +} + + +void VideoDecoder::onChannelQueue(CodedChannel *channel, + struct mbuf_coded_video_frame *frame) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + if (frame == nullptr) { + PDRAW_LOG_ERRNO("frame", EINVAL); + return; + } + if (mState != STARTED) { + PDRAW_LOGE("decoder is not started"); + return; + } + if ((mVdecFlushPending) || (mInputChannelFlushPending)) { + PDRAW_LOGI("frame input: flush pending, discard frame"); + return; + } + CodedSink::lock(); + struct mbuf_coded_video_frame_queue *queue = channel->getQueue(); + if (queue == nullptr) { + CodedSink::unlock(); + PDRAW_LOGE("invalid queue"); + return; + } + if (queue != mInputBufferQueue) { + CodedSink::unlock(); + PDRAW_LOGE("invalid input buffer queue"); + return; + } + + CodedSink::onChannelQueue(channel, frame); + mIsFlushed = false; + CodedSink::unlock(); +} + + +void VideoDecoder::onChannelFlush(CodedChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + PDRAW_LOGD("flushing input channel"); + mInputChannelFlushPending = true; + + int ret = flush(); + if (ret < 0) + PDRAW_LOG_ERRNO("flush", -ret); +} + + +void VideoDecoder::onChannelFlushed(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + Media *media = getOutputMediaFromChannel(channel->getKey()); + if (media == nullptr) { + PDRAW_LOGE("media not found"); + return; + } + PDRAW_LOGD("'%s': channel flushed media name=%s (channel key=%p)", + Element::getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + completeFlush(); +} + + +void VideoDecoder::onChannelTeardown(CodedChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + PDRAW_LOGD("tearing down input channel"); + + int ret = stop(); + if (ret < 0) + PDRAW_LOG_ERRNO("stop", -ret); +} + + +void VideoDecoder::onChannelUnlink(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSource::onChannelUnlink(channel); + + if (mState == STOPPING) { + mCompleteStopPendingCount++; + completeStop(); + } +} + + +void VideoDecoder::frameOutputCb(struct vdec_decoder *dec, + int status, + struct mbuf_raw_video_frame *out_frame, + void *userdata) +{ + int ret; + VideoDecoder *self = (VideoDecoder *)userdata; + struct vdef_raw_frame info; + struct mbuf_ancillary_data *ancillaryData; + CodedVideoMedia::Frame *in_meta; + RawVideoMedia::Frame out_meta; + unsigned int outputChannelCount, i; + RawChannel *channel; + + if (status != 0) { + PDRAW_LOGE("decoder error %d(%s), resync required", + -status, + strerror(-status)); + self->resync(); + return; + } + + if (userdata == nullptr) { + PDRAW_LOG_ERRNO("userdata", EINVAL); + return; + } + if (out_frame == nullptr) { + PDRAW_LOG_ERRNO("out_frame", EINVAL); + return; + } + if (self->mState != STARTED) { + PDRAW_LOGE("decoder is not started"); + return; + } + if ((self->mVdecFlushPending) || (self->mInputChannelFlushPending)) { + PDRAW_LOGI("frame output: flush pending, discard frame"); + return; + } + + self->CodedSink::lock(); + if (self->mInputMedia == nullptr) { + self->CodedSink::unlock(); + PDRAW_LOG_ERRNO("invalid input media", EPROTO); + return; + } + + ret = mbuf_raw_video_frame_get_frame_info(out_frame, &info); + if (ret < 0) { + self->CodedSink::unlock(); + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_get_frame_info", -ret); + return; + } + ret = mbuf_raw_video_frame_get_ancillary_data( + out_frame, + PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME, + &ancillaryData); + if (ret < 0) { + self->CodedSink::unlock(); + PDRAW_LOG_ERRNO( + "mbuf_raw_video_frame_get_ancillary_data:pdraw_in", + -ret); + return; + } + in_meta = (CodedVideoMedia::Frame *)mbuf_ancillary_data_get_buffer( + ancillaryData, NULL); + memset(&out_meta, 0, sizeof(out_meta)); + out_meta.ntpTimestamp = in_meta->ntpTimestamp; + out_meta.ntpUnskewedTimestamp = in_meta->ntpUnskewedTimestamp; + out_meta.ntpRawTimestamp = in_meta->ntpRawTimestamp; + out_meta.ntpRawUnskewedTimestamp = in_meta->ntpRawUnskewedTimestamp; + out_meta.playTimestamp = in_meta->playTimestamp; + out_meta.captureTimestamp = in_meta->captureTimestamp; + out_meta.localTimestamp = in_meta->localTimestamp; + out_meta.localTimestampPrecision = in_meta->localTimestampPrecision; + out_meta.recvStartTimestamp = in_meta->recvStartTimestamp; + out_meta.recvEndTimestamp = in_meta->recvEndTimestamp; + out_meta.demuxOutputTimestamp = in_meta->demuxOutputTimestamp; + out_meta.decoderOutputTimestamp = pdraw_getTimestampFromMbufFrame( + out_frame, VDEC_ANCILLARY_KEY_OUTPUT_TIME); + ret = mbuf_ancillary_data_unref(ancillaryData); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_ancillary_data_unref", -ret); + + /* Remove the PDrAW input ancillary data */ + ret = mbuf_raw_video_frame_remove_ancillary_data( + out_frame, PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_remove_ancillary_data", + -ret); + + self->CodedSink::unlock(); + self->RawSource::lock(); + + if (self->mOutputMedia == nullptr) { + /* Create the output media now that the format is known */ + ret = self->createOutputMedia(&info, out_meta); + if (ret < 0) { + self->RawSource::unlock(); + PDRAW_LOG_ERRNO("createOutputMedia", -ret); + return; + } + } else { + /* TODO: This should be generic for every filter element */ + /* Update the output media metadata */ + self->mOutputMedia->sessionMeta = + self->mInputMedia->sessionMeta; + } + + ret = mbuf_raw_video_frame_add_ancillary_buffer( + out_frame, + PDRAW_ANCILLARY_DATA_KEY_RAWVIDEOFRAME, + &out_meta, + sizeof(out_meta)); + if (ret < 0) { + self->RawSource::unlock(); + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_add_ancillary_buffer", + -ret); + return; + } + + /* Push the frame (unless it is silent) */ + if (!(info.info.flags & VDEF_FRAME_FLAG_SILENT)) { + outputChannelCount = + self->getOutputChannelCount(self->mOutputMedia); + for (i = 0; i < outputChannelCount; i++) { + channel = self->getOutputChannel(self->mOutputMedia, i); + if (channel == nullptr) { + PDRAW_LOGE("failed to get channel at index %d", + i); + continue; + } + ret = channel->queue(out_frame); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->queue", -ret); + } + } else { + PDRAW_LOGD("silent frame (ignored)"); + } + + self->RawSource::unlock(); +} + + +void VideoDecoder::flushCb(struct vdec_decoder *dec, void *userdata) +{ + VideoDecoder *self = (VideoDecoder *)userdata; + + if (userdata == nullptr) { + PDRAW_LOG_ERRNO("userdata", EINVAL); + return; + } + + PDRAW_LOGD("decoder is flushed"); + self->mVdecFlushPending = false; + + self->completeFlush(); +} + + +void VideoDecoder::stopCb(struct vdec_decoder *dec, void *userdata) +{ + VideoDecoder *self = (VideoDecoder *)userdata; + + if (userdata == nullptr) { + PDRAW_LOG_ERRNO("userdata", EINVAL); + return; + } + + PDRAW_LOGD("decoder is stopped"); + self->mVdecStopPending = false; + + self->mCompleteStopPendingCount++; + self->completeStop(); +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_decoder_video.hpp b/libpdraw/src/pdraw_decoder_video.hpp index 19ac5de..f3018ae 100644 --- a/libpdraw/src/pdraw_decoder_video.hpp +++ b/libpdraw/src/pdraw_decoder_video.hpp @@ -1,107 +1,107 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Video decoder element - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_DECODER_VIDEO_HPP_ -#define _PDRAW_DECODER_VIDEO_HPP_ - -#include "pdraw_element.hpp" - -#include - -#include -#include - -namespace Pdraw { - -class VideoDecoder : public CodedToRawFilterElement { -public: - VideoDecoder(Session *session, - Element::Listener *elementListener, - RawSource::Listener *sourceListener); - - ~VideoDecoder(void); - - int start(void); - - int stop(void); - - void completeFlush(void); - - void completeStop(void); - - void resync(void); - -private: - int createOutputMedia(struct vdef_raw_frame *frameInfo, - RawVideoMedia::Frame &frame); - - int flush(void); - - void completeResync(void); - - int tryStop(void); - - void onChannelQueue(CodedChannel *channel, - struct mbuf_coded_video_frame *buf); - - void onChannelFlush(CodedChannel *channel); - - void onChannelFlushed(RawChannel *channel); - - void onChannelTeardown(CodedChannel *channel); - - void onChannelUnlink(RawChannel *channel); - - static void frameOutputCb(struct vdec_decoder *dec, - int status, - struct mbuf_raw_video_frame *out_frame, - void *userdata); - - static void flushCb(struct vdec_decoder *dec, void *userdata); - - static void stopCb(struct vdec_decoder *dec, void *userdata); - - CodedVideoMedia *mInputMedia; - RawVideoMedia *mOutputMedia; - struct mbuf_pool *mInputBufferPool; - struct mbuf_coded_video_frame_queue *mInputBufferQueue; - struct vdec_decoder *mVdec; - bool mIsFlushed; - bool mInputChannelFlushPending; - bool mResyncPending; - bool mVdecFlushPending; - bool mVdecStopPending; - int mCompleteStopPendingCount; - static const struct vdec_cbs mDecoderCbs; -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_DECODER_VIDEO_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Video decoder element + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_DECODER_VIDEO_HPP_ +#define _PDRAW_DECODER_VIDEO_HPP_ + +#include "pdraw_element.hpp" + +#include + +#include +#include + +namespace Pdraw { + +class VideoDecoder : public CodedToRawFilterElement { +public: + VideoDecoder(Session *session, + Element::Listener *elementListener, + RawSource::Listener *sourceListener); + + ~VideoDecoder(void); + + int start(void); + + int stop(void); + + void completeFlush(void); + + void completeStop(void); + + void resync(void); + +private: + int createOutputMedia(struct vdef_raw_frame *frameInfo, + RawVideoMedia::Frame &frame); + + int flush(void); + + void completeResync(void); + + int tryStop(void); + + void onChannelQueue(CodedChannel *channel, + struct mbuf_coded_video_frame *buf); + + void onChannelFlush(CodedChannel *channel); + + void onChannelFlushed(RawChannel *channel); + + void onChannelTeardown(CodedChannel *channel); + + void onChannelUnlink(RawChannel *channel); + + static void frameOutputCb(struct vdec_decoder *dec, + int status, + struct mbuf_raw_video_frame *out_frame, + void *userdata); + + static void flushCb(struct vdec_decoder *dec, void *userdata); + + static void stopCb(struct vdec_decoder *dec, void *userdata); + + CodedVideoMedia *mInputMedia; + RawVideoMedia *mOutputMedia; + struct mbuf_pool *mInputBufferPool; + struct mbuf_coded_video_frame_queue *mInputBufferQueue; + struct vdec_decoder *mVdec; + bool mIsFlushed; + bool mInputChannelFlushPending; + bool mResyncPending; + bool mVdecFlushPending; + bool mVdecStopPending; + int mCompleteStopPendingCount; + static const struct vdec_cbs mDecoderCbs; +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_DECODER_VIDEO_HPP_ */ diff --git a/libpdraw/src/pdraw_demuxer.cpp b/libpdraw/src/pdraw_demuxer.cpp index 0c5361d..6e8d7b6 100644 --- a/libpdraw/src/pdraw_demuxer.cpp +++ b/libpdraw/src/pdraw_demuxer.cpp @@ -1,286 +1,286 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Generic demuxer - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_demuxer -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_demuxer.hpp" -#include "pdraw_session.hpp" - -namespace Pdraw { - - -Demuxer::~Demuxer(void) -{ - /* Remove any leftover idle callbacks */ - pomp_loop_idle_remove(mSession->getLoop(), callOpenResponse, this); - pomp_loop_idle_remove(mSession->getLoop(), callCloseResponse, this); - pomp_loop_idle_remove( - mSession->getLoop(), callOnUnrecoverableError, this); - pomp_loop_idle_remove(mSession->getLoop(), callReadyToPlay, this); - pomp_loop_idle_remove(mSession->getLoop(), callEndOfRange, this); - pomp_loop_idle_remove(mSession->getLoop(), callPlayResponse, this); - pomp_loop_idle_remove(mSession->getLoop(), callPauseResponse, this); - pomp_loop_idle_remove(mSession->getLoop(), callSeekResponse, this); -} - - -void Demuxer::openResponse(int status) -{ - if (mCalledOpenResp) { - PDRAW_LOGW("multiple openResponse call blocked"); - return; - } - mOpenRespStatusArgs.push(status); - pomp_loop_idle_add(mSession->getLoop(), callOpenResponse, this); - mCalledOpenResp = true; -} - - -void Demuxer::closeResponse(int status) -{ - mCloseRespStatusArgs.push(status); - pomp_loop_idle_add(mSession->getLoop(), callCloseResponse, this); -} - - -void Demuxer::onUnrecoverableError(int error) -{ - /* If openResponse was not yet called, call it instead */ - if (!mCalledOpenResp) { - openResponse(error); - return; - } - /* Report only the first error */ - if (mUnrecoverableError) - return; - - mUnrecoverableError = true; - - pomp_loop_idle_add(mSession->getLoop(), callOnUnrecoverableError, this); -} - - -int Demuxer::selectMedia(const struct pdraw_demuxer_media *medias, size_t count) -{ - if (mDemuxerListener == nullptr) - return -ENOSYS; - - return mDemuxerListener->demuxerSelectMedia( - mSession, mDemuxer, medias, count); -} - - -void Demuxer::readyToPlay(bool ready) -{ - /* Report only changes in value */ - if (mReadyToPlay == ready) - return; - - mReadyToPlay = ready; - - mReadyToPlayReadyArgs.push(ready); - pomp_loop_idle_add(mSession->getLoop(), callReadyToPlay, this); -} - - -void Demuxer::onEndOfRange(uint64_t timestamp) -{ - mEndOfRangeTimestampArgs.push(timestamp); - pomp_loop_idle_add(mSession->getLoop(), callEndOfRange, this); -} - - -void Demuxer::playResponse(int status, uint64_t timestamp, float speed) -{ - mPlayRespStatusArgs.push(status); - mPlayRespTimestampArgs.push(timestamp); - mPlayRespSpeedArgs.push(speed); - pomp_loop_idle_add(mSession->getLoop(), callPlayResponse, this); -} - - -void Demuxer::pauseResponse(int status, uint64_t timestamp) -{ - mPauseRespStatusArgs.push(status); - mPauseRespTimestampArgs.push(timestamp); - pomp_loop_idle_add(mSession->getLoop(), callPauseResponse, this); -} - - -void Demuxer::seekResponse(int status, uint64_t timestamp, float speed) -{ - mSeekRespStatusArgs.push(status); - mSeekRespTimestampArgs.push(timestamp); - mSeekRespSpeedArgs.push(speed); - pomp_loop_idle_add(mSession->getLoop(), callSeekResponse, this); -} - - -/** - * Demuxer listener calls from idle functions - */ - -void Demuxer::callOpenResponse(void *userdata) -{ - Demuxer *self = reinterpret_cast(userdata); - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - int status = self->mOpenRespStatusArgs.front(); - self->mOpenRespStatusArgs.pop(); - - if (self->mDemuxerListener == nullptr) - return; - - self->mDemuxerListener->demuxerOpenResponse( - self->mSession, self->mDemuxer, status); -} - - -void Demuxer::callCloseResponse(void *userdata) -{ - Demuxer *self = reinterpret_cast(userdata); - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - int status = self->mCloseRespStatusArgs.front(); - self->mCloseRespStatusArgs.pop(); - - if (self->mDemuxerListener == nullptr) - return; - - self->mDemuxerListener->demuxerCloseResponse( - self->mSession, self->mDemuxer, status); -} - - -void Demuxer::callOnUnrecoverableError(void *userdata) -{ - Demuxer *self = reinterpret_cast(userdata); - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - if (self->mDemuxerListener == nullptr) - return; - - self->mDemuxerListener->onDemuxerUnrecoverableError(self->mSession, - self->mDemuxer); -} - - -void Demuxer::callReadyToPlay(void *userdata) -{ - Demuxer *self = reinterpret_cast(userdata); - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - bool ready = self->mReadyToPlayReadyArgs.front(); - self->mReadyToPlayReadyArgs.pop(); - - if (self->mDemuxerListener == nullptr) - return; - - self->mDemuxerListener->demuxerReadyToPlay( - self->mSession, self->mDemuxer, ready); -} - - -void Demuxer::callEndOfRange(void *userdata) -{ - Demuxer *self = reinterpret_cast(userdata); - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - uint64_t timestamp = self->mEndOfRangeTimestampArgs.front(); - self->mEndOfRangeTimestampArgs.pop(); - - if (self->mDemuxerListener == nullptr) - return; - - self->mDemuxerListener->onDemuxerEndOfRange( - self->mSession, self->mDemuxer, timestamp); -} - - -void Demuxer::callPlayResponse(void *userdata) -{ - Demuxer *self = reinterpret_cast(userdata); - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - int status = self->mPlayRespStatusArgs.front(); - uint64_t timestamp = self->mPlayRespTimestampArgs.front(); - float speed = self->mPlayRespSpeedArgs.front(); - self->mPlayRespStatusArgs.pop(); - self->mPlayRespTimestampArgs.pop(); - self->mPlayRespSpeedArgs.pop(); - - if (self->mDemuxerListener == nullptr) - return; - - self->mDemuxerListener->demuxerPlayResponse( - self->mSession, self->mDemuxer, status, timestamp, speed); -} - - -void Demuxer::callPauseResponse(void *userdata) -{ - Demuxer *self = reinterpret_cast(userdata); - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - int status = self->mPauseRespStatusArgs.front(); - uint64_t timestamp = self->mPauseRespTimestampArgs.front(); - self->mPauseRespStatusArgs.pop(); - self->mPauseRespTimestampArgs.pop(); - - if (self->mDemuxerListener == nullptr) - return; - - self->mDemuxerListener->demuxerPauseResponse( - self->mSession, self->mDemuxer, status, timestamp); -} - - -void Demuxer::callSeekResponse(void *userdata) -{ - Demuxer *self = reinterpret_cast(userdata); - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - int status = self->mSeekRespStatusArgs.front(); - uint64_t timestamp = self->mSeekRespTimestampArgs.front(); - float speed = self->mSeekRespSpeedArgs.front(); - self->mSeekRespStatusArgs.pop(); - self->mSeekRespTimestampArgs.pop(); - self->mSeekRespSpeedArgs.pop(); - - if (self->mDemuxerListener == nullptr) - return; - - self->mDemuxerListener->demuxerSeekResponse( - self->mSession, self->mDemuxer, status, timestamp, speed); -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Generic demuxer + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_demuxer +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_demuxer.hpp" +#include "pdraw_session.hpp" + +namespace Pdraw { + + +Demuxer::~Demuxer(void) +{ + /* Remove any leftover idle callbacks */ + pomp_loop_idle_remove(mSession->getLoop(), callOpenResponse, this); + pomp_loop_idle_remove(mSession->getLoop(), callCloseResponse, this); + pomp_loop_idle_remove( + mSession->getLoop(), callOnUnrecoverableError, this); + pomp_loop_idle_remove(mSession->getLoop(), callReadyToPlay, this); + pomp_loop_idle_remove(mSession->getLoop(), callEndOfRange, this); + pomp_loop_idle_remove(mSession->getLoop(), callPlayResponse, this); + pomp_loop_idle_remove(mSession->getLoop(), callPauseResponse, this); + pomp_loop_idle_remove(mSession->getLoop(), callSeekResponse, this); +} + + +void Demuxer::openResponse(int status) +{ + if (mCalledOpenResp) { + PDRAW_LOGW("multiple openResponse call blocked"); + return; + } + mOpenRespStatusArgs.push(status); + pomp_loop_idle_add(mSession->getLoop(), callOpenResponse, this); + mCalledOpenResp = true; +} + + +void Demuxer::closeResponse(int status) +{ + mCloseRespStatusArgs.push(status); + pomp_loop_idle_add(mSession->getLoop(), callCloseResponse, this); +} + + +void Demuxer::onUnrecoverableError(int error) +{ + /* If openResponse was not yet called, call it instead */ + if (!mCalledOpenResp) { + openResponse(error); + return; + } + /* Report only the first error */ + if (mUnrecoverableError) + return; + + mUnrecoverableError = true; + + pomp_loop_idle_add(mSession->getLoop(), callOnUnrecoverableError, this); +} + + +int Demuxer::selectMedia(const struct pdraw_demuxer_media *medias, size_t count) +{ + if (mDemuxerListener == nullptr) + return -ENOSYS; + + return mDemuxerListener->demuxerSelectMedia( + mSession, mDemuxer, medias, count); +} + + +void Demuxer::readyToPlay(bool ready) +{ + /* Report only changes in value */ + if (mReadyToPlay == ready) + return; + + mReadyToPlay = ready; + + mReadyToPlayReadyArgs.push(ready); + pomp_loop_idle_add(mSession->getLoop(), callReadyToPlay, this); +} + + +void Demuxer::onEndOfRange(uint64_t timestamp) +{ + mEndOfRangeTimestampArgs.push(timestamp); + pomp_loop_idle_add(mSession->getLoop(), callEndOfRange, this); +} + + +void Demuxer::playResponse(int status, uint64_t timestamp, float speed) +{ + mPlayRespStatusArgs.push(status); + mPlayRespTimestampArgs.push(timestamp); + mPlayRespSpeedArgs.push(speed); + pomp_loop_idle_add(mSession->getLoop(), callPlayResponse, this); +} + + +void Demuxer::pauseResponse(int status, uint64_t timestamp) +{ + mPauseRespStatusArgs.push(status); + mPauseRespTimestampArgs.push(timestamp); + pomp_loop_idle_add(mSession->getLoop(), callPauseResponse, this); +} + + +void Demuxer::seekResponse(int status, uint64_t timestamp, float speed) +{ + mSeekRespStatusArgs.push(status); + mSeekRespTimestampArgs.push(timestamp); + mSeekRespSpeedArgs.push(speed); + pomp_loop_idle_add(mSession->getLoop(), callSeekResponse, this); +} + + +/** + * Demuxer listener calls from idle functions + */ + +void Demuxer::callOpenResponse(void *userdata) +{ + Demuxer *self = reinterpret_cast(userdata); + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + int status = self->mOpenRespStatusArgs.front(); + self->mOpenRespStatusArgs.pop(); + + if (self->mDemuxerListener == nullptr) + return; + + self->mDemuxerListener->demuxerOpenResponse( + self->mSession, self->mDemuxer, status); +} + + +void Demuxer::callCloseResponse(void *userdata) +{ + Demuxer *self = reinterpret_cast(userdata); + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + int status = self->mCloseRespStatusArgs.front(); + self->mCloseRespStatusArgs.pop(); + + if (self->mDemuxerListener == nullptr) + return; + + self->mDemuxerListener->demuxerCloseResponse( + self->mSession, self->mDemuxer, status); +} + + +void Demuxer::callOnUnrecoverableError(void *userdata) +{ + Demuxer *self = reinterpret_cast(userdata); + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + if (self->mDemuxerListener == nullptr) + return; + + self->mDemuxerListener->onDemuxerUnrecoverableError(self->mSession, + self->mDemuxer); +} + + +void Demuxer::callReadyToPlay(void *userdata) +{ + Demuxer *self = reinterpret_cast(userdata); + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + bool ready = self->mReadyToPlayReadyArgs.front(); + self->mReadyToPlayReadyArgs.pop(); + + if (self->mDemuxerListener == nullptr) + return; + + self->mDemuxerListener->demuxerReadyToPlay( + self->mSession, self->mDemuxer, ready); +} + + +void Demuxer::callEndOfRange(void *userdata) +{ + Demuxer *self = reinterpret_cast(userdata); + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + uint64_t timestamp = self->mEndOfRangeTimestampArgs.front(); + self->mEndOfRangeTimestampArgs.pop(); + + if (self->mDemuxerListener == nullptr) + return; + + self->mDemuxerListener->onDemuxerEndOfRange( + self->mSession, self->mDemuxer, timestamp); +} + + +void Demuxer::callPlayResponse(void *userdata) +{ + Demuxer *self = reinterpret_cast(userdata); + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + int status = self->mPlayRespStatusArgs.front(); + uint64_t timestamp = self->mPlayRespTimestampArgs.front(); + float speed = self->mPlayRespSpeedArgs.front(); + self->mPlayRespStatusArgs.pop(); + self->mPlayRespTimestampArgs.pop(); + self->mPlayRespSpeedArgs.pop(); + + if (self->mDemuxerListener == nullptr) + return; + + self->mDemuxerListener->demuxerPlayResponse( + self->mSession, self->mDemuxer, status, timestamp, speed); +} + + +void Demuxer::callPauseResponse(void *userdata) +{ + Demuxer *self = reinterpret_cast(userdata); + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + int status = self->mPauseRespStatusArgs.front(); + uint64_t timestamp = self->mPauseRespTimestampArgs.front(); + self->mPauseRespStatusArgs.pop(); + self->mPauseRespTimestampArgs.pop(); + + if (self->mDemuxerListener == nullptr) + return; + + self->mDemuxerListener->demuxerPauseResponse( + self->mSession, self->mDemuxer, status, timestamp); +} + + +void Demuxer::callSeekResponse(void *userdata) +{ + Demuxer *self = reinterpret_cast(userdata); + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + int status = self->mSeekRespStatusArgs.front(); + uint64_t timestamp = self->mSeekRespTimestampArgs.front(); + float speed = self->mSeekRespSpeedArgs.front(); + self->mSeekRespStatusArgs.pop(); + self->mSeekRespTimestampArgs.pop(); + self->mSeekRespSpeedArgs.pop(); + + if (self->mDemuxerListener == nullptr) + return; + + self->mDemuxerListener->demuxerSeekResponse( + self->mSession, self->mDemuxer, status, timestamp, speed); +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_demuxer.hpp b/libpdraw/src/pdraw_demuxer.hpp index 4666e7e..c581755 100644 --- a/libpdraw/src/pdraw_demuxer.hpp +++ b/libpdraw/src/pdraw_demuxer.hpp @@ -1,147 +1,147 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Generic demuxer - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_DEMUXER_HPP_ -#define _PDRAW_DEMUXER_HPP_ - -#include "pdraw_element.hpp" -#include "pdraw_media.hpp" - -#include - -#include - -namespace Pdraw { - - -#define DEMUXER_OUTPUT_BUFFER_COUNT (30) - - -class Demuxer : public CodedSourceElement { -public: - virtual ~Demuxer(void); - - virtual int flush(void) = 0; - - virtual int play(float speed = 1.0f) = 0; - - virtual bool isReadyToPlay(void) = 0; - - virtual bool isPaused(void) = 0; - - virtual int previous(void) = 0; - - virtual int next(void) = 0; - - virtual int seek(int64_t delta, bool exact = false) = 0; - - virtual int seekTo(uint64_t timestamp, bool exact = false) = 0; - - virtual uint64_t getDuration(void) = 0; - - virtual uint64_t getCurrentTime(void) = 0; - - IPdraw::IDemuxer *getDemuxer(void) - { - return mDemuxer; - } - - IPdraw::IDemuxer::Listener *getDemuxerListener(void) - { - return mDemuxerListener; - } - -protected: - Demuxer(Session *session, - Element::Listener *elementListener, - CodedSource::Listener *sourceListener, - IPdraw::IDemuxer *demuxer, - IPdraw::IDemuxer::Listener *demuxerListener) : - CodedSourceElement(session, - elementListener, - UINT_MAX, - sourceListener), - mDemuxer(demuxer), mDemuxerListener(demuxerListener), - mReadyToPlay(false), mUnrecoverableError(false), - mCalledOpenResp(false) - { - } - - void openResponse(int status); - - void closeResponse(int status); - - void onUnrecoverableError(int error = -EPROTO); - - int selectMedia(const struct pdraw_demuxer_media *medias, size_t count); - - void readyToPlay(bool ready); - - void onEndOfRange(uint64_t timestamp); - - void playResponse(int status, uint64_t timestamp, float speed); - - void pauseResponse(int status, uint64_t timestamp); - - void seekResponse(int status, uint64_t timestamp, float speed); - - IPdraw::IDemuxer *mDemuxer; - IPdraw::IDemuxer::Listener *mDemuxerListener; - bool mReadyToPlay; - bool mUnrecoverableError; - bool mCalledOpenResp; - - /* Demuxer listener calls from idle functions */ - static void callOpenResponse(void *userdata); - std::queue mOpenRespStatusArgs; - static void callCloseResponse(void *userdata); - std::queue mCloseRespStatusArgs; - static void callOnUnrecoverableError(void *userdata); - /* Note: callSelectMedia omitted: function has to be synchronous */ - static void callReadyToPlay(void *userdata); - std::queue mReadyToPlayReadyArgs; - static void callEndOfRange(void *userdata); - std::queue mEndOfRangeTimestampArgs; - static void callPlayResponse(void *userdata); - std::queue mPlayRespStatusArgs; - std::queue mPlayRespTimestampArgs; - std::queue mPlayRespSpeedArgs; - static void callPauseResponse(void *userdata); - std::queue mPauseRespStatusArgs; - std::queue mPauseRespTimestampArgs; - static void callSeekResponse(void *userdata); - std::queue mSeekRespStatusArgs; - std::queue mSeekRespTimestampArgs; - std::queue mSeekRespSpeedArgs; -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_DEMUXER_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Generic demuxer + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_DEMUXER_HPP_ +#define _PDRAW_DEMUXER_HPP_ + +#include "pdraw_element.hpp" +#include "pdraw_media.hpp" + +#include + +#include + +namespace Pdraw { + + +#define DEMUXER_OUTPUT_BUFFER_COUNT (30) + + +class Demuxer : public CodedSourceElement { +public: + virtual ~Demuxer(void); + + virtual int flush(void) = 0; + + virtual int play(float speed = 1.0f) = 0; + + virtual bool isReadyToPlay(void) = 0; + + virtual bool isPaused(void) = 0; + + virtual int previous(void) = 0; + + virtual int next(void) = 0; + + virtual int seek(int64_t delta, bool exact = false) = 0; + + virtual int seekTo(uint64_t timestamp, bool exact = false) = 0; + + virtual uint64_t getDuration(void) = 0; + + virtual uint64_t getCurrentTime(void) = 0; + + IPdraw::IDemuxer *getDemuxer(void) + { + return mDemuxer; + } + + IPdraw::IDemuxer::Listener *getDemuxerListener(void) + { + return mDemuxerListener; + } + +protected: + Demuxer(Session *session, + Element::Listener *elementListener, + CodedSource::Listener *sourceListener, + IPdraw::IDemuxer *demuxer, + IPdraw::IDemuxer::Listener *demuxerListener) : + CodedSourceElement(session, + elementListener, + UINT_MAX, + sourceListener), + mDemuxer(demuxer), mDemuxerListener(demuxerListener), + mReadyToPlay(false), mUnrecoverableError(false), + mCalledOpenResp(false) + { + } + + void openResponse(int status); + + void closeResponse(int status); + + void onUnrecoverableError(int error = -EPROTO); + + int selectMedia(const struct pdraw_demuxer_media *medias, size_t count); + + void readyToPlay(bool ready); + + void onEndOfRange(uint64_t timestamp); + + void playResponse(int status, uint64_t timestamp, float speed); + + void pauseResponse(int status, uint64_t timestamp); + + void seekResponse(int status, uint64_t timestamp, float speed); + + IPdraw::IDemuxer *mDemuxer; + IPdraw::IDemuxer::Listener *mDemuxerListener; + bool mReadyToPlay; + bool mUnrecoverableError; + bool mCalledOpenResp; + + /* Demuxer listener calls from idle functions */ + static void callOpenResponse(void *userdata); + std::queue mOpenRespStatusArgs; + static void callCloseResponse(void *userdata); + std::queue mCloseRespStatusArgs; + static void callOnUnrecoverableError(void *userdata); + /* Note: callSelectMedia omitted: function has to be synchronous */ + static void callReadyToPlay(void *userdata); + std::queue mReadyToPlayReadyArgs; + static void callEndOfRange(void *userdata); + std::queue mEndOfRangeTimestampArgs; + static void callPlayResponse(void *userdata); + std::queue mPlayRespStatusArgs; + std::queue mPlayRespTimestampArgs; + std::queue mPlayRespSpeedArgs; + static void callPauseResponse(void *userdata); + std::queue mPauseRespStatusArgs; + std::queue mPauseRespTimestampArgs; + static void callSeekResponse(void *userdata); + std::queue mSeekRespStatusArgs; + std::queue mSeekRespTimestampArgs; + std::queue mSeekRespSpeedArgs; +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_DEMUXER_HPP_ */ diff --git a/libpdraw/src/pdraw_demuxer_record.cpp b/libpdraw/src/pdraw_demuxer_record.cpp index 77991fb..a77f6df 100644 --- a/libpdraw/src/pdraw_demuxer_record.cpp +++ b/libpdraw/src/pdraw_demuxer_record.cpp @@ -1,1979 +1,1979 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Recording demuxer - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_dmxrec -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_demuxer_record.hpp" -#include "pdraw_session.hpp" -#include "pdraw_utils.hpp" - -#include -#include -#include -#include -#include - -#include - -#include -#include - -namespace Pdraw { - - -const struct h264_ctx_cbs RecordDemuxer::VideoMedia::mH264ReaderCbs = { - .au_end = nullptr, - .nalu_begin = nullptr, - .nalu_end = nullptr, - .slice = nullptr, - .slice_data_begin = nullptr, - .slice_data_end = nullptr, - .slice_data_mb = nullptr, - .sps = nullptr, - .pps = nullptr, - .aud = nullptr, - .sei = nullptr, - .sei_buffering_period = nullptr, - .sei_pic_timing = &RecordDemuxer::VideoMedia::h264PicTimingSeiCb, - .sei_pan_scan_rect = nullptr, - .sei_filler_payload = nullptr, - .sei_user_data_registered = nullptr, - .sei_user_data_unregistered = - &RecordDemuxer::VideoMedia::h264UserDataSeiCb, - .sei_recovery_point = nullptr, -}; - - -const struct h265_ctx_cbs RecordDemuxer::VideoMedia::mH265ReaderCbs = { - .nalu_begin = nullptr, - .nalu_end = nullptr, - .au_end = nullptr, - .vps = nullptr, - .sps = nullptr, - .pps = nullptr, - .aud = nullptr, - .sei = nullptr, - .sei_user_data_unregistered = - &RecordDemuxer::VideoMedia::h265UserDataSeiCb, - .sei_recovery_point = nullptr, - .sei_time_code = &RecordDemuxer::VideoMedia::h265TimeCodeSeiCb, - .sei_mastering_display_colour_volume = - &RecordDemuxer::VideoMedia::h265MdcvSeiCb, - .sei_content_light_level = &RecordDemuxer::VideoMedia::h265CllSeiCb, -}; - - -RecordDemuxer::RecordDemuxer(Session *session, - Element::Listener *elementListener, - CodedSource::Listener *sourceListener, - IPdraw::IDemuxer *demuxer, - IPdraw::IDemuxer::Listener *demuxerListener, - const std::string &fileName) : - Demuxer(session, - elementListener, - sourceListener, - demuxer, - demuxerListener), - mFileName(fileName), mRunning(false), mFrameByFrame(false), - mDemux(nullptr), mDuration(0), mCurrentTime(0), mSpeed(1.0), - mChannelsFlushing(0) -{ - Element::setClassName(__func__); - - setState(CREATED); -} - - -RecordDemuxer::~RecordDemuxer(void) -{ - int ret; - - if (mState != STOPPED && mState != CREATED) - PDRAW_LOGW("demuxer is still running"); - - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - delete *p; - p++; - } - - if (mDemux != nullptr) { - ret = mp4_demux_close(mDemux); - if (ret < 0) - PDRAW_LOG_ERRNO("mp4_demux_close", -ret); - mDemux = nullptr; - } -} - - -int RecordDemuxer::fetchSessionMetadata(unsigned int trackId, - struct vmeta_session *meta) -{ - int ret; - unsigned int count = 0, i; - char **keys = nullptr, *key; - char **values = nullptr, *value; - - memset(meta, 0, sizeof(*meta)); - - /* File-level session metadata */ - ret = mp4_demux_get_metadata_strings(mDemux, &count, &keys, &values); - if (ret < 0) { - PDRAW_LOG_ERRNO("mp4_demux_get_metadata_strings", -ret); - return ret; - } - for (i = 0; i < count; i++) { - key = keys[i]; - value = values[i]; - if ((key) && (value)) { - ret = vmeta_session_recording_read(key, value, meta); - if (ret < 0) { - PDRAW_LOG_ERRNO("vmeta_session_recording_read", - -ret); - continue; - } - } - } - - /* Track-level session metadata */ - ret = mp4_demux_get_track_metadata_strings( - mDemux, trackId, &count, &keys, &values); - if (ret < 0) { - PDRAW_LOG_ERRNO("mp4_demux_get_track_metadata_strings", -ret); - return ret; - } - for (i = 0; i < count; i++) { - key = keys[i]; - value = values[i]; - if ((key) && (value)) { - ret = vmeta_session_recording_read(key, value, meta); - if (ret < 0) { - PDRAW_LOG_ERRNO("vmeta_session_recording_read", - -ret); - continue; - } - } - } - - return 0; -} - - -int RecordDemuxer::start(void) -{ - int ret; - unsigned int i, tkCount = 0; - struct mp4_media_info info; - struct mp4_track_info tk; - struct pdraw_demuxer_media *medias = nullptr; - std::vector selectedMedias; - std::vector::iterator m; - size_t mediasCount = 0, mediaIndex = 0; - unsigned int hrs = 0, min = 0, sec = 0; - bool ready = true; - - if ((mState == STARTED) || (mState == STARTING)) { - return 0; - } - if (mState != CREATED) { - PDRAW_LOGE("demuxer is not created"); - return -EPROTO; - } - setState(STARTING); - - /* Create the MP4 demuxer */ - ret = mp4_demux_open(mFileName.c_str(), &mDemux); - if (ret < 0) { - PDRAW_LOG_ERRNO("mp4_demux_open", -ret); - goto exit; - } - - ret = mp4_demux_get_media_info(mDemux, &info); - if (ret < 0) { - PDRAW_LOG_ERRNO("mp4_demux_get_media_info", -ret); - goto exit; - } - - mDuration = info.duration; - tkCount = info.track_count; - PDRAW_LOGD("track count: %d", tkCount); - pdraw_friendlyTimeFromUs(info.duration, &hrs, &min, &sec, nullptr); - PDRAW_LOGD("duration: %02d:%02d:%02d", hrs, min, sec); - - /* Count the number of video tracks */ - for (i = 0; i < tkCount; i++) { - ret = mp4_demux_get_track_info(mDemux, i, &tk); - if (ret != 0 || tk.type != MP4_TRACK_TYPE_VIDEO) - continue; - mediasCount++; - } - if (mediasCount == 0) { - PDRAW_LOGE("no video track"); - ret = -ENOENT; - goto exit; - } - medias = (struct pdraw_demuxer_media *)calloc(mediasCount, - sizeof(*medias)); - if (medias == nullptr) { - ret = -ENOMEM; - goto exit; - } - - /* List all tracks */ - for (i = 0; i < tkCount; i++) { - ret = mp4_demux_get_track_info(mDemux, i, &tk); - if (ret != 0 || tk.type != MP4_TRACK_TYPE_VIDEO) - continue; - struct pdraw_demuxer_media *current = &medias[mediaIndex]; - memset(current, 0, sizeof(*current)); - current->media_id = tk.id; - current->idx = i; - current->name = strdup(tk.name); - current->is_default = tk.enabled; - mediaIndex++; - if (current->is_default) - selectedMedias.push_back(current); - (void)fetchSessionMetadata(tk.id, ¤t->session_meta); - } - - /* Ask which media(s) to use from the application */ - ret = selectMedia(medias, mediasCount); - if (ret == 0 || ret == -ENOSYS) { - if (selectedMedias.empty()) { - PDRAW_LOGE( - "application requested default media, " - "but no default media found"); - ret = -ENOENT; - goto exit; - } - if (selectedMedias.size() == 1) { - PDRAW_LOGI("auto-selecting media %d (%s)", - selectedMedias.back()->media_id, - selectedMedias.back()->name); - } else { - PDRAW_LOGI("audo-selecting medias {"); - m = selectedMedias.begin(); - while (m != selectedMedias.end()) { - PDRAW_LOGI(" - %d (%s)", - (*m)->media_id, - (*m)->name); - m++; - } - PDRAW_LOGI("}"); - } - } else if (ret == -ECANCELED) { - PDRAW_LOGI("application cancelled the media selection"); - ready = false; - goto exit; - } else if (ret < 0) { - PDRAW_LOG_ERRNO("application failed to select a video media", - -ret); - ret = -EPROTO; - goto exit; - } else { - selectedMedias.clear(); - for (size_t j = 0; j < mediasCount; j++) { - if (!(ret & (1 << medias[j].media_id))) - continue; - selectedMedias.push_back(&medias[j]); - PDRAW_LOGI("application selected media %d (%s)", - selectedMedias.back()->media_id, - selectedMedias.back()->name); - } - if (selectedMedias.empty()) { - PDRAW_LOGE("the application requested no valid media"); - ret = -ENOENT; - goto exit; - } - } - - /* Create the output ports for the selected medias */ - m = selectedMedias.begin(); - while (m != selectedMedias.end()) { - ret = mp4_demux_get_track_info(mDemux, (*m)->idx, &tk); - if (ret != 0) { - PDRAW_LOG_ERRNO("mp4_demux_get_track_info", -ret); - goto exit; - } - RecordDemuxer::VideoMedia *videoMedia = - new RecordDemuxer::VideoMedia(this); - ret = videoMedia->setup(&tk); - if (ret != 0) { - PDRAW_LOG_ERRNO("VideoMedia::setup", -ret); - delete videoMedia; - goto exit; - } - mVideoMedias.push_back(videoMedia); - m++; - } - -exit: - /* Cleanup track list */ - if (medias != nullptr) { - for (size_t j = 0; j < mediasCount; j++) { - free((void *)medias[j].name); - } - } - free(medias); - - if ((ret == 0) || (ret == -ECANCELED)) { - ret = 0; - setState(STARTED); - openResponse(ret); - readyToPlay(ready); - /* TODO: notify readyToPlay = false at end of file */ - if (!ready) - onUnrecoverableError(); - } else { - setState(CREATED); - } - - return ret; -} - - -int RecordDemuxer::stop(void) -{ - int ret; - - if ((mState == STOPPED) || (mState == STOPPING)) - return 0; - if (mState != STARTED && mState != STARTING) { - PDRAW_LOGE("demuxer is not started"); - return -EPROTO; - } - setState(STOPPING); - - readyToPlay(false); - - mRunning = false; - - CodedSource::lock(); - - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - (*p)->stop(); - p++; - } - - ret = flush(); - if (ret < 0) - PDRAW_LOG_ERRNO("flush", -ret); - - CodedSource::unlock(); - - return 0; -} - - -int RecordDemuxer::flush(void) -{ - if ((mState != STARTED) && (mState != STOPPING)) { - PDRAW_LOGE("demuxer is not started"); - return -EPROTO; - } - - CodedSource::lock(); - - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - (*p)->stop(); - p++; - } - - unsigned int outputMediaCount = getOutputMediaCount(); - for (unsigned int i = 0; i < outputMediaCount; i++) { - CodedVideoMedia *media = getOutputMedia(i); - if (media == nullptr) { - PDRAW_LOGW("failed to get media at index %d", i); - continue; - } - - unsigned int outputChannelCount = getOutputChannelCount(media); - - /* Flush the output channels */ - for (unsigned int j = 0; j < outputChannelCount; j++) { - CodedChannel *channel = getOutputChannel(media, j); - if (channel == nullptr) { - PDRAW_LOGW("failed to get channel at index %d", - j); - continue; - } - int ret = channel->flush(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->flush", -ret); - mChannelsFlushing++; - } - } - - CodedSource::unlock(); - - if (mChannelsFlushing == 0) - completeFlush(); - - return 0; -} - - -void RecordDemuxer::onChannelFlushed(CodedChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - Media *media = getOutputMediaFromChannel(channel->getKey()); - if (media == nullptr) { - PDRAW_LOGE("media not found"); - return; - } - PDRAW_LOGD("channel flushed media name=%s (channel key=%p)", - media->getName().c_str(), - channel->getKey()); - - if (mState == STOPPING) { - int ret = channel->teardown(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->teardown", -ret); - } - - if (--mChannelsFlushing <= 0) { - mChannelsFlushing = 0; - completeFlush(); - } -} - - -void RecordDemuxer::completeFlush(void) -{ - if (mRunning) { - /* restart playing */ - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - (*p)->play(); - p++; - } - } - if (mState == STOPPING) - completeTeardown(); -} - - -void RecordDemuxer::onChannelUnlink(CodedChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); - if (media == nullptr) { - PDRAW_LOGE("media not found"); - return; - } - - int ret = removeOutputChannel(media, channel->getKey()); - if (ret < 0) - PDRAW_LOG_ERRNO("removeOutputChannel", -ret); - - completeTeardown(); -} - - -void RecordDemuxer::completeTeardown(void) -{ - CodedSource::lock(); - - unsigned int outputMediaCount = getOutputMediaCount(); - for (unsigned int i = 0; i < outputMediaCount; i++) { - CodedVideoMedia *media = getOutputMedia(i); - if (media && getOutputChannelCount(media) > 0) { - CodedSource::unlock(); - return; - } - } - - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - delete *p; - p++; - } - mVideoMedias.clear(); - - CodedSource::unlock(); - - if (mState == STOPPING) { - closeResponse(0); - setStateAsyncNotify(STOPPED); - } -} - - -int RecordDemuxer::play(float speed) -{ - if (mState != STARTED) { - PDRAW_LOGE("demuxer is not started"); - return -EPROTO; - } - - if (speed == 0.) { - /* speed is null => pause */ - mRunning = false; - mFrameByFrame = true; - pauseResponse(0, getCurrentTime()); - } else { - mRunning = true; - mFrameByFrame = false; - mSpeed = speed; - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - (*p)->play(); - p++; - } - playResponse(0, getCurrentTime(), mSpeed); - } - - return 0; -} - - -bool RecordDemuxer::isReadyToPlay(void) -{ - if (mState != STARTED) { - PDRAW_LOG_ERRNO("demuxer is not started", EPROTO); - return false; - } - - return mReadyToPlay; -} - - -bool RecordDemuxer::isPaused(void) -{ - if (mState != STARTED) { - PDRAW_LOG_ERRNO("demuxer is not started", EPROTO); - return false; - } - - bool running = mRunning && !mFrameByFrame; - - return !running; -} - - -int RecordDemuxer::previous(void) -{ - if (mState != STARTED) { - PDRAW_LOGE("demuxer is not started"); - return -EPROTO; - } - - if (!mFrameByFrame) { - PDRAW_LOGE("demuxer is not paused"); - return -EPROTO; - } - - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - (*p)->previous(); - p++; - } - mRunning = true; - - return 0; -} - - -int RecordDemuxer::next(void) -{ - if (mState != STARTED) { - PDRAW_LOGE("demuxer is not configured"); - return -EPROTO; - } - - if (!mFrameByFrame) { - PDRAW_LOGE("demuxer is not paused"); - return -EPROTO; - } - - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - (*p)->next(); - p++; - } - mRunning = true; - - return 0; -} - - -int RecordDemuxer::seek(int64_t delta, bool exact) -{ - if (mState != STARTED) { - PDRAW_LOGE("demuxer is not started"); - return -EPROTO; - } - - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - (*p)->seek(delta, exact); - p++; - } - - return 0; -} - - -int RecordDemuxer::seekTo(uint64_t timestamp, bool exact) -{ - if (mState != STARTED) { - PDRAW_LOGE("demuxer is not started"); - return -EPROTO; - } - - if (timestamp > mDuration) - timestamp = mDuration; - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - (*p)->seekTo(timestamp, exact); - p++; - } - mRunning = true; - - return 0; -} - - -RecordDemuxer::VideoMedia::VideoMedia(RecordDemuxer *demuxer) : - mDemuxer(demuxer), mFirstFrame(true), mTimer(nullptr), - mH264Reader(nullptr), mH265Reader(nullptr), - mVideoMedias(nullptr), mNbVideoMedias(0), mVideoTrackId(0), - mMetadataMimeType(nullptr), mMetadataBufferSize(0), - mMetadataBuffer(nullptr), mTimescale(0), mAvgOutputInterval(0), - mLastFrameOutputTime(0), mLastFrameDuration(0), - mLastOutputError(0), mPendingSeekTs(-1), - mPendingSeekExact(false), mPendingSeekToPrevSample(false), - mPendingSeekToNextSample(false), mSeekResponse(0), - mCurrentFrame(nullptr), mCurrentMem(nullptr), - mCurrentFrameCaptureTs(0), mDecodingTs(0), mDecodingTsInc(0), - mFrameIndex(0) -{ - std::string name = demuxer->getName() + "#VideoMedia"; - Loggable::setName(name); -} - - -RecordDemuxer::VideoMedia::~VideoMedia(void) -{ - int ret; - - if (mCurrentFrame != nullptr) { - ret = mbuf_coded_video_frame_unref(mCurrentFrame); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_unref", -ret); - } - - if (mCurrentMem != nullptr) { - ret = mbuf_mem_unref(mCurrentMem); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_mem_unref", -ret); - } - - if (mTimer != nullptr) { - ret = pomp_timer_clear(mTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); - ret = pomp_timer_destroy(mTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_destroy", -ret); - } - - if (mH264Reader != nullptr) { - ret = h264_reader_destroy(mH264Reader); - if (ret < 0) - PDRAW_LOG_ERRNO("h264_reader_destroy", -ret); - } - if (mH265Reader != nullptr) { - ret = h265_reader_destroy(mH265Reader); - if (ret < 0) - PDRAW_LOG_ERRNO("h265_reader_destroy", -ret); - } - - /* Remove the output ports */ - for (unsigned int i = 0; i < mNbVideoMedias; i++) { - if (mDemuxer->CodedSource::mListener) { - mDemuxer->CodedSource::mListener->onOutputMediaRemoved( - mDemuxer, mVideoMedias[i]); - } - ret = mDemuxer->removeOutputPort(mVideoMedias[i]); - if (ret < 0) { - PDRAW_LOG_ERRNO("removeOutputPort", -ret); - } else { - delete mVideoMedias[i]; - } - } - - free(mVideoMedias); - free(mMetadataBuffer); - free(mMetadataMimeType); -} - - -int RecordDemuxer::VideoMedia::setup(struct mp4_track_info *tkinfo) -{ - int ret, err; - struct mp4_video_decoder_config vdc = {}; - CodedSource::OutputPort *basePort, *mediaPort; - - std::string name = - mDemuxer->getName() + "#track#" + std::to_string(tkinfo->id); - Loggable::setName(name); - - mMetadataBufferSize = 1024; - mMetadataBuffer = (uint8_t *)malloc(mMetadataBufferSize); - if (mMetadataBuffer == nullptr) { - ret = -ENOMEM; - PDRAW_LOG_ERRNO("malloc", -ret); - goto error; - } - - /* Create the demux timer */ - mTimer = pomp_timer_new(mDemuxer->mSession->getLoop(), timerCb, this); - if (mTimer == nullptr) { - ret = -ENOMEM; - PDRAW_LOG_ERRNO("pomp_timer_new", -ret); - goto error; - } - - /* Create the H.264 and H.265 readers */ - ret = h264_reader_new(&mH264ReaderCbs, this, &mH264Reader); - if (ret < 0) { - PDRAW_LOG_ERRNO("h264_reader_new", -ret); - goto error; - } - ret = h265_reader_new(&mH265ReaderCbs, this, &mH265Reader); - if (ret < 0) { - PDRAW_LOG_ERRNO("h265_reader_new", -ret); - goto error; - } - - mDemuxer->CodedSource::lock(); - - if (mNbVideoMedias != 0) { - ret = -EEXIST; - mDemuxer->CodedSource::unlock(); - PDRAW_LOGE("video track already defined"); - goto error; - } - - mVideoTrackId = tkinfo->id; - mTimescale = tkinfo->timescale; - ret = mp4_demux_get_track_video_decoder_config( - mDemuxer->mDemux, mVideoTrackId, &vdc); - if (ret < 0) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOG_ERRNO("mp4_demux_get_track_video_decoder_config", - -ret); - goto error; - } - - switch (vdc.codec) { - case MP4_VIDEO_CODEC_AVC: - if ((vdc.avc.sps == nullptr) || (vdc.avc.sps_size == 0)) { - ret = -EPROTO; - mDemuxer->CodedSource::unlock(); - PDRAW_LOGE("invalid SPS"); - goto error; - } - if ((vdc.avc.pps == nullptr) || (vdc.avc.pps_size == 0)) { - ret = -EPROTO; - mDemuxer->CodedSource::unlock(); - PDRAW_LOGE("invalid PPS"); - goto error; - } - break; - case MP4_VIDEO_CODEC_HEVC: - if ((vdc.hevc.vps == nullptr) || (vdc.hevc.vps_size == 0)) { - ret = -EPROTO; - mDemuxer->CodedSource::unlock(); - PDRAW_LOGE("invalid VPS"); - goto error; - } - if ((vdc.hevc.sps == nullptr) || (vdc.hevc.sps_size == 0)) { - ret = -EPROTO; - mDemuxer->CodedSource::unlock(); - PDRAW_LOGE("invalid SPS"); - goto error; - } - if ((vdc.hevc.pps == nullptr) || (vdc.hevc.pps_size == 0)) { - ret = -EPROTO; - mDemuxer->CodedSource::unlock(); - PDRAW_LOGE("invalid PPS"); - goto error; - } - break; - default: - ret = -EPROTO; - mDemuxer->CodedSource::unlock(); - PDRAW_LOGE("invalid video codec"); - goto error; - } - - /* Create the output port */ - mNbVideoMedias = 2; - mVideoMedias = (CodedVideoMedia **)calloc(mNbVideoMedias, - sizeof(*mVideoMedias)); - if (mVideoMedias == nullptr) { - ret = -ENOMEM; - mDemuxer->CodedSource::unlock(); - PDRAW_LOGE("media allocation failed"); - goto error; - } - for (unsigned int i = 0; i < mNbVideoMedias; i++) { - mVideoMedias[i] = new CodedVideoMedia(mDemuxer->mSession); - if (mVideoMedias[i] == nullptr) { - ret = -ENOMEM; - mDemuxer->CodedSource::unlock(); - PDRAW_LOGE("media allocation failed"); - goto error; - } - ret = mDemuxer->addOutputPort(mVideoMedias[i]); - if (ret < 0) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOG_ERRNO("addOutputPort", -ret); - goto error; - } - std::string path = mDemuxer->Element::getName() + "$" + - mVideoMedias[i]->getName(); - mVideoMedias[i]->setPath(path); - } - - /* Set the output media info */ - switch (tkinfo->video_codec) { - case MP4_VIDEO_CODEC_AVC: - mVideoMedias[0]->format = vdef_h264_avcc; - mVideoMedias[1]->format = vdef_h264_byte_stream; - for (unsigned int i = 0; i < mNbVideoMedias; i++) { - ret = mVideoMedias[i]->setPs(nullptr, - 0, - vdc.avc.sps, - vdc.avc.sps_size, - vdc.avc.pps, - vdc.avc.pps_size); - if (ret < 0) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOG_ERRNO("media->setPs", -ret); - goto error; - } - } - /* Initialize the H.264 parsing */ - ret = h264_reader_parse_nalu( - mH264Reader, 0, vdc.avc.sps, vdc.avc.sps_size); - if (ret < 0) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOG_ERRNO("h264_reader_parse_nalu", -ret); - goto error; - } - ret = h264_reader_parse_nalu( - mH264Reader, 0, vdc.avc.pps, vdc.avc.pps_size); - if (ret < 0) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOG_ERRNO("h264_reader_parse_nalu", -ret); - goto error; - } - break; - case MP4_VIDEO_CODEC_HEVC: - mVideoMedias[0]->format = vdef_h265_hvcc; - mVideoMedias[1]->format = vdef_h265_byte_stream; - for (unsigned int i = 0; i < mNbVideoMedias; i++) { - ret = mVideoMedias[i]->setPs(vdc.hevc.vps, - vdc.hevc.vps_size, - vdc.hevc.sps, - vdc.hevc.sps_size, - vdc.hevc.pps, - vdc.hevc.pps_size); - if (ret < 0) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOG_ERRNO("media->setPs", -ret); - goto error; - } - } - /* Initialize the H.265 parsing */ - ret = h265_reader_parse_nalu( - mH265Reader, 0, vdc.hevc.vps, vdc.hevc.vps_size); - if (ret < 0) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOG_ERRNO("h265_reader_parse_nalu", -ret); - goto error; - } - ret = h265_reader_parse_nalu( - mH265Reader, 0, vdc.hevc.sps, vdc.hevc.sps_size); - if (ret < 0) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOG_ERRNO("h265_reader_parse_nalu", -ret); - goto error; - } - ret = h265_reader_parse_nalu( - mH265Reader, 0, vdc.hevc.pps, vdc.hevc.pps_size); - if (ret < 0) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOG_ERRNO("h265_reader_parse_nalu", -ret); - goto error; - } - break; - default: - mVideoMedias[0]->format = (struct vdef_coded_format){ - .encoding = VDEF_ENCODING_UNKNOWN, - .data_format = VDEF_CODED_DATA_FORMAT_UNKNOWN, - }; - mVideoMedias[1]->format = (struct vdef_coded_format){ - .encoding = VDEF_ENCODING_UNKNOWN, - .data_format = VDEF_CODED_DATA_FORMAT_UNKNOWN, - }; - break; - } - - for (unsigned int i = 0; i < mNbVideoMedias; i++) { - (void)mDemuxer->fetchSessionMetadata( - mVideoTrackId, &mVideoMedias[i]->sessionMeta); - mVideoMedias[i]->playbackType = PDRAW_PLAYBACK_TYPE_REPLAY; - mVideoMedias[i]->duration = mDemuxer->mDuration; - } - if (tkinfo->has_metadata) - mMetadataMimeType = strdup(tkinfo->metadata_mime_format); - - /* Create the output buffers pool on the last media. As the medias will - * be destroyed in creation order, this ensures that the media which - * owns the buffers pool will be the last destroyed */ - ret = mDemuxer->createOutputPortMemoryPool( - mVideoMedias[1], - DEMUXER_OUTPUT_BUFFER_COUNT, - mVideoMedias[1]->info.resolution.width * - mVideoMedias[1]->info.resolution.height * 3 / 4); - if (ret < 0) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOG_ERRNO("createOutputPortMemoryPool", -ret); - goto error; - } - /* Make the pool shared between all medias */ - basePort = mDemuxer->getOutputPort(mVideoMedias[1]); - mediaPort = mDemuxer->getOutputPort(mVideoMedias[0]); - if (basePort == nullptr || mediaPort == nullptr) { - PDRAW_LOGW("unable to share memory pool between medias"); - } else { - mediaPort->pool = basePort->pool; - mediaPort->sharedPool = true; - } - mDemuxer->CodedSource::unlock(); - - if (mDemuxer->CodedSource::mListener) { - for (unsigned int i = 0; i < mNbVideoMedias; i++) - mDemuxer->CodedSource::mListener->onOutputMediaAdded( - mDemuxer, mVideoMedias[i]); - } - - return 0; - -error: - if (mTimer != nullptr) { - err = pomp_timer_clear(mTimer); - if (err < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -err); - err = pomp_timer_destroy(mTimer); - if (err < 0) - PDRAW_LOG_ERRNO("pomp_timer_destroy", -err); - mTimer = nullptr; - } - if (mH264Reader != nullptr) { - err = h264_reader_destroy(mH264Reader); - if (err < 0) - PDRAW_LOG_ERRNO("h264_reader_destroy", -err); - mH264Reader = nullptr; - } - if (mH265Reader != nullptr) { - err = h265_reader_destroy(mH265Reader); - if (err < 0) - PDRAW_LOG_ERRNO("h265_reader_destroy", -err); - mH265Reader = nullptr; - } - free(mMetadataBuffer); - mMetadataBuffer = nullptr; - return ret; -} - - -void RecordDemuxer::VideoMedia::play(void) -{ - mPendingSeekToPrevSample = false; - mPendingSeekToNextSample = false; - pomp_timer_set(mTimer, 1); -} - - -void RecordDemuxer::VideoMedia::previous(void) -{ - if (!mPendingSeekExact) { - /* Avoid seeking back too much if a seek to a - * previous frame is already in progress */ - mPendingSeekToPrevSample = true; - mPendingSeekToNextSample = false; - mPendingSeekExact = true; - pomp_timer_set(mTimer, 1); - } -} - - -void RecordDemuxer::VideoMedia::next(void) -{ - mPendingSeekToNextSample = true; - mPendingSeekToPrevSample = false; - pomp_timer_set(mTimer, 1); -} - - -void RecordDemuxer::VideoMedia::seek(int64_t delta, bool exact) -{ - int64_t ts = (int64_t)mDemuxer->mCurrentTime + delta; - if (ts < 0) - ts = 0; - if (ts > (int64_t)mDemuxer->mDuration) - ts = mDemuxer->mDuration; - seekTo(ts, exact); -} - - -void RecordDemuxer::VideoMedia::seekTo(uint64_t timestamp, bool exact) -{ - if (timestamp > mDemuxer->mDuration) - timestamp = mDemuxer->mDuration; - mPendingSeekTs = (int64_t)timestamp; - mPendingSeekExact = exact; - mPendingSeekToPrevSample = false; - mPendingSeekToNextSample = false; - pomp_timer_set(mTimer, 1); -} - - -void RecordDemuxer::VideoMedia::stop(void) -{ - int ret; - - pomp_timer_clear(mTimer); - - if (mCurrentFrame != nullptr) { - ret = mbuf_coded_video_frame_unref(mCurrentFrame); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_unref", -ret); - mCurrentFrame = nullptr; - } - if (mCurrentMem != nullptr) { - ret = mbuf_mem_unref(mCurrentMem); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_mem_unref", -ret); - mCurrentMem = nullptr; - } -} - - -void RecordDemuxer::VideoMedia::sendDownstreamEvent( - CodedChannel::DownstreamEvent event) -{ - for (unsigned int i = 0; i < mNbVideoMedias; i++) { - int res = mDemuxer->CodedSource::sendDownstreamEvent( - mVideoMedias[i], event); - if (res < 0) - PDRAW_LOG_ERRNO("CodedSource::sendDownstreamEvent", - -res); - } -} - - -void RecordDemuxer::VideoMedia::h264UserDataSeiCb( - struct h264_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h264_sei_user_data_unregistered *sei, - void *userdata) -{ - int ret = 0; - VideoMedia *self = (VideoMedia *)userdata; - - if (self == nullptr) - return; - if ((buf == nullptr) || (len == 0)) - return; - if (sei == nullptr) - return; - if (self->mCurrentFrame == nullptr) - return; - - /* Ignore "Parrot Streaming" user data SEI */ - if (vstrm_h264_is_sei_streaming(sei->uuid)) - return; - - ret = mbuf_coded_video_frame_add_ancillary_buffer( - self->mCurrentFrame, MBUF_ANCILLARY_KEY_USERDATA_SEI, buf, len); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_ancillary_buffer", - -ret); - return; - } -} - - -void RecordDemuxer::VideoMedia::h264PicTimingSeiCb( - struct h264_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h264_sei_pic_timing *sei, - void *userdata) -{ - VideoMedia *self = (VideoMedia *)userdata; - - if (self == nullptr) - return; - if (ctx == nullptr) - return; - if (sei == nullptr) - return; - if (self->mCurrentFrame == nullptr) - return; - - self->mCurrentFrameCaptureTs = h264_ctx_sei_pic_timing_to_us(ctx, sei); -} - - -void RecordDemuxer::VideoMedia::h265UserDataSeiCb( - struct h265_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h265_sei_user_data_unregistered *sei, - void *userdata) -{ - VideoMedia *self = (VideoMedia *)userdata; - int ret = 0; - - if (self == nullptr) - return; - if ((buf == nullptr) || (len == 0)) - return; - if (sei == nullptr) - return; - if (self->mCurrentFrame == nullptr) - return; - ret = mbuf_coded_video_frame_add_ancillary_buffer( - self->mCurrentFrame, MBUF_ANCILLARY_KEY_USERDATA_SEI, buf, len); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_ancillary_buffer", - -ret); - return; - } -} - - -void RecordDemuxer::VideoMedia::h265TimeCodeSeiCb( - struct h265_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h265_sei_time_code *sei, - void *userdata) -{ - VideoMedia *self = (VideoMedia *)userdata; - - if (self == nullptr) - return; - if (ctx == nullptr) - return; - if (sei == nullptr) - return; - if (self->mCurrentFrame == nullptr) - return; - - self->mCurrentFrameCaptureTs = h265_ctx_sei_time_code_to_us(ctx, sei); -} - - -void RecordDemuxer::VideoMedia::h265MdcvSeiCb( - struct h265_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h265_sei_mastering_display_colour_volume *sei, - void *userdata) -{ - VideoMedia *self = (VideoMedia *)userdata; - - if (self == nullptr) - return; - if (ctx == nullptr) - return; - if (sei == nullptr) - return; - - for (unsigned int i = 0; i < self->mNbVideoMedias; i++) { - for (unsigned int k = 0; k < 3; k++) { - self->mVideoMedias[i] - ->info.mdcv.display_primaries_val - .color_primaries[k] - .x = - (float)sei->display_primaries_x[k] / 50000.; - self->mVideoMedias[i] - ->info.mdcv.display_primaries_val - .color_primaries[k] - .y = - (float)sei->display_primaries_y[k] / 50000.; - } - self->mVideoMedias[i] - ->info.mdcv.display_primaries_val.white_point.x = - (float)sei->white_point_x / 50000.; - self->mVideoMedias[i] - ->info.mdcv.display_primaries_val.white_point.y = - (float)sei->white_point_y / 50000.; - self->mVideoMedias[i]->info.mdcv.display_primaries = - vdef_color_primaries_from_values( - &self->mVideoMedias[i] - ->info.mdcv.display_primaries_val); - self->mVideoMedias[i] - ->info.mdcv.max_display_mastering_luminance = - (float)sei->max_display_mastering_luminance / 10000.; - self->mVideoMedias[i] - ->info.mdcv.min_display_mastering_luminance = - (float)sei->min_display_mastering_luminance / 10000.; - } -} - - -void RecordDemuxer::VideoMedia::h265CllSeiCb( - struct h265_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h265_sei_content_light_level *sei, - void *userdata) -{ - VideoMedia *self = (VideoMedia *)userdata; - - if (self == nullptr) - return; - if (ctx == nullptr) - return; - if (sei == nullptr) - return; - - for (unsigned int i = 0; i < self->mNbVideoMedias; i++) { - self->mVideoMedias[i]->info.cll.max_cll = - sei->max_content_light_level; - self->mVideoMedias[i]->info.cll.max_fall = - sei->max_pic_average_light_level; - } -} - - -void RecordDemuxer::VideoMedia::timerCb(struct pomp_timer *timer, - void *userdata) -{ - VideoMedia *self = (VideoMedia *)userdata; - bool silent = false; - float speed = 1.0; - struct mp4_track_sample sample; - CodedVideoMedia::Frame data = {}; - uint8_t *buf = nullptr, *tmp, *sei = nullptr; - size_t bufSize = 0, offset, naluSize, seiSize = 0; - struct timespec ts = {0, 0}; - uint64_t curTime = 0; - int64_t error, duration, wait = 0; - uint32_t waitMs = 0; - int ret, retry = 0, waitFlush = 0; - unsigned int outputChannelCount = 0; - CodedChannel *channel; - int didSeek = 0; - unsigned int requiredMediaIndex; - CodedVideoMedia *requiredMedia; - struct vdef_coded_frame frameInfo; - struct mbuf_coded_video_frame *outputFrame = nullptr; - - if (self == nullptr) - return; - - RecordDemuxer *demuxer = self->mDemuxer; - - if (demuxer->mState != STARTED) { - PDRAW_LOGE("demuxer is not started"); - return; - } - - speed = demuxer->mSpeed; - - if (!demuxer->mRunning) { - self->mLastFrameDuration = 0; - self->mLastOutputError = 0; - return; - } - - time_get_monotonic(&ts); - time_timespec_to_us(&ts, &curTime); - memset(&sample, 0, sizeof(sample)); - - /* Seeking */ - if (self->mPendingSeekTs >= 0) { - ret = mp4_demux_seek(demuxer->mDemux, - (uint64_t)self->mPendingSeekTs, - MP4_SEEK_METHOD_PREVIOUS_SYNC); - if (ret < 0) { - PDRAW_LOG_ERRNO("mp4_demux_seek", -ret); - } else { - self->mLastFrameDuration = 0; - self->mLastOutputError = 0; - } - self->mSeekResponse = ret; - didSeek = 1; - } else if (self->mPendingSeekToPrevSample) { - ret = mp4_demux_seek_to_track_prev_sample(demuxer->mDemux, - self->mVideoTrackId); - if (ret != 0) { - PDRAW_LOG_ERRNO("mp4_demux_seek_to_track_prev_sample", - -ret); - } else { - self->mLastFrameDuration = 0; - self->mLastOutputError = 0; - } - self->mSeekResponse = ret; - /* If not error, seek to prev sample only finishes when - * mPendingSeekExact goes back to false */ - if (ret != 0) - didSeek = 1; - } else if (self->mPendingSeekToNextSample) { - /* Cannot fail, as there is nothing to do */ - self->mSeekResponse = 0; - didSeek = 1; - } - - demuxer->CodedSource::lock(); - - /* Get an output buffer */ - if (self->mCurrentFrame != nullptr) { - ret = mbuf_coded_video_frame_unref(self->mCurrentFrame); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_unref", -ret); - self->mCurrentFrame = nullptr; - } - if (self->mCurrentMem != nullptr) { - ret = mbuf_mem_unref(self->mCurrentMem); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_mem_unref", -ret); - self->mCurrentMem = nullptr; - } - ret = demuxer->getOutputMemory(self->mVideoMedias, - self->mNbVideoMedias, - &self->mCurrentMem, - &requiredMediaIndex); - if ((ret < 0) || (self->mCurrentMem == nullptr)) { - PDRAW_LOGW("failed to get an input buffer (%d)", ret); - waitFlush = 1; - goto out; - } - requiredMedia = self->mVideoMedias[requiredMediaIndex]; - ret = mbuf_mem_get_data(self->mCurrentMem, (void **)&buf, &bufSize); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_mem_get_data", -ret); - goto out; - } - self->mCurrentFrameCaptureTs = 0; - - /* Get a sample */ - ret = mp4_demux_get_track_sample(demuxer->mDemux, - self->mVideoTrackId, - 1, - buf, - bufSize, - self->mMetadataBuffer, - self->mMetadataBufferSize, - &sample); - if (ret != 0) { - PDRAW_LOG_ERRNO("mp4_demux_get_track_sample", -ret); - /* Go to the next sample */ - ret = mp4_demux_get_track_sample(demuxer->mDemux, - self->mVideoTrackId, - 1, - nullptr, - 0, - nullptr, - 0, - &sample); - if (ret != 0) - PDRAW_LOG_ERRNO("mp4_demux_get_track_sample", -ret); - retry = 1; - goto out; - } - if (sample.size == 0) { - if (demuxer->mFrameByFrame) - demuxer->mRunning = false; - goto out; - } - silent = ((sample.silent) && (self->mPendingSeekExact)) ? true : false; - - self->mPendingSeekTs = -1; - self->mPendingSeekToPrevSample = false; - self->mPendingSeekToNextSample = false; - /* Previous frame seek end on the first non-silent frame */ - if (self->mPendingSeekExact && !silent) - didSeek = 1; - self->mPendingSeekExact = (silent) ? self->mPendingSeekExact : false; - - frameInfo.format = requiredMedia->format; - vdef_format_to_frame_info(&requiredMedia->info, &frameInfo.info); - frameInfo.info.timestamp = self->mDecodingTs; - frameInfo.info.timescale = 1000000; - frameInfo.info.index = self->mFrameIndex++; - ret = mbuf_coded_video_frame_new(&frameInfo, &self->mCurrentFrame); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_new", -ret); - goto out; - } - if (silent || (self->mFirstFrame && !sample.sync)) - frameInfo.info.flags |= VDEF_FRAME_FLAG_SILENT; - - frameInfo.type = VDEF_CODED_FRAME_TYPE_I; - switch (requiredMedia->format.encoding) { - case VDEF_ENCODING_H264: - /* Parse the H.264 SEI to find user data SEI */ - tmp = buf; - offset = 0; - while (offset < sample.size) { - enum h264_nalu_type naluType; - enum h264_slice_type sliceType = - H264_SLICE_TYPE_UNKNOWN; - memcpy(&naluSize, tmp, sizeof(uint32_t)); - naluSize = ntohl(naluSize); - naluType = (enum h264_nalu_type)(*(tmp + 4) & 0x1F); - if (naluType == H264_NALU_TYPE_SEI) { - sei = tmp + 4; - seiSize = naluSize; - } else if (naluType == H264_NALU_TYPE_SLICE_IDR) { - frameInfo.type = VDEF_CODED_FRAME_TYPE_IDR; - - } else if (naluType == H264_NALU_TYPE_SLICE) { - sliceType = /* TODO */ H264_SLICE_TYPE_UNKNOWN; - /* TODO (coverity complains that the code can - * never be reached) - * if (sliceType == H264_SLICE_TYPE_P) - * frameInfo.type = VDEF_CODED_FRAME_TYPE_P;*/ - } - /* add nalu to frame */ - struct vdef_nalu nalu = {}; - nalu.size = naluSize + 4; - nalu.h264.type = naluType; - nalu.h264.slice_type = sliceType; - /* TODO: h264.slice_mb_count */ - ret = mbuf_coded_video_frame_add_nalu( - self->mCurrentFrame, - self->mCurrentMem, - offset, - &nalu); - if (ret < 0) { - PDRAW_LOG_ERRNO( - "mbuf_coded_video_frame_add_nalu", - -ret); - goto out; - } - tmp += 4 + naluSize; - offset += 4 + naluSize; - } - if ((sei != nullptr) && (seiSize != 0)) { - ret = h264_reader_parse_nalu( - self->mH264Reader, 0, sei, seiSize); - if (ret < 0) { - PDRAW_LOG_ERRNO("h264_reader_parse_nalu", -ret); - } - } - break; - case VDEF_ENCODING_H265: - /* Parse the H.265 SEI to find user data SEI */ - tmp = buf; - offset = 0; - while (offset < sample.size) { - enum h265_nalu_type naluType; - memcpy(&naluSize, tmp, sizeof(uint32_t)); - naluSize = ntohl(naluSize); - naluType = - (enum h265_nalu_type)((*(tmp + 4) >> 1) & 0x3F); - if ((naluType == H265_NALU_TYPE_PREFIX_SEI_NUT) || - (naluType == H265_NALU_TYPE_SUFFIX_SEI_NUT)) { - sei = tmp + 4; - seiSize = naluSize; - } - /* TODO find I/P/IDR frames from nalu type ??? */ - /* add nalu to frame */ - struct vdef_nalu nalu = {}; - nalu.size = naluSize + 4; - nalu.h265.type = naluType; - ret = mbuf_coded_video_frame_add_nalu( - self->mCurrentFrame, - self->mCurrentMem, - offset, - &nalu); - if (ret < 0) { - PDRAW_LOG_ERRNO( - "mbuf_coded_video_frame_add_nalu", - -ret); - goto out; - } - tmp += 4 + naluSize; - offset += 4 + naluSize; - } - if ((sei != nullptr) && (seiSize != 0)) { - ret = h265_reader_parse_nalu( - self->mH265Reader, 0, sei, seiSize); - if (ret < 0) { - PDRAW_LOG_ERRNO("h265_reader_parse_nalu", -ret); - } - } - break; - default: - break; - } - - data.isSync = sample.sync; - data.isRef = true; /* TODO? */ - data.ntpTimestamp = self->mDecodingTs; - data.ntpUnskewedTimestamp = self->mDecodingTs; - data.ntpRawTimestamp = self->mDecodingTs; - data.ntpRawUnskewedTimestamp = self->mDecodingTs; - if (sample.next_dts > 0) { - self->mDecodingTsInc = mp4_sample_time_to_usec( - sample.next_dts - sample.dts, self->mTimescale); - if (speed != 0.) - self->mDecodingTsInc /= fabs(speed); - } - self->mDecodingTs += self->mDecodingTsInc; - /* TODO: auSyncType */ - - /* Frame metadata */ - if (sample.metadata_size > 0) { - /* Set the metadata */ - struct vmeta_frame *meta = nullptr; - struct vmeta_buffer meta_buf; - vmeta_buffer_set_cdata(&meta_buf, - self->mMetadataBuffer, - sample.metadata_size, - 0); - ret = vmeta_frame_read( - &meta_buf, self->mMetadataMimeType, &meta); - if (ret < 0) { - PDRAW_LOG_ERRNO("vmeta_frame_read", -ret); - goto out; - } - - ret = mbuf_coded_video_frame_set_metadata(self->mCurrentFrame, - meta); - vmeta_frame_unref(meta); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_set_metadata", - -ret); - goto out; - } - } - - time_get_monotonic(&ts); - time_timespec_to_us(&ts, &curTime); - data.demuxOutputTimestamp = curTime; - data.playTimestamp = - mp4_sample_time_to_usec(sample.dts, self->mTimescale); - data.captureTimestamp = self->mCurrentFrameCaptureTs; - data.localTimestamp = curTime; - data.localTimestampPrecision = - 1; /* no estimation here, the precision is 1 microsecond */ - data.recvStartTimestamp = curTime; - data.recvEndTimestamp = curTime; - demuxer->mCurrentTime = data.playTimestamp; - - frameInfo.info.capture_timestamp = self->mCurrentFrameCaptureTs; - - /* update the frame info */ - ret = mbuf_coded_video_frame_set_frame_info(self->mCurrentFrame, - &frameInfo); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_set_frame_info", -ret); - goto out; - } - - ret = mbuf_coded_video_frame_add_ancillary_buffer( - self->mCurrentFrame, - PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME, - &data, - sizeof(data)); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_ancillary_buffer", - -ret); - goto out; - } - - if (didSeek) { - /* TODO: signal once, not for all medias */ - demuxer->seekResponse(self->mSeekResponse, - demuxer->mCurrentTime, - demuxer->mSpeed); - } - - /* Convert to byte stream if required */ - if (requiredMedia->format.data_format == - VDEF_CODED_DATA_FORMAT_BYTE_STREAM) { - switch (requiredMedia->format.encoding) { - case VDEF_ENCODING_H264: - ret = h264_avcc_to_byte_stream(buf, sample.size); - if (ret < 0) - PDRAW_LOG_ERRNO("h264_avcc_to_byte_stream", - -ret); - break; - case VDEF_ENCODING_H265: - ret = h265_hvcc_to_byte_stream(buf, sample.size); - if (ret < 0) - PDRAW_LOG_ERRNO("h265_hvcc_to_byte_stream", - -ret); - break; - default: - break; - } - } - - ret = mbuf_coded_video_frame_finalize(self->mCurrentFrame); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_finalize", -ret); - goto out; - } - - /* Queue the buffer in the output channels */ - for (unsigned int i = 0; i < self->mNbVideoMedias; i++) { - outputChannelCount = - demuxer->getOutputChannelCount(self->mVideoMedias[i]); - if (outputChannelCount == 0) - continue; - if (outputFrame != nullptr) - mbuf_coded_video_frame_unref(outputFrame); - outputFrame = self->mCurrentFrame; - if (!vdef_coded_format_cmp(&requiredMedia->format, - &self->mVideoMedias[i]->format)) { - /* The format is different, we need to pick another - * frame */ - ret = demuxer->copyOutputFrame(requiredMedia, - self->mCurrentFrame, - self->mVideoMedias[i], - &outputFrame); - if (ret < 0) { - PDRAW_LOG_ERRNO("copyOutputFrame", -ret); - outputFrame = nullptr; - continue; - } - } else { - mbuf_coded_video_frame_ref(outputFrame); - } - ret = mbuf_coded_video_frame_get_frame_info(outputFrame, - &frameInfo); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_frame_info", - -ret); - goto out; - } - for (unsigned int j = 0; j < outputChannelCount; j++) { - const struct vdef_coded_format *caps; - int capsCount; - - channel = demuxer->getOutputChannel( - self->mVideoMedias[i], j); - if (channel == nullptr) { - PDRAW_LOGW("invalid channel"); - continue; - } - - capsCount = - channel->getCodedVideoMediaFormatCaps(&caps); - if (capsCount < 0) { - PDRAW_LOGW("invalid channel (no caps)"); - continue; - } - - if (!vdef_coded_format_intersect( - &frameInfo.format, caps, capsCount)) { - PDRAW_LOGW( - "incompatible coded video format " - "on channel"); - continue; - } - - ret = channel->queue(outputFrame); - if (ret < 0) { - PDRAW_LOG_ERRNO("channel->queue", -ret); - } - } - } - if (outputFrame != nullptr) - mbuf_coded_video_frame_unref(outputFrame); - if ((self->mFirstFrame) && - (!(frameInfo.info.flags & VDEF_FRAME_FLAG_SILENT))) { - self->sendDownstreamEvent(CodedChannel::DownstreamEvent::SOS); - self->mFirstFrame = false; - } - - mbuf_mem_unref(self->mCurrentMem); - self->mCurrentMem = nullptr; - mbuf_coded_video_frame_unref(self->mCurrentFrame); - self->mCurrentFrame = nullptr; - - if ((demuxer->mFrameByFrame) && (!silent)) - demuxer->mRunning = false; - -out: -#define PREV_SAMPLE_TIME_BEFORE mp4_demux_get_track_prev_sample_time_before -#define NEXT_SAMPLE_TIME_AFTER mp4_demux_get_track_next_sample_time_after - if (waitFlush) { - uint64_t nextSampleTime; - /* Flush */ - demuxer->flush(); - /* Seek to next sync sample */ - ret = mp4_demux_get_track_next_sample_time( - demuxer->mDemux, self->mVideoTrackId, &nextSampleTime); - if (ret != 0) { - PDRAW_LOG_ERRNO("mp4_demux_get_track_next_sample_time", - -ret); - demuxer->CodedSource::unlock(); - return; - } - ret = mp4_demux_seek(demuxer->mDemux, - nextSampleTime, - MP4_SEEK_METHOD_NEXT_SYNC); - if (ret != 0) - PDRAW_LOG_ERRNO("mp4_demux_seek", -ret); - /* Stop the timer */ - waitMs = 0; - } else if (retry) { - waitMs = 5; - } else if (demuxer->mRunning) { - /* Schedule the next sample */ - uint64_t nextSampleDts = mp4_sample_time_to_usec( - sample.next_dts, self->mTimescale); - - /* If error > 0 we are late, if error < 0 we are early */ - error = ((self->mLastFrameOutputTime == 0) || - (self->mLastFrameDuration == 0) || (speed == 0.) || - (speed >= PDRAW_PLAY_SPEED_MAX) || (silent)) - ? 0 - : curTime - self->mLastFrameOutputTime - - self->mLastFrameDuration + - self->mLastOutputError; - if (self->mLastFrameOutputTime) { - /* Average frame output rate - * (sliding average, alpha = 1/2) */ - self->mAvgOutputInterval += - ((int64_t)(curTime - - self->mLastFrameOutputTime) - - self->mAvgOutputInterval) >> - 1; - } - - /* Sample duration */ - if ((speed >= PDRAW_PLAY_SPEED_MAX) || (nextSampleDts == 0) || - (silent)) { - duration = 0; - } else if (speed < 0.) { - /* Negative speed => play backward */ - nextSampleDts = mp4_sample_time_to_usec( - sample.prev_sync_dts, self->mTimescale); - uint64_t pendingSeekTs = nextSampleDts; - uint64_t nextSyncSampleDts = nextSampleDts; - uint64_t wantedSampleDts; - duration = nextSampleDts - - mp4_sample_time_to_usec(sample.dts, - self->mTimescale); - if (speed != 0.) - duration = (int64_t)((float)duration / speed); - int64_t newDuration = duration; - while (newDuration - error < 0) { - wantedSampleDts = nextSyncSampleDts; - /* We can't keep up => seek to the next sync - * sample that gives a positive wait time */ - ret = PREV_SAMPLE_TIME_BEFORE( - demuxer->mDemux, - self->mVideoTrackId, - wantedSampleDts, - 1, - &nextSyncSampleDts); - if (ret < 0) { - PDRAW_LOG_ERRNO( - "mp4_demux_get_track_" - "prev_sample_time_before", - -ret); - } - if (nextSyncSampleDts > 0) { - pendingSeekTs = nextSyncSampleDts; - newDuration = nextSyncSampleDts - - mp4_sample_time_to_usec( - sample.dts, - self->mTimescale); - if (speed != 0.) { - newDuration = - (float)newDuration / - speed; - } - } else { - break; - } - } - if (pendingSeekTs > 0) { - duration = newDuration; - nextSampleDts = nextSyncSampleDts; - ret = mp4_demux_seek( - demuxer->mDemux, - pendingSeekTs, - MP4_SEEK_METHOD_PREVIOUS_SYNC); - if (ret < 0) { - PDRAW_LOG_ERRNO("mp4_demux_seek", -ret); - } - } - } else { - /* Positive speed => play forward */ - uint64_t pendingSeekTs = 0; - uint64_t nextSyncSampleDts = nextSampleDts; - uint64_t wantedSampleDts; - duration = nextSampleDts - - mp4_sample_time_to_usec(sample.dts, - self->mTimescale); - if (speed != 0.) - duration = (int64_t)((float)duration / speed); - int64_t newDuration = duration; - while (newDuration - error < 0) { - wantedSampleDts = nextSyncSampleDts; - /* We can't keep up => seek to the next sync - * sample that gives a positive wait time */ - ret = NEXT_SAMPLE_TIME_AFTER( - demuxer->mDemux, - self->mVideoTrackId, - wantedSampleDts, - 1, - &nextSyncSampleDts); - if (ret < 0) { - PDRAW_LOG_ERRNO( - "mp4_demux_get_track_" - "next_sample_time_after", - -ret); - } - if (nextSyncSampleDts > 0) { - pendingSeekTs = nextSyncSampleDts; - newDuration = nextSyncSampleDts - - mp4_sample_time_to_usec( - sample.dts, - self->mTimescale); - if (speed != 0.) { - newDuration = - (float)newDuration / - speed; - } - } else { - break; - } - } - if ((pendingSeekTs > 0) && - (newDuration - error < - 2 * self->mAvgOutputInterval)) { - /* Only seek if the resulting wait time is less - * than twice the average frame output rate */ - PDRAW_LOGD( - "unable to keep up with playback " - "timings, seek forward %.2f ms", - (float)(nextSyncSampleDts - - mp4_sample_time_to_usec( - sample.dts, - self->mTimescale)) / - 1000.); - duration = newDuration; - nextSampleDts = nextSyncSampleDts; - ret = mp4_demux_seek( - demuxer->mDemux, - pendingSeekTs, - MP4_SEEK_METHOD_PREVIOUS_SYNC); - if (ret < 0) { - PDRAW_LOG_ERRNO("mp4_demux_seek", -ret); - } - } - } - - if (nextSampleDts != 0) { - wait = duration - error; - /* TODO: loop in the timer cb when silent - * or speed>=PDRAW_PLAY_SPEED_MAX */ - if (wait < 0) { - if (duration > 0) { - PDRAW_LOGD( - "unable to keep " - "up with playback timings " - "(%.1f ms late, speed=%.2f)", - -(float)wait / 1000., - speed); - } - wait = 0; - } - waitMs = (wait + 500) / 1000; - if (waitMs == 0) - waitMs = 1; - } else if (demuxer->mRunning) { - self->sendDownstreamEvent( - CodedChannel::DownstreamEvent::EOS); - - /* Notify of the end of range */ - /* TODO: signal once, not for all medias */ - demuxer->onEndOfRange(demuxer->mCurrentTime); - } - self->mLastFrameOutputTime = curTime; - self->mLastFrameDuration = duration; - self->mLastOutputError = error; - -#if 0 - /* TODO: remove debug */ - PDRAW_LOGD("timerCb: error=%d duration=%d wait=%d%s", - (int)error, - (int)duration, - (int)wait, - (silent) ? " (silent)" : ""); -#endif - } else { - self->mLastFrameOutputTime = curTime; - self->mLastFrameDuration = 0; - self->mLastOutputError = 0; - } - - demuxer->CodedSource::unlock(); - - if (waitMs > 0) { - ret = pomp_timer_set(timer, waitMs); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_set", -ret); - } -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Recording demuxer + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_dmxrec +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_demuxer_record.hpp" +#include "pdraw_session.hpp" +#include "pdraw_utils.hpp" + +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace Pdraw { + + +const struct h264_ctx_cbs RecordDemuxer::VideoMedia::mH264ReaderCbs = { + .au_end = nullptr, + .nalu_begin = nullptr, + .nalu_end = nullptr, + .slice = nullptr, + .slice_data_begin = nullptr, + .slice_data_end = nullptr, + .slice_data_mb = nullptr, + .sps = nullptr, + .pps = nullptr, + .aud = nullptr, + .sei = nullptr, + .sei_buffering_period = nullptr, + .sei_pic_timing = &RecordDemuxer::VideoMedia::h264PicTimingSeiCb, + .sei_pan_scan_rect = nullptr, + .sei_filler_payload = nullptr, + .sei_user_data_registered = nullptr, + .sei_user_data_unregistered = + &RecordDemuxer::VideoMedia::h264UserDataSeiCb, + .sei_recovery_point = nullptr, +}; + + +const struct h265_ctx_cbs RecordDemuxer::VideoMedia::mH265ReaderCbs = { + .nalu_begin = nullptr, + .nalu_end = nullptr, + .au_end = nullptr, + .vps = nullptr, + .sps = nullptr, + .pps = nullptr, + .aud = nullptr, + .sei = nullptr, + .sei_user_data_unregistered = + &RecordDemuxer::VideoMedia::h265UserDataSeiCb, + .sei_recovery_point = nullptr, + .sei_time_code = &RecordDemuxer::VideoMedia::h265TimeCodeSeiCb, + .sei_mastering_display_colour_volume = + &RecordDemuxer::VideoMedia::h265MdcvSeiCb, + .sei_content_light_level = &RecordDemuxer::VideoMedia::h265CllSeiCb, +}; + + +RecordDemuxer::RecordDemuxer(Session *session, + Element::Listener *elementListener, + CodedSource::Listener *sourceListener, + IPdraw::IDemuxer *demuxer, + IPdraw::IDemuxer::Listener *demuxerListener, + const std::string &fileName) : + Demuxer(session, + elementListener, + sourceListener, + demuxer, + demuxerListener), + mFileName(fileName), mRunning(false), mFrameByFrame(false), + mDemux(nullptr), mDuration(0), mCurrentTime(0), mSpeed(1.0), + mChannelsFlushing(0) +{ + Element::setClassName(__func__); + + setState(CREATED); +} + + +RecordDemuxer::~RecordDemuxer(void) +{ + int ret; + + if (mState != STOPPED && mState != CREATED) + PDRAW_LOGW("demuxer is still running"); + + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + delete *p; + p++; + } + + if (mDemux != nullptr) { + ret = mp4_demux_close(mDemux); + if (ret < 0) + PDRAW_LOG_ERRNO("mp4_demux_close", -ret); + mDemux = nullptr; + } +} + + +int RecordDemuxer::fetchSessionMetadata(unsigned int trackId, + struct vmeta_session *meta) +{ + int ret; + unsigned int count = 0, i; + char **keys = nullptr, *key; + char **values = nullptr, *value; + + memset(meta, 0, sizeof(*meta)); + + /* File-level session metadata */ + ret = mp4_demux_get_metadata_strings(mDemux, &count, &keys, &values); + if (ret < 0) { + PDRAW_LOG_ERRNO("mp4_demux_get_metadata_strings", -ret); + return ret; + } + for (i = 0; i < count; i++) { + key = keys[i]; + value = values[i]; + if ((key) && (value)) { + ret = vmeta_session_recording_read(key, value, meta); + if (ret < 0) { + PDRAW_LOG_ERRNO("vmeta_session_recording_read", + -ret); + continue; + } + } + } + + /* Track-level session metadata */ + ret = mp4_demux_get_track_metadata_strings( + mDemux, trackId, &count, &keys, &values); + if (ret < 0) { + PDRAW_LOG_ERRNO("mp4_demux_get_track_metadata_strings", -ret); + return ret; + } + for (i = 0; i < count; i++) { + key = keys[i]; + value = values[i]; + if ((key) && (value)) { + ret = vmeta_session_recording_read(key, value, meta); + if (ret < 0) { + PDRAW_LOG_ERRNO("vmeta_session_recording_read", + -ret); + continue; + } + } + } + + return 0; +} + + +int RecordDemuxer::start(void) +{ + int ret; + unsigned int i, tkCount = 0; + struct mp4_media_info info; + struct mp4_track_info tk; + struct pdraw_demuxer_media *medias = nullptr; + std::vector selectedMedias; + std::vector::iterator m; + size_t mediasCount = 0, mediaIndex = 0; + unsigned int hrs = 0, min = 0, sec = 0; + bool ready = true; + + if ((mState == STARTED) || (mState == STARTING)) { + return 0; + } + if (mState != CREATED) { + PDRAW_LOGE("demuxer is not created"); + return -EPROTO; + } + setState(STARTING); + + /* Create the MP4 demuxer */ + ret = mp4_demux_open(mFileName.c_str(), &mDemux); + if (ret < 0) { + PDRAW_LOG_ERRNO("mp4_demux_open", -ret); + goto exit; + } + + ret = mp4_demux_get_media_info(mDemux, &info); + if (ret < 0) { + PDRAW_LOG_ERRNO("mp4_demux_get_media_info", -ret); + goto exit; + } + + mDuration = info.duration; + tkCount = info.track_count; + PDRAW_LOGD("track count: %d", tkCount); + pdraw_friendlyTimeFromUs(info.duration, &hrs, &min, &sec, nullptr); + PDRAW_LOGD("duration: %02d:%02d:%02d", hrs, min, sec); + + /* Count the number of video tracks */ + for (i = 0; i < tkCount; i++) { + ret = mp4_demux_get_track_info(mDemux, i, &tk); + if (ret != 0 || tk.type != MP4_TRACK_TYPE_VIDEO) + continue; + mediasCount++; + } + if (mediasCount == 0) { + PDRAW_LOGE("no video track"); + ret = -ENOENT; + goto exit; + } + medias = (struct pdraw_demuxer_media *)calloc(mediasCount, + sizeof(*medias)); + if (medias == nullptr) { + ret = -ENOMEM; + goto exit; + } + + /* List all tracks */ + for (i = 0; i < tkCount; i++) { + ret = mp4_demux_get_track_info(mDemux, i, &tk); + if (ret != 0 || tk.type != MP4_TRACK_TYPE_VIDEO) + continue; + struct pdraw_demuxer_media *current = &medias[mediaIndex]; + memset(current, 0, sizeof(*current)); + current->media_id = tk.id; + current->idx = i; + current->name = strdup(tk.name); + current->is_default = tk.enabled; + mediaIndex++; + if (current->is_default) + selectedMedias.push_back(current); + (void)fetchSessionMetadata(tk.id, ¤t->session_meta); + } + + /* Ask which media(s) to use from the application */ + ret = selectMedia(medias, mediasCount); + if (ret == 0 || ret == -ENOSYS) { + if (selectedMedias.empty()) { + PDRAW_LOGE( + "application requested default media, " + "but no default media found"); + ret = -ENOENT; + goto exit; + } + if (selectedMedias.size() == 1) { + PDRAW_LOGI("auto-selecting media %d (%s)", + selectedMedias.back()->media_id, + selectedMedias.back()->name); + } else { + PDRAW_LOGI("audo-selecting medias {"); + m = selectedMedias.begin(); + while (m != selectedMedias.end()) { + PDRAW_LOGI(" - %d (%s)", + (*m)->media_id, + (*m)->name); + m++; + } + PDRAW_LOGI("}"); + } + } else if (ret == -ECANCELED) { + PDRAW_LOGI("application cancelled the media selection"); + ready = false; + goto exit; + } else if (ret < 0) { + PDRAW_LOG_ERRNO("application failed to select a video media", + -ret); + ret = -EPROTO; + goto exit; + } else { + selectedMedias.clear(); + for (size_t j = 0; j < mediasCount; j++) { + if (!(ret & (1 << medias[j].media_id))) + continue; + selectedMedias.push_back(&medias[j]); + PDRAW_LOGI("application selected media %d (%s)", + selectedMedias.back()->media_id, + selectedMedias.back()->name); + } + if (selectedMedias.empty()) { + PDRAW_LOGE("the application requested no valid media"); + ret = -ENOENT; + goto exit; + } + } + + /* Create the output ports for the selected medias */ + m = selectedMedias.begin(); + while (m != selectedMedias.end()) { + ret = mp4_demux_get_track_info(mDemux, (*m)->idx, &tk); + if (ret != 0) { + PDRAW_LOG_ERRNO("mp4_demux_get_track_info", -ret); + goto exit; + } + RecordDemuxer::VideoMedia *videoMedia = + new RecordDemuxer::VideoMedia(this); + ret = videoMedia->setup(&tk); + if (ret != 0) { + PDRAW_LOG_ERRNO("VideoMedia::setup", -ret); + delete videoMedia; + goto exit; + } + mVideoMedias.push_back(videoMedia); + m++; + } + +exit: + /* Cleanup track list */ + if (medias != nullptr) { + for (size_t j = 0; j < mediasCount; j++) { + free((void *)medias[j].name); + } + } + free(medias); + + if ((ret == 0) || (ret == -ECANCELED)) { + ret = 0; + setState(STARTED); + openResponse(ret); + readyToPlay(ready); + /* TODO: notify readyToPlay = false at end of file */ + if (!ready) + onUnrecoverableError(); + } else { + setState(CREATED); + } + + return ret; +} + + +int RecordDemuxer::stop(void) +{ + int ret; + + if ((mState == STOPPED) || (mState == STOPPING)) + return 0; + if (mState != STARTED && mState != STARTING) { + PDRAW_LOGE("demuxer is not started"); + return -EPROTO; + } + setState(STOPPING); + + readyToPlay(false); + + mRunning = false; + + CodedSource::lock(); + + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + (*p)->stop(); + p++; + } + + ret = flush(); + if (ret < 0) + PDRAW_LOG_ERRNO("flush", -ret); + + CodedSource::unlock(); + + return 0; +} + + +int RecordDemuxer::flush(void) +{ + if ((mState != STARTED) && (mState != STOPPING)) { + PDRAW_LOGE("demuxer is not started"); + return -EPROTO; + } + + CodedSource::lock(); + + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + (*p)->stop(); + p++; + } + + unsigned int outputMediaCount = getOutputMediaCount(); + for (unsigned int i = 0; i < outputMediaCount; i++) { + CodedVideoMedia *media = getOutputMedia(i); + if (media == nullptr) { + PDRAW_LOGW("failed to get media at index %d", i); + continue; + } + + unsigned int outputChannelCount = getOutputChannelCount(media); + + /* Flush the output channels */ + for (unsigned int j = 0; j < outputChannelCount; j++) { + CodedChannel *channel = getOutputChannel(media, j); + if (channel == nullptr) { + PDRAW_LOGW("failed to get channel at index %d", + j); + continue; + } + int ret = channel->flush(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->flush", -ret); + mChannelsFlushing++; + } + } + + CodedSource::unlock(); + + if (mChannelsFlushing == 0) + completeFlush(); + + return 0; +} + + +void RecordDemuxer::onChannelFlushed(CodedChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + Media *media = getOutputMediaFromChannel(channel->getKey()); + if (media == nullptr) { + PDRAW_LOGE("media not found"); + return; + } + PDRAW_LOGD("channel flushed media name=%s (channel key=%p)", + media->getName().c_str(), + channel->getKey()); + + if (mState == STOPPING) { + int ret = channel->teardown(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->teardown", -ret); + } + + if (--mChannelsFlushing <= 0) { + mChannelsFlushing = 0; + completeFlush(); + } +} + + +void RecordDemuxer::completeFlush(void) +{ + if (mRunning) { + /* restart playing */ + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + (*p)->play(); + p++; + } + } + if (mState == STOPPING) + completeTeardown(); +} + + +void RecordDemuxer::onChannelUnlink(CodedChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); + if (media == nullptr) { + PDRAW_LOGE("media not found"); + return; + } + + int ret = removeOutputChannel(media, channel->getKey()); + if (ret < 0) + PDRAW_LOG_ERRNO("removeOutputChannel", -ret); + + completeTeardown(); +} + + +void RecordDemuxer::completeTeardown(void) +{ + CodedSource::lock(); + + unsigned int outputMediaCount = getOutputMediaCount(); + for (unsigned int i = 0; i < outputMediaCount; i++) { + CodedVideoMedia *media = getOutputMedia(i); + if (media && getOutputChannelCount(media) > 0) { + CodedSource::unlock(); + return; + } + } + + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + delete *p; + p++; + } + mVideoMedias.clear(); + + CodedSource::unlock(); + + if (mState == STOPPING) { + closeResponse(0); + setStateAsyncNotify(STOPPED); + } +} + + +int RecordDemuxer::play(float speed) +{ + if (mState != STARTED) { + PDRAW_LOGE("demuxer is not started"); + return -EPROTO; + } + + if (speed == 0.) { + /* speed is null => pause */ + mRunning = false; + mFrameByFrame = true; + pauseResponse(0, getCurrentTime()); + } else { + mRunning = true; + mFrameByFrame = false; + mSpeed = speed; + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + (*p)->play(); + p++; + } + playResponse(0, getCurrentTime(), mSpeed); + } + + return 0; +} + + +bool RecordDemuxer::isReadyToPlay(void) +{ + if (mState != STARTED) { + PDRAW_LOG_ERRNO("demuxer is not started", EPROTO); + return false; + } + + return mReadyToPlay; +} + + +bool RecordDemuxer::isPaused(void) +{ + if (mState != STARTED) { + PDRAW_LOG_ERRNO("demuxer is not started", EPROTO); + return false; + } + + bool running = mRunning && !mFrameByFrame; + + return !running; +} + + +int RecordDemuxer::previous(void) +{ + if (mState != STARTED) { + PDRAW_LOGE("demuxer is not started"); + return -EPROTO; + } + + if (!mFrameByFrame) { + PDRAW_LOGE("demuxer is not paused"); + return -EPROTO; + } + + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + (*p)->previous(); + p++; + } + mRunning = true; + + return 0; +} + + +int RecordDemuxer::next(void) +{ + if (mState != STARTED) { + PDRAW_LOGE("demuxer is not configured"); + return -EPROTO; + } + + if (!mFrameByFrame) { + PDRAW_LOGE("demuxer is not paused"); + return -EPROTO; + } + + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + (*p)->next(); + p++; + } + mRunning = true; + + return 0; +} + + +int RecordDemuxer::seek(int64_t delta, bool exact) +{ + if (mState != STARTED) { + PDRAW_LOGE("demuxer is not started"); + return -EPROTO; + } + + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + (*p)->seek(delta, exact); + p++; + } + + return 0; +} + + +int RecordDemuxer::seekTo(uint64_t timestamp, bool exact) +{ + if (mState != STARTED) { + PDRAW_LOGE("demuxer is not started"); + return -EPROTO; + } + + if (timestamp > mDuration) + timestamp = mDuration; + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + (*p)->seekTo(timestamp, exact); + p++; + } + mRunning = true; + + return 0; +} + + +RecordDemuxer::VideoMedia::VideoMedia(RecordDemuxer *demuxer) : + mDemuxer(demuxer), mFirstFrame(true), mTimer(nullptr), + mH264Reader(nullptr), mH265Reader(nullptr), + mVideoMedias(nullptr), mNbVideoMedias(0), mVideoTrackId(0), + mMetadataMimeType(nullptr), mMetadataBufferSize(0), + mMetadataBuffer(nullptr), mTimescale(0), mAvgOutputInterval(0), + mLastFrameOutputTime(0), mLastFrameDuration(0), + mLastOutputError(0), mPendingSeekTs(-1), + mPendingSeekExact(false), mPendingSeekToPrevSample(false), + mPendingSeekToNextSample(false), mSeekResponse(0), + mCurrentFrame(nullptr), mCurrentMem(nullptr), + mCurrentFrameCaptureTs(0), mDecodingTs(0), mDecodingTsInc(0), + mFrameIndex(0) +{ + std::string name = demuxer->getName() + "#VideoMedia"; + Loggable::setName(name); +} + + +RecordDemuxer::VideoMedia::~VideoMedia(void) +{ + int ret; + + if (mCurrentFrame != nullptr) { + ret = mbuf_coded_video_frame_unref(mCurrentFrame); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_unref", -ret); + } + + if (mCurrentMem != nullptr) { + ret = mbuf_mem_unref(mCurrentMem); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_mem_unref", -ret); + } + + if (mTimer != nullptr) { + ret = pomp_timer_clear(mTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); + ret = pomp_timer_destroy(mTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_destroy", -ret); + } + + if (mH264Reader != nullptr) { + ret = h264_reader_destroy(mH264Reader); + if (ret < 0) + PDRAW_LOG_ERRNO("h264_reader_destroy", -ret); + } + if (mH265Reader != nullptr) { + ret = h265_reader_destroy(mH265Reader); + if (ret < 0) + PDRAW_LOG_ERRNO("h265_reader_destroy", -ret); + } + + /* Remove the output ports */ + for (unsigned int i = 0; i < mNbVideoMedias; i++) { + if (mDemuxer->CodedSource::mListener) { + mDemuxer->CodedSource::mListener->onOutputMediaRemoved( + mDemuxer, mVideoMedias[i]); + } + ret = mDemuxer->removeOutputPort(mVideoMedias[i]); + if (ret < 0) { + PDRAW_LOG_ERRNO("removeOutputPort", -ret); + } else { + delete mVideoMedias[i]; + } + } + + free(mVideoMedias); + free(mMetadataBuffer); + free(mMetadataMimeType); +} + + +int RecordDemuxer::VideoMedia::setup(struct mp4_track_info *tkinfo) +{ + int ret, err; + struct mp4_video_decoder_config vdc = {}; + CodedSource::OutputPort *basePort, *mediaPort; + + std::string name = + mDemuxer->getName() + "#track#" + std::to_string(tkinfo->id); + Loggable::setName(name); + + mMetadataBufferSize = 1024; + mMetadataBuffer = (uint8_t *)malloc(mMetadataBufferSize); + if (mMetadataBuffer == nullptr) { + ret = -ENOMEM; + PDRAW_LOG_ERRNO("malloc", -ret); + goto error; + } + + /* Create the demux timer */ + mTimer = pomp_timer_new(mDemuxer->mSession->getLoop(), timerCb, this); + if (mTimer == nullptr) { + ret = -ENOMEM; + PDRAW_LOG_ERRNO("pomp_timer_new", -ret); + goto error; + } + + /* Create the H.264 and H.265 readers */ + ret = h264_reader_new(&mH264ReaderCbs, this, &mH264Reader); + if (ret < 0) { + PDRAW_LOG_ERRNO("h264_reader_new", -ret); + goto error; + } + ret = h265_reader_new(&mH265ReaderCbs, this, &mH265Reader); + if (ret < 0) { + PDRAW_LOG_ERRNO("h265_reader_new", -ret); + goto error; + } + + mDemuxer->CodedSource::lock(); + + if (mNbVideoMedias != 0) { + ret = -EEXIST; + mDemuxer->CodedSource::unlock(); + PDRAW_LOGE("video track already defined"); + goto error; + } + + mVideoTrackId = tkinfo->id; + mTimescale = tkinfo->timescale; + ret = mp4_demux_get_track_video_decoder_config( + mDemuxer->mDemux, mVideoTrackId, &vdc); + if (ret < 0) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOG_ERRNO("mp4_demux_get_track_video_decoder_config", + -ret); + goto error; + } + + switch (vdc.codec) { + case MP4_VIDEO_CODEC_AVC: + if ((vdc.avc.sps == nullptr) || (vdc.avc.sps_size == 0)) { + ret = -EPROTO; + mDemuxer->CodedSource::unlock(); + PDRAW_LOGE("invalid SPS"); + goto error; + } + if ((vdc.avc.pps == nullptr) || (vdc.avc.pps_size == 0)) { + ret = -EPROTO; + mDemuxer->CodedSource::unlock(); + PDRAW_LOGE("invalid PPS"); + goto error; + } + break; + case MP4_VIDEO_CODEC_HEVC: + if ((vdc.hevc.vps == nullptr) || (vdc.hevc.vps_size == 0)) { + ret = -EPROTO; + mDemuxer->CodedSource::unlock(); + PDRAW_LOGE("invalid VPS"); + goto error; + } + if ((vdc.hevc.sps == nullptr) || (vdc.hevc.sps_size == 0)) { + ret = -EPROTO; + mDemuxer->CodedSource::unlock(); + PDRAW_LOGE("invalid SPS"); + goto error; + } + if ((vdc.hevc.pps == nullptr) || (vdc.hevc.pps_size == 0)) { + ret = -EPROTO; + mDemuxer->CodedSource::unlock(); + PDRAW_LOGE("invalid PPS"); + goto error; + } + break; + default: + ret = -EPROTO; + mDemuxer->CodedSource::unlock(); + PDRAW_LOGE("invalid video codec"); + goto error; + } + + /* Create the output port */ + mNbVideoMedias = 2; + mVideoMedias = (CodedVideoMedia **)calloc(mNbVideoMedias, + sizeof(*mVideoMedias)); + if (mVideoMedias == nullptr) { + ret = -ENOMEM; + mDemuxer->CodedSource::unlock(); + PDRAW_LOGE("media allocation failed"); + goto error; + } + for (unsigned int i = 0; i < mNbVideoMedias; i++) { + mVideoMedias[i] = new CodedVideoMedia(mDemuxer->mSession); + if (mVideoMedias[i] == nullptr) { + ret = -ENOMEM; + mDemuxer->CodedSource::unlock(); + PDRAW_LOGE("media allocation failed"); + goto error; + } + ret = mDemuxer->addOutputPort(mVideoMedias[i]); + if (ret < 0) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOG_ERRNO("addOutputPort", -ret); + goto error; + } + std::string path = mDemuxer->Element::getName() + "$" + + mVideoMedias[i]->getName(); + mVideoMedias[i]->setPath(path); + } + + /* Set the output media info */ + switch (tkinfo->video_codec) { + case MP4_VIDEO_CODEC_AVC: + mVideoMedias[0]->format = vdef_h264_avcc; + mVideoMedias[1]->format = vdef_h264_byte_stream; + for (unsigned int i = 0; i < mNbVideoMedias; i++) { + ret = mVideoMedias[i]->setPs(nullptr, + 0, + vdc.avc.sps, + vdc.avc.sps_size, + vdc.avc.pps, + vdc.avc.pps_size); + if (ret < 0) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOG_ERRNO("media->setPs", -ret); + goto error; + } + } + /* Initialize the H.264 parsing */ + ret = h264_reader_parse_nalu( + mH264Reader, 0, vdc.avc.sps, vdc.avc.sps_size); + if (ret < 0) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOG_ERRNO("h264_reader_parse_nalu", -ret); + goto error; + } + ret = h264_reader_parse_nalu( + mH264Reader, 0, vdc.avc.pps, vdc.avc.pps_size); + if (ret < 0) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOG_ERRNO("h264_reader_parse_nalu", -ret); + goto error; + } + break; + case MP4_VIDEO_CODEC_HEVC: + mVideoMedias[0]->format = vdef_h265_hvcc; + mVideoMedias[1]->format = vdef_h265_byte_stream; + for (unsigned int i = 0; i < mNbVideoMedias; i++) { + ret = mVideoMedias[i]->setPs(vdc.hevc.vps, + vdc.hevc.vps_size, + vdc.hevc.sps, + vdc.hevc.sps_size, + vdc.hevc.pps, + vdc.hevc.pps_size); + if (ret < 0) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOG_ERRNO("media->setPs", -ret); + goto error; + } + } + /* Initialize the H.265 parsing */ + ret = h265_reader_parse_nalu( + mH265Reader, 0, vdc.hevc.vps, vdc.hevc.vps_size); + if (ret < 0) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOG_ERRNO("h265_reader_parse_nalu", -ret); + goto error; + } + ret = h265_reader_parse_nalu( + mH265Reader, 0, vdc.hevc.sps, vdc.hevc.sps_size); + if (ret < 0) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOG_ERRNO("h265_reader_parse_nalu", -ret); + goto error; + } + ret = h265_reader_parse_nalu( + mH265Reader, 0, vdc.hevc.pps, vdc.hevc.pps_size); + if (ret < 0) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOG_ERRNO("h265_reader_parse_nalu", -ret); + goto error; + } + break; + default: + mVideoMedias[0]->format = (struct vdef_coded_format){ + .encoding = VDEF_ENCODING_UNKNOWN, + .data_format = VDEF_CODED_DATA_FORMAT_UNKNOWN, + }; + mVideoMedias[1]->format = (struct vdef_coded_format){ + .encoding = VDEF_ENCODING_UNKNOWN, + .data_format = VDEF_CODED_DATA_FORMAT_UNKNOWN, + }; + break; + } + + for (unsigned int i = 0; i < mNbVideoMedias; i++) { + (void)mDemuxer->fetchSessionMetadata( + mVideoTrackId, &mVideoMedias[i]->sessionMeta); + mVideoMedias[i]->playbackType = PDRAW_PLAYBACK_TYPE_REPLAY; + mVideoMedias[i]->duration = mDemuxer->mDuration; + } + if (tkinfo->has_metadata) + mMetadataMimeType = strdup(tkinfo->metadata_mime_format); + + /* Create the output buffers pool on the last media. As the medias will + * be destroyed in creation order, this ensures that the media which + * owns the buffers pool will be the last destroyed */ + ret = mDemuxer->createOutputPortMemoryPool( + mVideoMedias[1], + DEMUXER_OUTPUT_BUFFER_COUNT, + mVideoMedias[1]->info.resolution.width * + mVideoMedias[1]->info.resolution.height * 3 / 4); + if (ret < 0) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOG_ERRNO("createOutputPortMemoryPool", -ret); + goto error; + } + /* Make the pool shared between all medias */ + basePort = mDemuxer->getOutputPort(mVideoMedias[1]); + mediaPort = mDemuxer->getOutputPort(mVideoMedias[0]); + if (basePort == nullptr || mediaPort == nullptr) { + PDRAW_LOGW("unable to share memory pool between medias"); + } else { + mediaPort->pool = basePort->pool; + mediaPort->sharedPool = true; + } + mDemuxer->CodedSource::unlock(); + + if (mDemuxer->CodedSource::mListener) { + for (unsigned int i = 0; i < mNbVideoMedias; i++) + mDemuxer->CodedSource::mListener->onOutputMediaAdded( + mDemuxer, mVideoMedias[i]); + } + + return 0; + +error: + if (mTimer != nullptr) { + err = pomp_timer_clear(mTimer); + if (err < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -err); + err = pomp_timer_destroy(mTimer); + if (err < 0) + PDRAW_LOG_ERRNO("pomp_timer_destroy", -err); + mTimer = nullptr; + } + if (mH264Reader != nullptr) { + err = h264_reader_destroy(mH264Reader); + if (err < 0) + PDRAW_LOG_ERRNO("h264_reader_destroy", -err); + mH264Reader = nullptr; + } + if (mH265Reader != nullptr) { + err = h265_reader_destroy(mH265Reader); + if (err < 0) + PDRAW_LOG_ERRNO("h265_reader_destroy", -err); + mH265Reader = nullptr; + } + free(mMetadataBuffer); + mMetadataBuffer = nullptr; + return ret; +} + + +void RecordDemuxer::VideoMedia::play(void) +{ + mPendingSeekToPrevSample = false; + mPendingSeekToNextSample = false; + pomp_timer_set(mTimer, 1); +} + + +void RecordDemuxer::VideoMedia::previous(void) +{ + if (!mPendingSeekExact) { + /* Avoid seeking back too much if a seek to a + * previous frame is already in progress */ + mPendingSeekToPrevSample = true; + mPendingSeekToNextSample = false; + mPendingSeekExact = true; + pomp_timer_set(mTimer, 1); + } +} + + +void RecordDemuxer::VideoMedia::next(void) +{ + mPendingSeekToNextSample = true; + mPendingSeekToPrevSample = false; + pomp_timer_set(mTimer, 1); +} + + +void RecordDemuxer::VideoMedia::seek(int64_t delta, bool exact) +{ + int64_t ts = (int64_t)mDemuxer->mCurrentTime + delta; + if (ts < 0) + ts = 0; + if (ts > (int64_t)mDemuxer->mDuration) + ts = mDemuxer->mDuration; + seekTo(ts, exact); +} + + +void RecordDemuxer::VideoMedia::seekTo(uint64_t timestamp, bool exact) +{ + if (timestamp > mDemuxer->mDuration) + timestamp = mDemuxer->mDuration; + mPendingSeekTs = (int64_t)timestamp; + mPendingSeekExact = exact; + mPendingSeekToPrevSample = false; + mPendingSeekToNextSample = false; + pomp_timer_set(mTimer, 1); +} + + +void RecordDemuxer::VideoMedia::stop(void) +{ + int ret; + + pomp_timer_clear(mTimer); + + if (mCurrentFrame != nullptr) { + ret = mbuf_coded_video_frame_unref(mCurrentFrame); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_unref", -ret); + mCurrentFrame = nullptr; + } + if (mCurrentMem != nullptr) { + ret = mbuf_mem_unref(mCurrentMem); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_mem_unref", -ret); + mCurrentMem = nullptr; + } +} + + +void RecordDemuxer::VideoMedia::sendDownstreamEvent( + CodedChannel::DownstreamEvent event) +{ + for (unsigned int i = 0; i < mNbVideoMedias; i++) { + int res = mDemuxer->CodedSource::sendDownstreamEvent( + mVideoMedias[i], event); + if (res < 0) + PDRAW_LOG_ERRNO("CodedSource::sendDownstreamEvent", + -res); + } +} + + +void RecordDemuxer::VideoMedia::h264UserDataSeiCb( + struct h264_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h264_sei_user_data_unregistered *sei, + void *userdata) +{ + int ret = 0; + VideoMedia *self = (VideoMedia *)userdata; + + if (self == nullptr) + return; + if ((buf == nullptr) || (len == 0)) + return; + if (sei == nullptr) + return; + if (self->mCurrentFrame == nullptr) + return; + + /* Ignore "Parrot Streaming" user data SEI */ + if (vstrm_h264_is_sei_streaming(sei->uuid)) + return; + + ret = mbuf_coded_video_frame_add_ancillary_buffer( + self->mCurrentFrame, MBUF_ANCILLARY_KEY_USERDATA_SEI, buf, len); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_ancillary_buffer", + -ret); + return; + } +} + + +void RecordDemuxer::VideoMedia::h264PicTimingSeiCb( + struct h264_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h264_sei_pic_timing *sei, + void *userdata) +{ + VideoMedia *self = (VideoMedia *)userdata; + + if (self == nullptr) + return; + if (ctx == nullptr) + return; + if (sei == nullptr) + return; + if (self->mCurrentFrame == nullptr) + return; + + self->mCurrentFrameCaptureTs = h264_ctx_sei_pic_timing_to_us(ctx, sei); +} + + +void RecordDemuxer::VideoMedia::h265UserDataSeiCb( + struct h265_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h265_sei_user_data_unregistered *sei, + void *userdata) +{ + VideoMedia *self = (VideoMedia *)userdata; + int ret = 0; + + if (self == nullptr) + return; + if ((buf == nullptr) || (len == 0)) + return; + if (sei == nullptr) + return; + if (self->mCurrentFrame == nullptr) + return; + ret = mbuf_coded_video_frame_add_ancillary_buffer( + self->mCurrentFrame, MBUF_ANCILLARY_KEY_USERDATA_SEI, buf, len); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_ancillary_buffer", + -ret); + return; + } +} + + +void RecordDemuxer::VideoMedia::h265TimeCodeSeiCb( + struct h265_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h265_sei_time_code *sei, + void *userdata) +{ + VideoMedia *self = (VideoMedia *)userdata; + + if (self == nullptr) + return; + if (ctx == nullptr) + return; + if (sei == nullptr) + return; + if (self->mCurrentFrame == nullptr) + return; + + self->mCurrentFrameCaptureTs = h265_ctx_sei_time_code_to_us(ctx, sei); +} + + +void RecordDemuxer::VideoMedia::h265MdcvSeiCb( + struct h265_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h265_sei_mastering_display_colour_volume *sei, + void *userdata) +{ + VideoMedia *self = (VideoMedia *)userdata; + + if (self == nullptr) + return; + if (ctx == nullptr) + return; + if (sei == nullptr) + return; + + for (unsigned int i = 0; i < self->mNbVideoMedias; i++) { + for (unsigned int k = 0; k < 3; k++) { + self->mVideoMedias[i] + ->info.mdcv.display_primaries_val + .color_primaries[k] + .x = + (float)sei->display_primaries_x[k] / 50000.; + self->mVideoMedias[i] + ->info.mdcv.display_primaries_val + .color_primaries[k] + .y = + (float)sei->display_primaries_y[k] / 50000.; + } + self->mVideoMedias[i] + ->info.mdcv.display_primaries_val.white_point.x = + (float)sei->white_point_x / 50000.; + self->mVideoMedias[i] + ->info.mdcv.display_primaries_val.white_point.y = + (float)sei->white_point_y / 50000.; + self->mVideoMedias[i]->info.mdcv.display_primaries = + vdef_color_primaries_from_values( + &self->mVideoMedias[i] + ->info.mdcv.display_primaries_val); + self->mVideoMedias[i] + ->info.mdcv.max_display_mastering_luminance = + (float)sei->max_display_mastering_luminance / 10000.; + self->mVideoMedias[i] + ->info.mdcv.min_display_mastering_luminance = + (float)sei->min_display_mastering_luminance / 10000.; + } +} + + +void RecordDemuxer::VideoMedia::h265CllSeiCb( + struct h265_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h265_sei_content_light_level *sei, + void *userdata) +{ + VideoMedia *self = (VideoMedia *)userdata; + + if (self == nullptr) + return; + if (ctx == nullptr) + return; + if (sei == nullptr) + return; + + for (unsigned int i = 0; i < self->mNbVideoMedias; i++) { + self->mVideoMedias[i]->info.cll.max_cll = + sei->max_content_light_level; + self->mVideoMedias[i]->info.cll.max_fall = + sei->max_pic_average_light_level; + } +} + + +void RecordDemuxer::VideoMedia::timerCb(struct pomp_timer *timer, + void *userdata) +{ + VideoMedia *self = (VideoMedia *)userdata; + bool silent = false; + float speed = 1.0; + struct mp4_track_sample sample; + CodedVideoMedia::Frame data = {}; + uint8_t *buf = nullptr, *tmp, *sei = nullptr; + size_t bufSize = 0, offset, naluSize, seiSize = 0; + struct timespec ts = {0, 0}; + uint64_t curTime = 0; + int64_t error, duration, wait = 0; + uint32_t waitMs = 0; + int ret, retry = 0, waitFlush = 0; + unsigned int outputChannelCount = 0; + CodedChannel *channel; + int didSeek = 0; + unsigned int requiredMediaIndex; + CodedVideoMedia *requiredMedia; + struct vdef_coded_frame frameInfo; + struct mbuf_coded_video_frame *outputFrame = nullptr; + + if (self == nullptr) + return; + + RecordDemuxer *demuxer = self->mDemuxer; + + if (demuxer->mState != STARTED) { + PDRAW_LOGE("demuxer is not started"); + return; + } + + speed = demuxer->mSpeed; + + if (!demuxer->mRunning) { + self->mLastFrameDuration = 0; + self->mLastOutputError = 0; + return; + } + + time_get_monotonic(&ts); + time_timespec_to_us(&ts, &curTime); + memset(&sample, 0, sizeof(sample)); + + /* Seeking */ + if (self->mPendingSeekTs >= 0) { + ret = mp4_demux_seek(demuxer->mDemux, + (uint64_t)self->mPendingSeekTs, + MP4_SEEK_METHOD_PREVIOUS_SYNC); + if (ret < 0) { + PDRAW_LOG_ERRNO("mp4_demux_seek", -ret); + } else { + self->mLastFrameDuration = 0; + self->mLastOutputError = 0; + } + self->mSeekResponse = ret; + didSeek = 1; + } else if (self->mPendingSeekToPrevSample) { + ret = mp4_demux_seek_to_track_prev_sample(demuxer->mDemux, + self->mVideoTrackId); + if (ret != 0) { + PDRAW_LOG_ERRNO("mp4_demux_seek_to_track_prev_sample", + -ret); + } else { + self->mLastFrameDuration = 0; + self->mLastOutputError = 0; + } + self->mSeekResponse = ret; + /* If not error, seek to prev sample only finishes when + * mPendingSeekExact goes back to false */ + if (ret != 0) + didSeek = 1; + } else if (self->mPendingSeekToNextSample) { + /* Cannot fail, as there is nothing to do */ + self->mSeekResponse = 0; + didSeek = 1; + } + + demuxer->CodedSource::lock(); + + /* Get an output buffer */ + if (self->mCurrentFrame != nullptr) { + ret = mbuf_coded_video_frame_unref(self->mCurrentFrame); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_unref", -ret); + self->mCurrentFrame = nullptr; + } + if (self->mCurrentMem != nullptr) { + ret = mbuf_mem_unref(self->mCurrentMem); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_mem_unref", -ret); + self->mCurrentMem = nullptr; + } + ret = demuxer->getOutputMemory(self->mVideoMedias, + self->mNbVideoMedias, + &self->mCurrentMem, + &requiredMediaIndex); + if ((ret < 0) || (self->mCurrentMem == nullptr)) { + PDRAW_LOGW("failed to get an input buffer (%d)", ret); + waitFlush = 1; + goto out; + } + requiredMedia = self->mVideoMedias[requiredMediaIndex]; + ret = mbuf_mem_get_data(self->mCurrentMem, (void **)&buf, &bufSize); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_mem_get_data", -ret); + goto out; + } + self->mCurrentFrameCaptureTs = 0; + + /* Get a sample */ + ret = mp4_demux_get_track_sample(demuxer->mDemux, + self->mVideoTrackId, + 1, + buf, + bufSize, + self->mMetadataBuffer, + self->mMetadataBufferSize, + &sample); + if (ret != 0) { + PDRAW_LOG_ERRNO("mp4_demux_get_track_sample", -ret); + /* Go to the next sample */ + ret = mp4_demux_get_track_sample(demuxer->mDemux, + self->mVideoTrackId, + 1, + nullptr, + 0, + nullptr, + 0, + &sample); + if (ret != 0) + PDRAW_LOG_ERRNO("mp4_demux_get_track_sample", -ret); + retry = 1; + goto out; + } + if (sample.size == 0) { + if (demuxer->mFrameByFrame) + demuxer->mRunning = false; + goto out; + } + silent = ((sample.silent) && (self->mPendingSeekExact)) ? true : false; + + self->mPendingSeekTs = -1; + self->mPendingSeekToPrevSample = false; + self->mPendingSeekToNextSample = false; + /* Previous frame seek end on the first non-silent frame */ + if (self->mPendingSeekExact && !silent) + didSeek = 1; + self->mPendingSeekExact = (silent) ? self->mPendingSeekExact : false; + + frameInfo.format = requiredMedia->format; + vdef_format_to_frame_info(&requiredMedia->info, &frameInfo.info); + frameInfo.info.timestamp = self->mDecodingTs; + frameInfo.info.timescale = 1000000; + frameInfo.info.index = self->mFrameIndex++; + ret = mbuf_coded_video_frame_new(&frameInfo, &self->mCurrentFrame); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_new", -ret); + goto out; + } + if (silent || (self->mFirstFrame && !sample.sync)) + frameInfo.info.flags |= VDEF_FRAME_FLAG_SILENT; + + frameInfo.type = VDEF_CODED_FRAME_TYPE_I; + switch (requiredMedia->format.encoding) { + case VDEF_ENCODING_H264: + /* Parse the H.264 SEI to find user data SEI */ + tmp = buf; + offset = 0; + while (offset < sample.size) { + enum h264_nalu_type naluType; + enum h264_slice_type sliceType = + H264_SLICE_TYPE_UNKNOWN; + memcpy(&naluSize, tmp, sizeof(uint32_t)); + naluSize = ntohl(naluSize); + naluType = (enum h264_nalu_type)(*(tmp + 4) & 0x1F); + if (naluType == H264_NALU_TYPE_SEI) { + sei = tmp + 4; + seiSize = naluSize; + } else if (naluType == H264_NALU_TYPE_SLICE_IDR) { + frameInfo.type = VDEF_CODED_FRAME_TYPE_IDR; + + } else if (naluType == H264_NALU_TYPE_SLICE) { + sliceType = /* TODO */ H264_SLICE_TYPE_UNKNOWN; + /* TODO (coverity complains that the code can + * never be reached) + * if (sliceType == H264_SLICE_TYPE_P) + * frameInfo.type = VDEF_CODED_FRAME_TYPE_P;*/ + } + /* add nalu to frame */ + struct vdef_nalu nalu = {}; + nalu.size = naluSize + 4; + nalu.h264.type = naluType; + nalu.h264.slice_type = sliceType; + /* TODO: h264.slice_mb_count */ + ret = mbuf_coded_video_frame_add_nalu( + self->mCurrentFrame, + self->mCurrentMem, + offset, + &nalu); + if (ret < 0) { + PDRAW_LOG_ERRNO( + "mbuf_coded_video_frame_add_nalu", + -ret); + goto out; + } + tmp += 4 + naluSize; + offset += 4 + naluSize; + } + if ((sei != nullptr) && (seiSize != 0)) { + ret = h264_reader_parse_nalu( + self->mH264Reader, 0, sei, seiSize); + if (ret < 0) { + PDRAW_LOG_ERRNO("h264_reader_parse_nalu", -ret); + } + } + break; + case VDEF_ENCODING_H265: + /* Parse the H.265 SEI to find user data SEI */ + tmp = buf; + offset = 0; + while (offset < sample.size) { + enum h265_nalu_type naluType; + memcpy(&naluSize, tmp, sizeof(uint32_t)); + naluSize = ntohl(naluSize); + naluType = + (enum h265_nalu_type)((*(tmp + 4) >> 1) & 0x3F); + if ((naluType == H265_NALU_TYPE_PREFIX_SEI_NUT) || + (naluType == H265_NALU_TYPE_SUFFIX_SEI_NUT)) { + sei = tmp + 4; + seiSize = naluSize; + } + /* TODO find I/P/IDR frames from nalu type ??? */ + /* add nalu to frame */ + struct vdef_nalu nalu = {}; + nalu.size = naluSize + 4; + nalu.h265.type = naluType; + ret = mbuf_coded_video_frame_add_nalu( + self->mCurrentFrame, + self->mCurrentMem, + offset, + &nalu); + if (ret < 0) { + PDRAW_LOG_ERRNO( + "mbuf_coded_video_frame_add_nalu", + -ret); + goto out; + } + tmp += 4 + naluSize; + offset += 4 + naluSize; + } + if ((sei != nullptr) && (seiSize != 0)) { + ret = h265_reader_parse_nalu( + self->mH265Reader, 0, sei, seiSize); + if (ret < 0) { + PDRAW_LOG_ERRNO("h265_reader_parse_nalu", -ret); + } + } + break; + default: + break; + } + + data.isSync = sample.sync; + data.isRef = true; /* TODO? */ + data.ntpTimestamp = self->mDecodingTs; + data.ntpUnskewedTimestamp = self->mDecodingTs; + data.ntpRawTimestamp = self->mDecodingTs; + data.ntpRawUnskewedTimestamp = self->mDecodingTs; + if (sample.next_dts > 0) { + self->mDecodingTsInc = mp4_sample_time_to_usec( + sample.next_dts - sample.dts, self->mTimescale); + if (speed != 0.) + self->mDecodingTsInc /= fabs(speed); + } + self->mDecodingTs += self->mDecodingTsInc; + /* TODO: auSyncType */ + + /* Frame metadata */ + if (sample.metadata_size > 0) { + /* Set the metadata */ + struct vmeta_frame *meta = nullptr; + struct vmeta_buffer meta_buf; + vmeta_buffer_set_cdata(&meta_buf, + self->mMetadataBuffer, + sample.metadata_size, + 0); + ret = vmeta_frame_read( + &meta_buf, self->mMetadataMimeType, &meta); + if (ret < 0) { + PDRAW_LOG_ERRNO("vmeta_frame_read", -ret); + goto out; + } + + ret = mbuf_coded_video_frame_set_metadata(self->mCurrentFrame, + meta); + vmeta_frame_unref(meta); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_set_metadata", + -ret); + goto out; + } + } + + time_get_monotonic(&ts); + time_timespec_to_us(&ts, &curTime); + data.demuxOutputTimestamp = curTime; + data.playTimestamp = + mp4_sample_time_to_usec(sample.dts, self->mTimescale); + data.captureTimestamp = self->mCurrentFrameCaptureTs; + data.localTimestamp = curTime; + data.localTimestampPrecision = + 1; /* no estimation here, the precision is 1 microsecond */ + data.recvStartTimestamp = curTime; + data.recvEndTimestamp = curTime; + demuxer->mCurrentTime = data.playTimestamp; + + frameInfo.info.capture_timestamp = self->mCurrentFrameCaptureTs; + + /* update the frame info */ + ret = mbuf_coded_video_frame_set_frame_info(self->mCurrentFrame, + &frameInfo); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_set_frame_info", -ret); + goto out; + } + + ret = mbuf_coded_video_frame_add_ancillary_buffer( + self->mCurrentFrame, + PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME, + &data, + sizeof(data)); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_ancillary_buffer", + -ret); + goto out; + } + + if (didSeek) { + /* TODO: signal once, not for all medias */ + demuxer->seekResponse(self->mSeekResponse, + demuxer->mCurrentTime, + demuxer->mSpeed); + } + + /* Convert to byte stream if required */ + if (requiredMedia->format.data_format == + VDEF_CODED_DATA_FORMAT_BYTE_STREAM) { + switch (requiredMedia->format.encoding) { + case VDEF_ENCODING_H264: + ret = h264_avcc_to_byte_stream(buf, sample.size); + if (ret < 0) + PDRAW_LOG_ERRNO("h264_avcc_to_byte_stream", + -ret); + break; + case VDEF_ENCODING_H265: + ret = h265_hvcc_to_byte_stream(buf, sample.size); + if (ret < 0) + PDRAW_LOG_ERRNO("h265_hvcc_to_byte_stream", + -ret); + break; + default: + break; + } + } + + ret = mbuf_coded_video_frame_finalize(self->mCurrentFrame); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_finalize", -ret); + goto out; + } + + /* Queue the buffer in the output channels */ + for (unsigned int i = 0; i < self->mNbVideoMedias; i++) { + outputChannelCount = + demuxer->getOutputChannelCount(self->mVideoMedias[i]); + if (outputChannelCount == 0) + continue; + if (outputFrame != nullptr) + mbuf_coded_video_frame_unref(outputFrame); + outputFrame = self->mCurrentFrame; + if (!vdef_coded_format_cmp(&requiredMedia->format, + &self->mVideoMedias[i]->format)) { + /* The format is different, we need to pick another + * frame */ + ret = demuxer->copyOutputFrame(requiredMedia, + self->mCurrentFrame, + self->mVideoMedias[i], + &outputFrame); + if (ret < 0) { + PDRAW_LOG_ERRNO("copyOutputFrame", -ret); + outputFrame = nullptr; + continue; + } + } else { + mbuf_coded_video_frame_ref(outputFrame); + } + ret = mbuf_coded_video_frame_get_frame_info(outputFrame, + &frameInfo); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_frame_info", + -ret); + goto out; + } + for (unsigned int j = 0; j < outputChannelCount; j++) { + const struct vdef_coded_format *caps; + int capsCount; + + channel = demuxer->getOutputChannel( + self->mVideoMedias[i], j); + if (channel == nullptr) { + PDRAW_LOGW("invalid channel"); + continue; + } + + capsCount = + channel->getCodedVideoMediaFormatCaps(&caps); + if (capsCount < 0) { + PDRAW_LOGW("invalid channel (no caps)"); + continue; + } + + if (!vdef_coded_format_intersect( + &frameInfo.format, caps, capsCount)) { + PDRAW_LOGW( + "incompatible coded video format " + "on channel"); + continue; + } + + ret = channel->queue(outputFrame); + if (ret < 0) { + PDRAW_LOG_ERRNO("channel->queue", -ret); + } + } + } + if (outputFrame != nullptr) + mbuf_coded_video_frame_unref(outputFrame); + if ((self->mFirstFrame) && + (!(frameInfo.info.flags & VDEF_FRAME_FLAG_SILENT))) { + self->sendDownstreamEvent(CodedChannel::DownstreamEvent::SOS); + self->mFirstFrame = false; + } + + mbuf_mem_unref(self->mCurrentMem); + self->mCurrentMem = nullptr; + mbuf_coded_video_frame_unref(self->mCurrentFrame); + self->mCurrentFrame = nullptr; + + if ((demuxer->mFrameByFrame) && (!silent)) + demuxer->mRunning = false; + +out: +#define PREV_SAMPLE_TIME_BEFORE mp4_demux_get_track_prev_sample_time_before +#define NEXT_SAMPLE_TIME_AFTER mp4_demux_get_track_next_sample_time_after + if (waitFlush) { + uint64_t nextSampleTime; + /* Flush */ + demuxer->flush(); + /* Seek to next sync sample */ + ret = mp4_demux_get_track_next_sample_time( + demuxer->mDemux, self->mVideoTrackId, &nextSampleTime); + if (ret != 0) { + PDRAW_LOG_ERRNO("mp4_demux_get_track_next_sample_time", + -ret); + demuxer->CodedSource::unlock(); + return; + } + ret = mp4_demux_seek(demuxer->mDemux, + nextSampleTime, + MP4_SEEK_METHOD_NEXT_SYNC); + if (ret != 0) + PDRAW_LOG_ERRNO("mp4_demux_seek", -ret); + /* Stop the timer */ + waitMs = 0; + } else if (retry) { + waitMs = 5; + } else if (demuxer->mRunning) { + /* Schedule the next sample */ + uint64_t nextSampleDts = mp4_sample_time_to_usec( + sample.next_dts, self->mTimescale); + + /* If error > 0 we are late, if error < 0 we are early */ + error = ((self->mLastFrameOutputTime == 0) || + (self->mLastFrameDuration == 0) || (speed == 0.) || + (speed >= PDRAW_PLAY_SPEED_MAX) || (silent)) + ? 0 + : curTime - self->mLastFrameOutputTime - + self->mLastFrameDuration + + self->mLastOutputError; + if (self->mLastFrameOutputTime) { + /* Average frame output rate + * (sliding average, alpha = 1/2) */ + self->mAvgOutputInterval += + ((int64_t)(curTime - + self->mLastFrameOutputTime) - + self->mAvgOutputInterval) >> + 1; + } + + /* Sample duration */ + if ((speed >= PDRAW_PLAY_SPEED_MAX) || (nextSampleDts == 0) || + (silent)) { + duration = 0; + } else if (speed < 0.) { + /* Negative speed => play backward */ + nextSampleDts = mp4_sample_time_to_usec( + sample.prev_sync_dts, self->mTimescale); + uint64_t pendingSeekTs = nextSampleDts; + uint64_t nextSyncSampleDts = nextSampleDts; + uint64_t wantedSampleDts; + duration = nextSampleDts - + mp4_sample_time_to_usec(sample.dts, + self->mTimescale); + if (speed != 0.) + duration = (int64_t)((float)duration / speed); + int64_t newDuration = duration; + while (newDuration - error < 0) { + wantedSampleDts = nextSyncSampleDts; + /* We can't keep up => seek to the next sync + * sample that gives a positive wait time */ + ret = PREV_SAMPLE_TIME_BEFORE( + demuxer->mDemux, + self->mVideoTrackId, + wantedSampleDts, + 1, + &nextSyncSampleDts); + if (ret < 0) { + PDRAW_LOG_ERRNO( + "mp4_demux_get_track_" + "prev_sample_time_before", + -ret); + } + if (nextSyncSampleDts > 0) { + pendingSeekTs = nextSyncSampleDts; + newDuration = nextSyncSampleDts - + mp4_sample_time_to_usec( + sample.dts, + self->mTimescale); + if (speed != 0.) { + newDuration = + (float)newDuration / + speed; + } + } else { + break; + } + } + if (pendingSeekTs > 0) { + duration = newDuration; + nextSampleDts = nextSyncSampleDts; + ret = mp4_demux_seek( + demuxer->mDemux, + pendingSeekTs, + MP4_SEEK_METHOD_PREVIOUS_SYNC); + if (ret < 0) { + PDRAW_LOG_ERRNO("mp4_demux_seek", -ret); + } + } + } else { + /* Positive speed => play forward */ + uint64_t pendingSeekTs = 0; + uint64_t nextSyncSampleDts = nextSampleDts; + uint64_t wantedSampleDts; + duration = nextSampleDts - + mp4_sample_time_to_usec(sample.dts, + self->mTimescale); + if (speed != 0.) + duration = (int64_t)((float)duration / speed); + int64_t newDuration = duration; + while (newDuration - error < 0) { + wantedSampleDts = nextSyncSampleDts; + /* We can't keep up => seek to the next sync + * sample that gives a positive wait time */ + ret = NEXT_SAMPLE_TIME_AFTER( + demuxer->mDemux, + self->mVideoTrackId, + wantedSampleDts, + 1, + &nextSyncSampleDts); + if (ret < 0) { + PDRAW_LOG_ERRNO( + "mp4_demux_get_track_" + "next_sample_time_after", + -ret); + } + if (nextSyncSampleDts > 0) { + pendingSeekTs = nextSyncSampleDts; + newDuration = nextSyncSampleDts - + mp4_sample_time_to_usec( + sample.dts, + self->mTimescale); + if (speed != 0.) { + newDuration = + (float)newDuration / + speed; + } + } else { + break; + } + } + if ((pendingSeekTs > 0) && + (newDuration - error < + 2 * self->mAvgOutputInterval)) { + /* Only seek if the resulting wait time is less + * than twice the average frame output rate */ + PDRAW_LOGD( + "unable to keep up with playback " + "timings, seek forward %.2f ms", + (float)(nextSyncSampleDts - + mp4_sample_time_to_usec( + sample.dts, + self->mTimescale)) / + 1000.); + duration = newDuration; + nextSampleDts = nextSyncSampleDts; + ret = mp4_demux_seek( + demuxer->mDemux, + pendingSeekTs, + MP4_SEEK_METHOD_PREVIOUS_SYNC); + if (ret < 0) { + PDRAW_LOG_ERRNO("mp4_demux_seek", -ret); + } + } + } + + if (nextSampleDts != 0) { + wait = duration - error; + /* TODO: loop in the timer cb when silent + * or speed>=PDRAW_PLAY_SPEED_MAX */ + if (wait < 0) { + if (duration > 0) { + PDRAW_LOGD( + "unable to keep " + "up with playback timings " + "(%.1f ms late, speed=%.2f)", + -(float)wait / 1000., + speed); + } + wait = 0; + } + waitMs = (wait + 500) / 1000; + if (waitMs == 0) + waitMs = 1; + } else if (demuxer->mRunning) { + self->sendDownstreamEvent( + CodedChannel::DownstreamEvent::EOS); + + /* Notify of the end of range */ + /* TODO: signal once, not for all medias */ + demuxer->onEndOfRange(demuxer->mCurrentTime); + } + self->mLastFrameOutputTime = curTime; + self->mLastFrameDuration = duration; + self->mLastOutputError = error; + +#if 0 + /* TODO: remove debug */ + PDRAW_LOGD("timerCb: error=%d duration=%d wait=%d%s", + (int)error, + (int)duration, + (int)wait, + (silent) ? " (silent)" : ""); +#endif + } else { + self->mLastFrameOutputTime = curTime; + self->mLastFrameDuration = 0; + self->mLastOutputError = 0; + } + + demuxer->CodedSource::unlock(); + + if (waitMs > 0) { + ret = pomp_timer_set(timer, waitMs); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_set", -ret); + } +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_demuxer_record.hpp b/libpdraw/src/pdraw_demuxer_record.hpp index 64bed92..c70d759 100644 --- a/libpdraw/src/pdraw_demuxer_record.hpp +++ b/libpdraw/src/pdraw_demuxer_record.hpp @@ -1,211 +1,211 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Recording demuxer - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_DEMUXER_RECORD_HPP_ -#define _PDRAW_DEMUXER_RECORD_HPP_ - -#include "pdraw_demuxer.hpp" - -#include -#include - -#include -#include -#include -#include - -namespace Pdraw { - -class RecordDemuxer : public Demuxer { -public: - RecordDemuxer(Session *session, - Element::Listener *elementListener, - CodedSource::Listener *sourceListener, - IPdraw::IDemuxer *demuxer, - IPdraw::IDemuxer::Listener *demuxerListener, - const std::string &fileName); - - ~RecordDemuxer(void); - - int start(void); - - int stop(void); - - int play(float speed = 1.0f); - - bool isReadyToPlay(void); - - bool isPaused(void); - - int previous(void); - - int next(void); - - int seek(int64_t delta, bool exact = false); - - int seekTo(uint64_t timestamp, bool exact = false); - - uint64_t getDuration(void) - { - return mDuration; - } - - uint64_t getCurrentTime(void) - { - return mCurrentTime; - } - -private: - class VideoMedia : public Loggable { - public: - VideoMedia(RecordDemuxer *demuxer); - - ~VideoMedia(void); - - int setup(struct mp4_track_info *tkinfo); - - void play(void); - - void previous(void); - - void next(void); - - void seek(int64_t delta, bool exact); - - void seekTo(uint64_t timestamp, bool exact); - - void stop(void); - - private: - void sendDownstreamEvent(CodedChannel::DownstreamEvent event); - - static void h264UserDataSeiCb( - struct h264_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h264_sei_user_data_unregistered *sei, - void *userdata); - - static void - h264PicTimingSeiCb(struct h264_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h264_sei_pic_timing *sei, - void *userdata); - - static void h265UserDataSeiCb( - struct h265_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h265_sei_user_data_unregistered *sei, - void *userdata); - - static void - h265TimeCodeSeiCb(struct h265_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h265_sei_time_code *sei, - void *userdata); - - static void h265MdcvSeiCb( - struct h265_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h265_sei_mastering_display_colour_volume - *sei, - void *userdata); - - static void - h265CllSeiCb(struct h265_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h265_sei_content_light_level *sei, - void *userdata); - - static void timerCb(struct pomp_timer *timer, void *userdata); - - RecordDemuxer *mDemuxer; - bool mFirstFrame; - struct pomp_timer *mTimer; - struct h264_reader *mH264Reader; - struct h265_reader *mH265Reader; - CodedVideoMedia **mVideoMedias; - unsigned int mNbVideoMedias; - unsigned int mVideoTrackId; - char *mMetadataMimeType; - size_t mMetadataBufferSize; - uint8_t *mMetadataBuffer; - uint32_t mTimescale; - int64_t mAvgOutputInterval; - uint64_t mLastFrameOutputTime; - int64_t mLastFrameDuration; - int64_t mLastOutputError; - int64_t mPendingSeekTs; - bool mPendingSeekExact; - bool mPendingSeekToPrevSample; - bool mPendingSeekToNextSample; - int mSeekResponse; - struct mbuf_coded_video_frame *mCurrentFrame; - struct mbuf_mem *mCurrentMem; - uint64_t mCurrentFrameCaptureTs; - uint64_t mDecodingTs; - uint64_t mDecodingTsInc; - unsigned int mFrameIndex; - static const struct h264_ctx_cbs mH264ReaderCbs; - static const struct h265_ctx_cbs mH265ReaderCbs; - }; - - int fetchSessionMetadata(unsigned int trackId, - struct vmeta_session *meta); - - int flush(void); - - void completeFlush(void); - - void completeTeardown(void); - - void onChannelFlushed(CodedChannel *channel); - - void onChannelUnlink(CodedChannel *channel); - - std::string mFileName; - bool mRunning; - bool mFrameByFrame; - struct mp4_demux *mDemux; - std::vector mVideoMedias; - uint64_t mDuration; - uint64_t mCurrentTime; - float mSpeed; - int mChannelsFlushing; -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_DEMUXER_RECORD_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Recording demuxer + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_DEMUXER_RECORD_HPP_ +#define _PDRAW_DEMUXER_RECORD_HPP_ + +#include "pdraw_demuxer.hpp" + +#include +#include + +#include +#include +#include +#include + +namespace Pdraw { + +class RecordDemuxer : public Demuxer { +public: + RecordDemuxer(Session *session, + Element::Listener *elementListener, + CodedSource::Listener *sourceListener, + IPdraw::IDemuxer *demuxer, + IPdraw::IDemuxer::Listener *demuxerListener, + const std::string &fileName); + + ~RecordDemuxer(void); + + int start(void); + + int stop(void); + + int play(float speed = 1.0f); + + bool isReadyToPlay(void); + + bool isPaused(void); + + int previous(void); + + int next(void); + + int seek(int64_t delta, bool exact = false); + + int seekTo(uint64_t timestamp, bool exact = false); + + uint64_t getDuration(void) + { + return mDuration; + } + + uint64_t getCurrentTime(void) + { + return mCurrentTime; + } + +private: + class VideoMedia : public Loggable { + public: + VideoMedia(RecordDemuxer *demuxer); + + ~VideoMedia(void); + + int setup(struct mp4_track_info *tkinfo); + + void play(void); + + void previous(void); + + void next(void); + + void seek(int64_t delta, bool exact); + + void seekTo(uint64_t timestamp, bool exact); + + void stop(void); + + private: + void sendDownstreamEvent(CodedChannel::DownstreamEvent event); + + static void h264UserDataSeiCb( + struct h264_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h264_sei_user_data_unregistered *sei, + void *userdata); + + static void + h264PicTimingSeiCb(struct h264_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h264_sei_pic_timing *sei, + void *userdata); + + static void h265UserDataSeiCb( + struct h265_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h265_sei_user_data_unregistered *sei, + void *userdata); + + static void + h265TimeCodeSeiCb(struct h265_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h265_sei_time_code *sei, + void *userdata); + + static void h265MdcvSeiCb( + struct h265_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h265_sei_mastering_display_colour_volume + *sei, + void *userdata); + + static void + h265CllSeiCb(struct h265_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h265_sei_content_light_level *sei, + void *userdata); + + static void timerCb(struct pomp_timer *timer, void *userdata); + + RecordDemuxer *mDemuxer; + bool mFirstFrame; + struct pomp_timer *mTimer; + struct h264_reader *mH264Reader; + struct h265_reader *mH265Reader; + CodedVideoMedia **mVideoMedias; + unsigned int mNbVideoMedias; + unsigned int mVideoTrackId; + char *mMetadataMimeType; + size_t mMetadataBufferSize; + uint8_t *mMetadataBuffer; + uint32_t mTimescale; + int64_t mAvgOutputInterval; + uint64_t mLastFrameOutputTime; + int64_t mLastFrameDuration; + int64_t mLastOutputError; + int64_t mPendingSeekTs; + bool mPendingSeekExact; + bool mPendingSeekToPrevSample; + bool mPendingSeekToNextSample; + int mSeekResponse; + struct mbuf_coded_video_frame *mCurrentFrame; + struct mbuf_mem *mCurrentMem; + uint64_t mCurrentFrameCaptureTs; + uint64_t mDecodingTs; + uint64_t mDecodingTsInc; + unsigned int mFrameIndex; + static const struct h264_ctx_cbs mH264ReaderCbs; + static const struct h265_ctx_cbs mH265ReaderCbs; + }; + + int fetchSessionMetadata(unsigned int trackId, + struct vmeta_session *meta); + + int flush(void); + + void completeFlush(void); + + void completeTeardown(void); + + void onChannelFlushed(CodedChannel *channel); + + void onChannelUnlink(CodedChannel *channel); + + std::string mFileName; + bool mRunning; + bool mFrameByFrame; + struct mp4_demux *mDemux; + std::vector mVideoMedias; + uint64_t mDuration; + uint64_t mCurrentTime; + float mSpeed; + int mChannelsFlushing; +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_DEMUXER_RECORD_HPP_ */ diff --git a/libpdraw/src/pdraw_demuxer_stream.cpp b/libpdraw/src/pdraw_demuxer_stream.cpp index 90bdd0c..de33b89 100644 --- a/libpdraw/src/pdraw_demuxer_stream.cpp +++ b/libpdraw/src/pdraw_demuxer_stream.cpp @@ -1,3431 +1,3431 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Streaming demuxer - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_dmxstrm -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_demuxer_stream.hpp" -#include "pdraw_session.hpp" -#include "pdraw_utils.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -namespace Pdraw { - - -#define DEMUXER_STREAM_TIMER_INTERVAL_MS 50 -#define DEMUXER_STREAM_FRAME_TIMEOUT_US 2000000 - -#define DEMUXER_STREAM_GOODBYE_REASON_USER "user disconnection" -#define DEMUXER_STREAM_GOODBYE_REASON_RECONFIGURE "configuration change" -#define DEMUXER_STREAM_GOODBYE_REASON_PHOTO_TRIGGER "photo trigger" - - -const struct rtsp_client_cbs StreamDemuxer::mRtspClientCbs = { - .socket_cb = &StreamDemuxer::onRtspSocketCreated, - .connection_state = &StreamDemuxer::onRtspConnectionState, - .session_removed = &StreamDemuxer::onRtspSessionRemoved, - .options_resp = &StreamDemuxer::onRtspOptionsResp, - .describe_resp = &StreamDemuxer::onRtspDescribeResp, - .setup_resp = &StreamDemuxer::onRtspSetupResp, - .play_resp = &StreamDemuxer::onRtspPlayResp, - .pause_resp = &StreamDemuxer::onRtspPauseResp, - .teardown_resp = &StreamDemuxer::onRtspTeardownResp, - .announce = &StreamDemuxer::onRtspAnnounce, -}; - - -const struct vstrm_receiver_cbs StreamDemuxer::VideoMedia::mReceiverCbs = { - .send_ctrl = &StreamDemuxer::VideoMedia::sendCtrlCb, - .codec_info_changed = &StreamDemuxer::VideoMedia::codecInfoChangedCb, - .recv_frame = &StreamDemuxer::VideoMedia::recvFrameCb, - .recv_rtp_pkt = nullptr, - .session_metadata_peer_changed = - &StreamDemuxer::VideoMedia::sessionMetadataPeerChangedCb, - .event = &StreamDemuxer::VideoMedia::eventCb, - .goodbye = &StreamDemuxer::VideoMedia::goodbyeCb, -}; - - -const struct h264_ctx_cbs StreamDemuxer::VideoMedia::mH264Cbs = { - .au_end = nullptr, - .nalu_begin = nullptr, - .nalu_end = nullptr, - .slice = nullptr, - .slice_data_begin = nullptr, - .slice_data_end = nullptr, - .slice_data_mb = nullptr, - .sps = nullptr, - .pps = nullptr, - .aud = nullptr, - .sei = nullptr, - .sei_buffering_period = nullptr, - .sei_pic_timing = &StreamDemuxer::VideoMedia::h264PicTimingSeiCb, - .sei_pan_scan_rect = nullptr, - .sei_filler_payload = nullptr, - .sei_user_data_registered = nullptr, - .sei_user_data_unregistered = - &StreamDemuxer::VideoMedia::h264UserDataSeiCb, - .sei_recovery_point = - &StreamDemuxer::VideoMedia::h264RecoveryPointSeiCb, -}; - - -StreamDemuxer::StreamDemuxer(Session *session, - Element::Listener *elementListener, - CodedSource::Listener *sourceListener, - IPdraw::IDemuxer *demuxer, - IPdraw::IDemuxer::Listener *demuxerListener) : - Demuxer(session, - elementListener, - sourceListener, - demuxer, - demuxerListener), - mContentBase(nullptr), mSessionProtocol(NONE), - mSetupRequestsCount(0), mSessionMetaFromSdp({}), - mChannelsReadyForStop(false), mNetworkReadyForStop(false), - mRtspState(DISCONNECTED), mRtspClient(nullptr), - mRtspSessionId(nullptr), mRunning(false), mFlushing(false), - mDestroyMediasAfterFlush(false), mFlushChannelCount(0), - mStartTime(0), mDuration(0), mTrackDuration(0), - mUpdateTrackDuration(false), mCurrentTime(0), mPausePoint(0), - mNtpToNptOffset(0), mRtpClockRate(0), mSpeed(1.f), - mFrameByFrame(true), mEndOfRangeNotified(false), mSeeking(false) -{ - Element::setClassName(__func__); -} - - -StreamDemuxer::~StreamDemuxer(void) -{ - int ret; - - if (mState != STOPPED && mState != CREATED) - PDRAW_LOGW("demuxer is still running"); - - destroyAllVideoMedias(); - - if (mRtspClient != nullptr) { - ret = rtsp_client_destroy(mRtspClient); - if (ret < 0) - PDRAW_LOG_ERRNO("rtsp_client_destroy", -ret); - } - - ret = pomp_loop_idle_remove_by_cookie(mSession->getLoop(), this); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_loop_idle_remove_by_cookie", -ret); - - free((void *)mContentBase); - free((void *)mRtspSessionId); -} - - -void StreamDemuxer::sessionMetadataFromSdp(const struct sdp_session *session, - struct vmeta_session *meta) -{ - int err; - struct sdp_attr *attr = nullptr; - - memset(meta, 0, sizeof(*meta)); - - if (session->session_name != nullptr) { - err = vmeta_session_streaming_sdp_read( - VMETA_STRM_SDP_TYPE_SESSION_NAME, - session->session_name, - nullptr, - meta); - if (err < 0) - ULOG_ERRNO("vmeta_session_streaming_sdp_read", -err); - } - if (session->session_info != nullptr) { - err = vmeta_session_streaming_sdp_read( - VMETA_STRM_SDP_TYPE_SESSION_INFO, - session->session_info, - nullptr, - meta); - if (err < 0) - ULOG_ERRNO("vmeta_session_streaming_sdp_read", -err); - } - if (session->tool != nullptr) { - err = vmeta_session_streaming_sdp_read( - VMETA_STRM_SDP_TYPE_SESSION_TOOL, - session->tool, - nullptr, - meta); - if (err < 0) - ULOG_ERRNO("vmeta_session_streaming_sdp_read", -err); - } - list_walk_entry_forward(&session->attrs, attr, node) - { - err = vmeta_session_streaming_sdp_read( - VMETA_STRM_SDP_TYPE_SESSION_ATTR, - attr->value, - attr->key, - meta); - if (err < 0) - ULOG_ERRNO("vmeta_session_streaming_sdp_read", -err); - } -} - - -int StreamDemuxer::processSetupRequests(void) -{ - int res; - - if (mSetupRequests.empty()) { - if (mSetupRequestsCount > 0) { - /* Returning -EBUSY here to notify that there are still - * setup requests in progress (for example asynchronous - * requests from the StreamDemuxerMux subclass that are - * not yet in the queue) */ - return -EBUSY; - } else { - /* All setup requests have benn processed; nothing - * more to do */ - return 0; - } - } - - /* Process the next setup request */ - SetupRequest req = mSetupRequests.front(); - - res = rtsp_client_setup(mRtspClient, - mContentBase, - req.controlUrl, - mRtspSessionId, - RTSP_DELIVERY_UNICAST, - req.lowerTransport, - req.localStreamPort, - req.localControlPort, - req.headerExt, - req.headerExtCount, - req.media, - RTSP_CLIENT_DEFAULT_RESP_TIMEOUT_MS); - if (res == -EBUSY) { - /* Another setup request is already in progress, the current - * setup request will be processed later (i.e. chained in the - * setup response callback function) */ - return 0; - } else if (res < 0) { - PDRAW_LOG_ERRNO("rtsp_client_setup", -res); - } else { - /* Success: returning -EBUSY here to notify that there are - * still setup requests in progress */ - res = -EBUSY; - } - - mSetupRequests.pop(); - mSetupRequestsCount--; - free(req.controlUrl); - return res; -} - - -void StreamDemuxer::destroyAllVideoMedias(void) -{ - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - delete *p; - p++; - } - mVideoMedias.clear(); -} - - -void StreamDemuxer::onRtspSocketCreated(int fd, void *userdata) -{ - StreamDemuxer *self = reinterpret_cast(userdata); - - self->mSession->socketCreated(fd); -} - - -void StreamDemuxer::onRtspConnectionState(struct rtsp_client *client, - enum rtsp_client_conn_state state, - void *userdata) -{ - StreamDemuxer *self = reinterpret_cast(userdata); - int res = 0; - - PDRAW_LOGI("RTSP client %s", rtsp_client_conn_state_str(state)); - - switch (state) { - case RTSP_CLIENT_CONN_STATE_DISCONNECTED: - self->mRtspState = DISCONNECTED; - PDRAW_LOGD("RTSP state change to %s", - getRtspStateStr(self->mRtspState)); - /* TODO? pomp_timer_clear(self->mFrameTimer); */ - self->mRunning = false; - self->mNetworkReadyForStop = true; - - /* If channels are also ready, set the state to stopped */ - if (self->mChannelsReadyForStop) { - self->mChannelsReadyForStop = false; - self->mNetworkReadyForStop = false; - self->closeResponse(0); - self->setStateAsyncNotify(STOPPED); - } - break; - case RTSP_CLIENT_CONN_STATE_CONNECTED: - /* If previous RTSP state is not DISCONNECTED, do not - * send OPTIONS request */ - if (self->mRtspState != DISCONNECTED) - break; - self->mRtspState = CONNECTED; - PDRAW_LOGD("RTSP state change to %s", - getRtspStateStr(self->mRtspState)); - - res = rtsp_client_options(self->mRtspClient, - nullptr, - 0, - nullptr, - RTSP_CLIENT_DEFAULT_RESP_TIMEOUT_MS); - if (res < 0) { - PDRAW_LOG_ERRNO("rtsp_client_options", -res); - } - break; - case RTSP_CLIENT_CONN_STATE_CONNECTING: - case RTSP_CLIENT_CONN_STATE_DISCONNECTING: - break; - default: - PDRAW_LOGW("unhandled RTSP connection state: (%d: %s)", - state, - rtsp_client_conn_state_str(state)); - break; - } -} - - -void StreamDemuxer::onRtspSessionRemoved(struct rtsp_client *client, - const char *session_id, - int status, - void *userdata) -{ - StreamDemuxer *self = reinterpret_cast(userdata); - - if (xstrcmp(session_id, self->mRtspSessionId) != 0) { - PDRAW_LOGD("wrong session removed (%s, expected %s)", - session_id, - self->mRtspSessionId); - return; - } else { - free((void *)self->mRtspSessionId); - self->mRtspSessionId = nullptr; - } - - ULOG_EVT("STREAM", - "event='client_session_removed';element='%s';" - "status=%d;status_str='%s';session='%s';res='%s'", - self->getCName(), - status, - strerror(-status), - session_id ? session_id : "", - self->mRtspPath.c_str()); - - auto p = self->mVideoMedias.begin(); - while (p != self->mVideoMedias.end()) { - (*p)->sendDownstreamEvent(CodedChannel::DownstreamEvent::EOS); - p++; - } - - - if (self->mState == STOPPING) { - /* If we are stopping, continue with rtsp disconnection */ - self->mRtspState = OPTIONS_DONE; - PDRAW_LOGD("RTSP state change to %s", - getRtspStateStr(self->mRtspState)); - - int res = pomp_loop_idle_add_with_cookie( - self->mSession->getLoop(), - &idleRtspDisconnect, - self, - self); - if (res < 0) - PDRAW_LOG_ERRNO("pomp_loop_idle_add_with_cookie", -res); - } else { - /* We used to call stop() here; this is no longer the case - * because now a demuxer must not stop itself; we call - * unrecoverableError instead, and it is the application's - * responsibility to stop and destroy the demuxer and create a - * new one. */ - self->onUnrecoverableError(); - } -} - - -void StreamDemuxer::onRtspOptionsResp(struct rtsp_client *client, - enum rtsp_client_req_status req_status, - int status, - uint32_t methods, - const struct rtsp_header_ext *ext, - size_t ext_count, - void *userdata, - void *req_userdata) -{ - StreamDemuxer *self = reinterpret_cast(userdata); - int res = 0; - - if (req_status != RTSP_CLIENT_REQ_STATUS_OK) { - switch (req_status) { - case RTSP_CLIENT_REQ_STATUS_FAILED: - PDRAW_LOGE("RTSP options request failed (%d: %s)", - status, - strerror(-status)); - res = status; - break; - case RTSP_CLIENT_REQ_STATUS_ABORTED: - PDRAW_LOGE("RTSP options request aborted"); - res = -EPROTO; - break; - case RTSP_CLIENT_REQ_STATUS_CANCELED: - PDRAW_LOGE("RTSP options request canceled"); - res = -ECANCELED; - break; - case RTSP_CLIENT_REQ_STATUS_TIMEOUT: - PDRAW_LOGE("timeout on RTSP options request"); - res = -ETIMEDOUT; - break; - default: - /* This should not happen */ - PDRAW_LOGE("unexpected status on options request: %d", - req_status); - res = -EPROTO; - break; - } - - ULOG_EVT("STREAM", - "event='client_options_resp';element='%s';" - "status=%d;status_str='%s';res='%s'", - self->getCName(), - res, - strerror(-res), - self->mRtspPath.c_str()); - - self->onUnrecoverableError(res); - return; - } - - ULOG_EVT("STREAM", - "event='client_options_resp';element='%s';" - "status=%d;status_str='%s';res='%s'", - self->getCName(), - res, - strerror(-res), - self->mRtspPath.c_str()); - - self->mRtspState = OPTIONS_DONE; - PDRAW_LOGD("RTSP state change to %s", - getRtspStateStr(self->mRtspState)); - - res = rtsp_client_describe(self->mRtspClient, - self->mRtspPath.c_str(), - nullptr, - 0, - nullptr, - RTSP_CLIENT_DEFAULT_RESP_TIMEOUT_MS); - if (res < 0) - PDRAW_LOG_ERRNO("rtsp_client_describe", -res); -} - - -void StreamDemuxer::onRtspDescribeResp(struct rtsp_client *client, - enum rtsp_client_req_status req_status, - int status, - const char *content_base, - const struct rtsp_header_ext *ext, - size_t ext_count, - const char *sdp, - void *userdata, - void *req_userdata) -{ - StreamDemuxer *self = reinterpret_cast(userdata); - int res = 0; - - if (req_status != RTSP_CLIENT_REQ_STATUS_OK) { - switch (req_status) { - case RTSP_CLIENT_REQ_STATUS_FAILED: - PDRAW_LOGE("RTSP describe request failed (%d: %s)", - status, - strerror(-status)); - res = status; - break; - case RTSP_CLIENT_REQ_STATUS_ABORTED: - PDRAW_LOGE("RTSP describe request aborted"); - res = -EPROTO; - break; - case RTSP_CLIENT_REQ_STATUS_CANCELED: - PDRAW_LOGE("RTSP describe request canceled"); - res = -ECANCELED; - break; - case RTSP_CLIENT_REQ_STATUS_TIMEOUT: - PDRAW_LOGE("timeout on RTSP describe request"); - res = -ETIMEDOUT; - break; - default: - /* This should not happen */ - PDRAW_LOGE("unexpected status on describe request: %d", - req_status); - res = -EPROTO; - break; - } - - ULOG_EVT("STREAM", - "event='client_describe_resp';element='%s';" - "status=%d;status_str='%s';res='%s'", - self->getCName(), - res, - strerror(-res), - self->mRtspPath.c_str()); - - self->onUnrecoverableError(res); - return; - } - - ULOG_EVT("STREAM", - "event='client_describe_resp';element='%s';" - "status=%d;status_str='%s';res='%s'", - self->getCName(), - res, - strerror(-res), - self->mRtspPath.c_str()); - - self->onNewSdp(content_base, sdp); -} - - -void StreamDemuxer::onRtspSetupResp(struct rtsp_client *client, - const char *session_id, - enum rtsp_client_req_status req_status, - int status, - uint16_t server_stream_port, - uint16_t server_control_port, - int ssrc_valid, - uint32_t ssrc, - const struct rtsp_header_ext *ext, - size_t ext_count, - void *userdata, - void *req_userdata) -{ - StreamDemuxer *self = reinterpret_cast(userdata); - VideoMedia *media = (VideoMedia *)req_userdata; - int res = 0; - const char *proxy_session = nullptr; - - for (size_t i = 0; i < ext_count; i++) { - if (strcasecmp(ext[i].key, - RTSP_HEADER_EXT_PARROT_PROXY_SESSION) == 0) { - proxy_session = ext[i].value; - break; - } - } - - if (req_status != RTSP_CLIENT_REQ_STATUS_OK) { - switch (req_status) { - case RTSP_CLIENT_REQ_STATUS_FAILED: - PDRAW_LOGE("RTSP setup request failed (%d: %s)", - status, - strerror(-status)); - res = status; - break; - case RTSP_CLIENT_REQ_STATUS_ABORTED: - PDRAW_LOGE("RTSP setup request aborted"); - res = -EPROTO; - break; - case RTSP_CLIENT_REQ_STATUS_CANCELED: - PDRAW_LOGE("RTSP setup request canceled"); - res = -ECANCELED; - break; - case RTSP_CLIENT_REQ_STATUS_TIMEOUT: - PDRAW_LOGE("timeout on RTSP setup request"); - res = -ETIMEDOUT; - break; - default: - /* This should not happen */ - PDRAW_LOGE("unexpected status on setup request: %d", - req_status); - res = -EPROTO; - break; - } - - ULOG_EVT("STREAM", - "event='client_setup_resp';element='%s';" - "status=%d;status_str='%s';session='%s'%s%s%s;" - "res='%s';media='%s';src='%s:%" PRIu16 ",%" PRIu16 - "';dst='%s:%" PRIu16 ",%" PRIu16 "'", - self->getCName(), - res, - strerror(-res), - session_id ? session_id : "", - proxy_session ? ";proxy_session='" : "", - proxy_session ? proxy_session : "", - proxy_session ? "'" : "", - self->mRtspPath.c_str(), - media ? media->getControlUrl() : "", - self->mRemoteAddr.c_str(), - (uint16_t)0, - (uint16_t)0, - self->mLocalAddr.c_str(), - (uint16_t)0, - (uint16_t)0); - - self->onUnrecoverableError(res); - return; - } - - free((void *)self->mRtspSessionId); - self->mRtspSessionId = xstrdup(session_id); - - if (media != nullptr) { - media->setSsrc((ssrc_valid) ? ssrc : 0); - media->setRemoteStreamPort(server_stream_port); - media->setRemoteControlPort(server_control_port); - - res = media->startRtpAvp(); - if (res < 0) - PDRAW_LOG_ERRNO("startRtpAvp", -res); - } - - ULOG_EVT("STREAM", - "event='client_setup_resp';element='%s';" - "status=%d;status_str='%s';session='%s'%s%s%s;" - "res='%s';media='%s';src='%s:%" PRIu16 ",%" PRIu16 - "';dst='%s:%" PRIu16 ",%" PRIu16 "'", - self->getCName(), - res, - strerror(-res), - session_id ? session_id : "", - proxy_session ? ";proxy_session='" : "", - proxy_session ? proxy_session : "", - proxy_session ? "'" : "", - self->mRtspPath.c_str(), - media ? media->getControlUrl() : "", - self->mRemoteAddr.c_str(), - server_stream_port, - server_control_port, - self->mLocalAddr.c_str(), - media ? media->getLocalStreamPort() : (uint16_t)0, - media ? media->getLocalControlPort() : (uint16_t)0); - - if (res < 0) { - self->onUnrecoverableError(res); - return; - } - - res = self->processSetupRequests(); - if (res < 0) { - if (res != -EBUSY) - self->onUnrecoverableError(res); - return; - } - - self->mRtspState = SETUP_DONE; - PDRAW_LOGD("RTSP state change to %s", - getRtspStateStr(self->mRtspState)); - - /* If the setup is a success, we always need to be in STARTED state */ - self->setState(STARTED); - self->openResponse(0); - self->readyToPlay(true); -} - - -void StreamDemuxer::onRtspPlayResp(struct rtsp_client *client, - const char *session_id, - enum rtsp_client_req_status req_status, - int status, - const struct rtsp_range *range, - float scale, - int seq_valid, - uint16_t seq, - int rtptime_valid, - uint32_t rtptime, - const struct rtsp_header_ext *ext, - size_t ext_count, - void *userdata, - void *req_userdata) -{ - StreamDemuxer *self = reinterpret_cast(userdata); - uint64_t start = 0, stop = 0, ntptime = 0; - int res = 0; - const char *proxy_session = nullptr; - - for (size_t i = 0; i < ext_count; i++) { - if (strcasecmp(ext[i].key, - RTSP_HEADER_EXT_PARROT_PROXY_SESSION) == 0) { - proxy_session = ext[i].value; - break; - } - } - if (range != nullptr) { - if (range->start.format == RTSP_TIME_FORMAT_NPT) - rtsp_time_npt_to_us(&range->start.npt, &start); - if (range->stop.format == RTSP_TIME_FORMAT_NPT) { - rtsp_time_npt_to_us(&range->stop.npt, &stop); - if (self->mUpdateTrackDuration) { - /* RTSP server may update track duration, if - * it was requested to play the file to the end. - */ - self->mUpdateTrackDuration = false; - self->mTrackDuration = stop; - } - } - } - - if (req_status != RTSP_CLIENT_REQ_STATUS_OK) { - switch (req_status) { - case RTSP_CLIENT_REQ_STATUS_FAILED: - PDRAW_LOGE("RTSP play request failed (%d: %s)", - status, - strerror(-status)); - res = status; - break; - case RTSP_CLIENT_REQ_STATUS_ABORTED: - PDRAW_LOGE("RTSP play request aborted"); - res = -EPROTO; - break; - case RTSP_CLIENT_REQ_STATUS_CANCELED: - PDRAW_LOGE("RTSP play request canceled"); - res = -ECANCELED; - break; - case RTSP_CLIENT_REQ_STATUS_TIMEOUT: - /* TODO: strategy on RTSP play timeout to be - * defined. Retry RTSP play? */ - PDRAW_LOGE("timeout on RTSP play request"); - res = -ETIMEDOUT; - break; - default: - /* This should not happen */ - PDRAW_LOGE("unexpected status on play request: %d", - req_status); - res = -EPROTO; - break; - } - - ULOG_EVT("STREAM", - "event='client_play_resp';element='%s';" - "status=%d;status_str='%s';session='%s'%s%s%s;" - "res='%s';start_ts=%" PRIu64 ";stop_ts=%" PRIu64 - ";rtp_ts=%" PRIu32 ";seq=%" PRIu16, - self->getCName(), - res, - strerror(-res), - session_id ? session_id : "", - proxy_session ? ";proxy_session='" : "", - proxy_session ? proxy_session : "", - proxy_session ? "'" : "", - self->mRtspPath.c_str(), - start, - stop, - rtptime, - seq); - - if (self->mSeeking) - self->seekResponse( - status, self->mCurrentTime, self->mSpeed); - else - self->playResponse( - status, self->mCurrentTime, self->mSpeed); - self->mSeeking = false; - return; - } - - if (xstrcmp(session_id, self->mRtspSessionId) != 0) { - PDRAW_LOGE( - "RTSP play response for a wrong session" - " (%s instead of %s)", - session_id, - self->mRtspSessionId); - return; - } - - ULOG_EVT("STREAM", - "event='client_play_resp';element='%s';" - "status=%d;status_str='%s';session='%s'%s%s%s;" - "res='%s';start_ts=%" PRIu64 ";stop_ts=%" PRIu64 - ";rtp_ts=%" PRIu32 ";seq=%" PRIu16, - self->getCName(), - res, - strerror(-res), - session_id ? session_id : "", - proxy_session ? ";proxy_session='" : "", - proxy_session ? proxy_session : "", - proxy_session ? "'" : "", - self->mRtspPath.c_str(), - start, - stop, - rtptime, - seq); - - self->mSpeed = (scale != 0.) ? scale : 1.0; - if ((rtptime_valid) && (self->mRtpClockRate != 0)) { - ntptime = rtp_timestamp_to_us(rtptime, self->mRtpClockRate); - } - self->mNtpToNptOffset = - (int64_t)(ntptime * self->mSpeed) - (int64_t)start; - self->mPausePoint = stop; - if (self->mSeeking) - self->seekResponse(0, start, self->mSpeed); - else - self->playResponse(0, start, self->mSpeed); - self->mSeeking = false; - - if ((start == stop) && start && stop) { - /* The end of range is reached */ - int res = pomp_loop_idle_add_with_cookie( - self->mSession->getLoop(), - &idleEndOfRangeNotification, - self, - self); - if (res < 0) - PDRAW_LOG_ERRNO("pomp_loop_idle_add_with_cookie", -res); - self->mSeeking = false; - } -} - - -void StreamDemuxer::onRtspPauseResp(struct rtsp_client *client, - const char *session_id, - enum rtsp_client_req_status req_status, - int status, - const struct rtsp_range *range, - const struct rtsp_header_ext *ext, - size_t ext_count, - void *userdata, - void *req_userdata) -{ - StreamDemuxer *self = reinterpret_cast(userdata); - uint64_t start = 0; - int res = 0; - const char *proxy_session = nullptr; - - for (size_t i = 0; i < ext_count; i++) { - if (strcasecmp(ext[i].key, - RTSP_HEADER_EXT_PARROT_PROXY_SESSION) == 0) { - proxy_session = ext[i].value; - break; - } - } - if ((range != nullptr) && (range->start.format == RTSP_TIME_FORMAT_NPT)) - rtsp_time_npt_to_us(&range->start.npt, &start); - - if (req_status != RTSP_CLIENT_REQ_STATUS_OK) { - switch (req_status) { - case RTSP_CLIENT_REQ_STATUS_FAILED: - PDRAW_LOGE("RTSP pause request failed (%d: %s)", - status, - strerror(-status)); - res = status; - break; - case RTSP_CLIENT_REQ_STATUS_ABORTED: - PDRAW_LOGE("RTSP pause request aborted"); - res = -EPROTO; - break; - case RTSP_CLIENT_REQ_STATUS_CANCELED: - PDRAW_LOGE("RTSP pause request canceled"); - res = -ECANCELED; - break; - case RTSP_CLIENT_REQ_STATUS_TIMEOUT: - /* TODO: strategy on RTSP pause timeout to be - * defined. Retry RTSP pause? */ - PDRAW_LOGE("timeout on RTSP pause request"); - res = -ETIMEDOUT; - break; - default: - /* This should not happen */ - PDRAW_LOGE("unexpected status on pause request: %d", - req_status); - res = -EPROTO; - break; - } - - ULOG_EVT("STREAM", - "event='client_pause_resp';element='%s';" - "status=%d;status_str='%s';session='%s'%s%s%s;" - "res='%s';ts=%" PRIu64, - self->getCName(), - res, - strerror(-res), - session_id ? session_id : "", - proxy_session ? ";proxy_session='" : "", - proxy_session ? proxy_session : "", - proxy_session ? "'" : "", - self->mRtspPath.c_str(), - start); - - self->pauseResponse(status, self->mCurrentTime); - return; - } - - if (xstrcmp(session_id, self->mRtspSessionId) != 0) { - PDRAW_LOGE( - "RTSP pause response for a wrong session" - " (%s instead of %s)", - session_id, - self->mRtspSessionId); - return; - } - - ULOG_EVT("STREAM", - "event='client_pause_resp';element='%s';" - "status=%d;status_str='%s';session='%s'%s%s%s;" - "res='%s';ts=%" PRIu64, - self->getCName(), - res, - strerror(-res), - session_id ? session_id : "", - proxy_session ? ";proxy_session='" : "", - proxy_session ? proxy_session : "", - proxy_session ? "'" : "", - self->mRtspPath.c_str(), - start); - - self->mPausePoint = start; - self->pauseResponse(0, self->mPausePoint); -} - - -void StreamDemuxer::onRtspTeardownResp(struct rtsp_client *client, - const char *session_id, - enum rtsp_client_req_status req_status, - int status, - const struct rtsp_header_ext *ext, - size_t ext_count, - void *userdata, - void *req_userdata) -{ - StreamDemuxer *self = reinterpret_cast(userdata); - int res = 0; - const char *proxy_session = nullptr; - - for (size_t i = 0; i < ext_count; i++) { - if (strcasecmp(ext[i].key, - RTSP_HEADER_EXT_PARROT_PROXY_SESSION) == 0) { - proxy_session = ext[i].value; - break; - } - } - - - switch (req_status) { - case RTSP_CLIENT_REQ_STATUS_OK: - break; - case RTSP_CLIENT_REQ_STATUS_ABORTED: - /* Teardown effective by disconnection */ - PDRAW_LOGW("RTSP teardown request aborted"); - res = -EPROTO; - break; - case RTSP_CLIENT_REQ_STATUS_FAILED: - /* Force disconnection anyway */ - PDRAW_LOGE("RTSP teardown request failed (%d: %s)", - status, - strerror(-status)); - res = status; - break; - case RTSP_CLIENT_REQ_STATUS_CANCELED: - /* No disconnection */ - PDRAW_LOGI("RTSP teardown request canceled"); - res = -ECANCELED; - break; - case RTSP_CLIENT_REQ_STATUS_TIMEOUT: - /* Force disconnection anyway */ - PDRAW_LOGE("timeout on RTSP teardown request"); - res = -ETIMEDOUT; - break; - default: - /* This should not happen */ - PDRAW_LOGE("unexpected status on teardown request: %d", - req_status); - res = -EPROTO; - break; - } - - if (xstrcmp(session_id, self->mRtspSessionId) != 0) { - PDRAW_LOGE( - "RTSP teardown response for a wrong session" - " (%s instead of %s)", - session_id, - self->mRtspSessionId); - return; - } - - ULOG_EVT("STREAM", - "event='client_teardown_resp';element='%s';" - "status=%d;status_str='%s';session='%s'%s%s%s;" - "res='%s'", - self->getCName(), - res, - strerror(-res), - session_id ? session_id : "", - proxy_session ? ";proxy_session='" : "", - proxy_session ? proxy_session : "", - proxy_session ? "'" : "", - self->mRtspPath.c_str()); -} - - -void StreamDemuxer::onRtspAnnounce(struct rtsp_client *client, - const char *content_base, - const struct rtsp_header_ext *ext, - size_t ext_count, - const char *sdp, - void *userdata) -{ - StreamDemuxer *self = reinterpret_cast(userdata); - - if (!self->mContentBase || - strcmp(self->mContentBase, content_base) != 0) - return; - - const char *resource = nullptr; - if (strlen(content_base) > 7) { - const char *p = strchr(content_base + 7, '/'); - if (p) - resource = p + 1; - } - ULOG_EVT("STREAM", - "event='client_announce';element='%s';res='%s'", - self->getCName(), - resource ? resource : ""); - - self->onNewSdp(content_base, sdp); -} - - -void StreamDemuxer::idleRtspDisconnect(void *userdata) -{ - StreamDemuxer *self = reinterpret_cast(userdata); - int res; - - res = rtsp_client_disconnect(self->mRtspClient); - if (res < 0) { - PDRAW_LOG_ERRNO("rtsp_client_disconnect", -res); - return; - } -} - - -int StreamDemuxer::startRtsp(const std::string &url) -{ - int res; - std::string::size_type n; - - if (mRtspClient != nullptr) { - res = -EBUSY; - PDRAW_LOG_ERRNO("mRtspClient", -res); - return res; - } - - /* Check that we actually have something after 'rtsp://' */ - if (url.length() < 8) - return -EINVAL; - - /* Find first '/' in url */ - n = url.find("/", 7); - if (n == std::string::npos) - return -EINVAL; - mServerAddr = url.substr(7, n - 7); - mRtspAddr = url.substr(0, n); - mRtspPath = url.substr(n + 1); - - mSessionProtocol = RTSP; - - std::string userAgent; - mSession->getSettings()->getSoftwareVersion(&userAgent); - - /* Create the RTSP client */ - res = rtsp_client_new(mSession->getLoop(), - userAgent.c_str(), - &mRtspClientCbs, - this, - &mRtspClient); - if (res < 0) { - PDRAW_LOG_ERRNO("rtsp_client_new", -res); - return res; - } - - res = rtsp_client_connect(mRtspClient, mRtspAddr.c_str()); - if (res < 0) { - PDRAW_LOG_ERRNO("rtsp_client_connect", -res); - return res; - } - - return 0; -} - - -int StreamDemuxer::start(void) -{ - int res; - - if ((mState == STARTED) || (mState == STARTING)) { - return 0; - } - if (mState != CREATED) { - PDRAW_LOGE("demuxer is not created"); - return -EPROTO; - } - setState(STARTING); - - if (mUrl.length() > 0) { - std::string ext = mUrl.substr(mUrl.length() - 4, 4); - std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); - - if (mUrl.substr(0, 7) == "rtsp://") { - res = startRtsp(mUrl); - if (res < 0) { - PDRAW_LOG_ERRNO("startRtsp", -res); - return res; - } - } else { - PDRAW_LOGE("unsupported URL"); - return -ENOSYS; - } - - setState(STARTED); - } else { - /* TODO create VideoMedia and setup */ - if (mVideoMedias.size() >= 1) { - res = mVideoMedias.front()->startRtpAvp(); - if (res < 0) { - PDRAW_LOG_ERRNO("startRtpAvp", -res); - return res; - } - } - - setState(STARTED); - openResponse(0); - readyToPlay(true); - } - - return 0; -} - - -int StreamDemuxer::stop(void) -{ - int ret = 0; - - if ((mState == STOPPED) || (mState == STOPPING)) - return 0; - if (mState != STARTED && mState != STARTING) { - PDRAW_LOGE("demuxer is not started"); - return -EPROTO; - } - setState(STOPPING); - readyToPlay(false); - - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - (*p)->sendDownstreamEvent(CodedChannel::DownstreamEvent::EOS); - p++; - } - - mChannelsReadyForStop = false; - mNetworkReadyForStop = false; - if (mSessionProtocol == RTSP) { - if (mRtspState == SETUP_DONE) { - ret = rtsp_client_teardown( - mRtspClient, - mRtspSessionId, - nullptr, - 0, - nullptr, - RTSP_CLIENT_DEFAULT_RESP_TIMEOUT_MS); - if (ret < 0) { - PDRAW_LOG_ERRNO("rtsp_client_teardown", -ret); - /* disconnect directly the rstp */ - ret = rtsp_client_disconnect(mRtspClient); - if (ret < 0) { - PDRAW_LOG_ERRNO( - "rtsp_client_disconnect", -ret); - return ret; - } - } - } else { - ret = rtsp_client_disconnect(mRtspClient); - if (ret < 0) { - PDRAW_LOG_ERRNO("rtsp_client_disconnect", -ret); - return ret; - } - } - } else { - mRunning = false; - mNetworkReadyForStop = true; - if (mVideoMedias.size() >= 1) - mVideoMedias.front()->stopRtpAvp(); - } - - CodedSource::lock(); - - ret = flush(); - if (ret < 0) - PDRAW_LOG_ERRNO("flush", -ret); - - CodedSource::unlock(); - - if (mNetworkReadyForStop && mChannelsReadyForStop) { - mChannelsReadyForStop = false; - mNetworkReadyForStop = false; - closeResponse(0); - setStateAsyncNotify(STOPPED); - } - - return ret; -} - - -int StreamDemuxer::flush(void) -{ - return flush(false); -} - - -int StreamDemuxer::flush(bool destroyMedias) -{ - if ((mState != STARTED) && (mState != STOPPING)) { - PDRAW_LOGE("demuxer is not started"); - return -EPROTO; - } - - CodedSource::lock(); - - mDestroyMediasAfterFlush = destroyMedias; - - if (mFlushing) { - CodedSource::unlock(); - return -EALREADY; - } - - mFlushChannelCount = 0; - - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - (*p)->flush(); - p++; - } - - unsigned int outputMediaCount = getOutputMediaCount(); - for (unsigned int i = 0; i < outputMediaCount; i++) { - CodedVideoMedia *media = getOutputMedia(i); - if (media == nullptr) { - PDRAW_LOGW("failed to get media at index %d", i); - continue; - } - - mFlushChannelCount += getOutputChannelCount(media); - } - - if (mFlushChannelCount == 0) { - mChannelsReadyForStop = true; - mFlushing = false; - mDestroyMediasAfterFlush = false; - } - - CodedSource::unlock(); - - return 0; -} - - -void StreamDemuxer::onChannelFlushed(CodedChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - CodedSource::lock(); - - CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); - if (media == nullptr) { - PDRAW_LOGE("media not found"); - CodedSource::unlock(); - return; - } - PDRAW_LOGD("'%s': channel flushed media name=%s (channel key=%p)", - Element::getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - if ((*p)->hasMedia(media)) { - (*p)->channelFlushed(channel); - break; - } - p++; - } - - if (mState == STOPPING || mDestroyMediasAfterFlush) { - int ret = channel->teardown(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->teardown", -ret); - } - - if (--mFlushChannelCount == 0) { - mFlushing = false; - mDestroyMediasAfterFlush = false; - } - - CodedSource::unlock(); -} - - -void StreamDemuxer::onChannelUnlink(CodedChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); - if (media == nullptr) { - PDRAW_LOGE("media not found"); - return; - } - - int ret = removeOutputChannel(media, channel->getKey()); - if (ret < 0) - PDRAW_LOG_ERRNO("removeOutputChannel", -ret); - - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - if ((*p)->hasMedia(media)) { - (*p)->channelUnlink(channel); - break; - } - p++; - } - - ret = pomp_loop_idle_add_with_cookie( - this->mSession->getLoop(), completeTeardownAsync, this, this); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_loop_idle_add_with_cookie", -ret); -} - - -void StreamDemuxer::completeTeardownAsync(void *userdata) -{ - StreamDemuxer *self = (StreamDemuxer *)userdata; - self->completeTeardown(); -} - - -void StreamDemuxer::completeTeardown(void) -{ - CodedSource::lock(); - - unsigned int outputMediaCount = getOutputMediaCount(); - for (unsigned int i = 0; i < outputMediaCount; i++) { - CodedVideoMedia *media = getOutputMedia(i); - if (media && getOutputChannelCount(media) > 0) { - CodedSource::unlock(); - return; - } - } - - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - delete *p; - p++; - } - mVideoMedias.clear(); - - CodedSource::unlock(); - - if (mState == STOPPING) { - mChannelsReadyForStop = true; - - /* If network is also ready, set the state to stopped */ - if (mNetworkReadyForStop && mChannelsReadyForStop) { - mChannelsReadyForStop = false; - mNetworkReadyForStop = false; - closeResponse(0); - setStateAsyncNotify(STOPPED); - } -#if 0 /* TODO: this should be re-enabled on a per-media basis */ - } else if (mCodecInfoChanging) { - PDRAW_LOGI("new output media"); - mCodecInfoChanging = false; - ret = setupInputMedia(); - if (ret < 0) { - PDRAW_LOG_ERRNO("setupInputMedia", -ret); - return; - } -#endif - } -} - - -void StreamDemuxer::onChannelResync(CodedChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - CodedSource::lock(); - - CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); - if (media == nullptr) { - PDRAW_LOGE("media not found"); - CodedSource::unlock(); - return; - } - PDRAW_LOGD("'%s': channel resync media name=%s (channel key=%p)", - Element::getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - if ((*p)->hasMedia(media)) { - (*p)->stop(); - CodedSource::unlock(); - return; - } - p++; - } - - CodedSource::unlock(); -} - - -void StreamDemuxer::onChannelVideoPresStats(CodedChannel *channel, - VideoPresStats *stats) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - if (stats == nullptr) { - PDRAW_LOG_ERRNO("stats", EINVAL); - return; - } - - CodedSource::lock(); - - CodedSource::onChannelVideoPresStats(channel, stats); - - CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); - if (media == nullptr) { - PDRAW_LOGE("media not found"); - CodedSource::unlock(); - return; - } - - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - if ((*p)->hasMedia(media)) { - (*p)->channelSendVideoPresStats(channel, stats); - break; - } - p++; - } - - CodedSource::unlock(); -} - - -void StreamDemuxer::onNewSdp(const char *content_base, const char *sdp) -{ - int res = 0; - struct sdp_session *session = nullptr; - char *remoteAddr = nullptr; - struct pdraw_demuxer_media *medias = nullptr; - std::vector selectedMedias; - std::vector::iterator m; - size_t mediasCount = 0, i; - bool found, noError = false; - - if (mState == STOPPING) { - PDRAW_LOGI("new SDP while stopping, ignore it"); - return; - } - - res = sdp_description_read(sdp, &session); - if (res < 0) { - PDRAW_LOG_ERRNO("sdp_description_read", -res); - return; - } - if (session->deletion) - PDRAW_LOGW("sdp refers to a no longer existing session"); - - if ((!mContentBase) && (content_base)) - mContentBase = strdup(content_base); - - /* Session-level metadata */ - sessionMetadataFromSdp(session, &mSessionMetaFromSdp); - - struct sdp_media *media = nullptr; - - list_walk_entry_forward(&session->medias, media, node) - { - if (media->type != SDP_MEDIA_TYPE_VIDEO || !media->control_url) - continue; - mediasCount++; - } - i = 0; - medias = (struct pdraw_demuxer_media *)calloc(mediasCount, - sizeof(*medias)); - if (medias == nullptr) - goto stop; - list_walk_entry_forward(&session->medias, media, node) - { - if (media->type != SDP_MEDIA_TYPE_VIDEO || !media->control_url) - continue; - -#if 0 - /* TODO: this is wrong with multistream: the codec info - * and clock rate will be from the last media in the - * list instead of the chosen media */ - if ((media->h264_fmtp.valid) && - (media->h264_fmtp.sps != nullptr) && - (media->h264_fmtp.pps != nullptr)) { - memset(&mCodecInfo, 0, sizeof(mCodecInfo)); - mCodecInfo.codec = VSTRM_CODEC_VIDEO_H264; - if (media->h264_fmtp.sps_size <= - sizeof(mCodecInfo.h264.sps)) { - memcpy(mCodecInfo.h264.sps, - media->h264_fmtp.sps, - media->h264_fmtp.sps_size); - mCodecInfo.h264.spslen = - media->h264_fmtp.sps_size; - } - if (media->h264_fmtp.pps_size <= - sizeof(mCodecInfo.h264.pps)) { - memcpy(mCodecInfo.h264.pps, - media->h264_fmtp.pps, - media->h264_fmtp.pps_size); - mCodecInfo.h264.ppslen = - media->h264_fmtp.pps_size; - } - } -#endif - mRtpClockRate = media->clock_rate; - - struct pdraw_demuxer_media *current = &medias[i]; - current->media_id = i + 1; - current->idx = i; - current->name = xstrdup(media->media_title); - if (current->name == nullptr) - current->name = xstrdup(media->control_url); - current->uri = xstrdup(media->control_url); - StreamDemuxer::VideoMedia::sessionMetadataFromSdp( - media, &mSessionMetaFromSdp, ¤t->session_meta); - current->is_default = current->session_meta.default_media; - if (current->is_default) - selectedMedias.push_back(current); - i++; - } - - if (mediasCount == 1 && selectedMedias.empty()) { - medias[0].is_default = 1; - selectedMedias.push_back(&medias[0]); - } else if (mediasCount == 0) { - /* Empty SDP */ - PDRAW_LOGI("empty SDP, no stream"); - /* An empty SDP means that both the server & the URL are good, - * but there is currently no streams. In this case, we can set - * the demuxer as STARTED here, and wait for an ANNOUCE to get - * the media. Otherwise, wait for the setup response before - * setting the state. If we have any media, remove them */ - flush(true); - setState(STARTED); - openResponse(0); - noError = true; - goto stop; - } else if (selectedMedias.empty()) { - /* If no default media, check for a media with camera_type == - * VMETA_CAMERA_TYPE_FRONT, and flag it as the default media */ - for (i = 0; i < mediasCount; i++) { - struct pdraw_demuxer_media *current = &medias[i]; - if (current->session_meta.camera_type != - VMETA_CAMERA_TYPE_FRONT) - continue; - current->is_default = 1; - selectedMedias.push_back(current); - } - } - - res = selectMedia(medias, mediasCount); - if (res == 0 || res == -ENOSYS) { - if (selectedMedias.empty()) { - PDRAW_LOGE( - "application requested default media, " - "but no default media found"); - res = -ENOENT; - goto stop; - } - if (selectedMedias.size() == 1) { - PDRAW_LOGI("auto-selecting media %d (%s)", - selectedMedias.back()->media_id, - selectedMedias.back()->name); - } else { - PDRAW_LOGI("audo-selecting medias {"); - m = selectedMedias.begin(); - while (m != selectedMedias.end()) { - PDRAW_LOGI(" - %d (%s)", - (*m)->media_id, - (*m)->name); - m++; - } - PDRAW_LOGI("}"); - } - } else if (res == -ECANCELED) { - PDRAW_LOGI("application cancelled the media selection"); - setState(STARTED); - openResponse(0); - goto stop; - } else if (res < 0) { - PDRAW_LOGE("application failed to select a video media"); - /* Selecting a wrong video media is an error, stop the demuxer - * to either report an open response, or an unrecoverable error - * to the application */ - goto stop; - } else { - selectedMedias.clear(); - for (i = 0; i < mediasCount; i++) { - if (!(res & (1 << medias[i].media_id))) - continue; - selectedMedias.push_back(&medias[i]); - PDRAW_LOGI("application selected media %d (%s)", - selectedMedias.back()->media_id, - selectedMedias.back()->name); - } - if (selectedMedias.empty()) { - PDRAW_LOGE("the application requested no valid media"); - res = -ENOENT; - goto stop; - } - } - - if (session->connection_addr && - strcmp(session->connection_addr, "0.0.0.0") != 0) - remoteAddr = strdup(session->connection_addr); - else if (session->server_addr && - strcmp(session->server_addr, "0.0.0.0") != 0) - remoteAddr = strdup(session->server_addr); - else if (mServerAddr.length() > 0) - remoteAddr = strdup(mServerAddr.c_str()); - if (remoteAddr == nullptr) { - PDRAW_LOGE("failed to get server address"); - goto stop; - } - - if (session->range.start.format == SDP_TIME_FORMAT_NPT) { - mDuration = (uint64_t)session->range.stop.npt.sec * 1000000 + - (uint64_t)session->range.stop.npt.usec; - mTrackDuration = mDuration; - } - - mRtspState = DESCRIBE_DONE; - PDRAW_LOGD("RTSP state change to %s", getRtspStateStr(mRtspState)); - - mLocalAddr = "0.0.0.0"; - mRemoteAddr = std::string(remoteAddr); - - m = selectedMedias.begin(); - while (m != selectedMedias.end()) { - found = false; - media = nullptr; - int idx = 0; - list_walk_entry_forward(&session->medias, media, node) - { - if (idx == (*m)->idx) { - found = true; - break; - } - idx++; - } - if (!found) { - PDRAW_LOGE( - "failed to find the selected " - "media in the list"); - goto stop; - } - StreamDemuxer::VideoMedia *videoMedia = createVideoMedia(); - res = videoMedia->setup(media); - if (res < 0) { - PDRAW_LOG_ERRNO("VideoMedia::setup", -res); - delete videoMedia; - goto stop; - } - mVideoMedias.push_back(videoMedia); - m++; - } - - goto exit; - -stop: - if (!noError) - onUnrecoverableError(); - readyToPlay(false); - if (mRtspState == SETUP_DONE) { - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - (*p)->stopRtpAvp(); - p++; - } - } - mRtspState = OPTIONS_DONE; - PDRAW_LOGD("RTSP state change to %s", getRtspStateStr(mRtspState)); - -exit: - if (medias != nullptr) { - for (i = 0; i < mediasCount; i++) { - free((char *)medias[i].name); - free((char *)medias[i].uri); - } - } - free(medias); - sdp_session_destroy(session); - free(remoteAddr); -} - -int StreamDemuxer::internalPlay(float speed) -{ - mRunning = true; - mSpeed = speed; - mFrameByFrame = false; - - if ((mSessionProtocol == RTSP) && (mRtspState == SETUP_DONE)) { - float scale = mSpeed; - struct rtsp_range range; - memset(&range, 0, sizeof(range)); - range.start.format = RTSP_TIME_FORMAT_NPT; - range.start.npt.now = 1; - range.stop.format = RTSP_TIME_FORMAT_NPT; - range.stop.npt.infinity = 1; - mUpdateTrackDuration = true; - int ret = rtsp_client_play(mRtspClient, - mRtspSessionId, - &range, - scale, - nullptr, - 0, - nullptr, - RTSP_CLIENT_DEFAULT_RESP_TIMEOUT_MS); - if (ret < 0) { - PDRAW_LOG_ERRNO("rtsp_client_play", -ret); - return ret; - } - mEndOfRangeNotified = false; - } - - return 0; -} - - -int StreamDemuxer::internalPause(void) -{ - mRunning = false; - mFrameByFrame = true; - - if ((mSessionProtocol == RTSP) && (mRtspState == SETUP_DONE)) { - struct rtsp_range range; - memset(&range, 0, sizeof(range)); - int ret = - rtsp_client_pause(mRtspClient, - mRtspSessionId, - &range, - nullptr, - 0, - nullptr, - RTSP_CLIENT_DEFAULT_RESP_TIMEOUT_MS); - if (ret < 0) { - PDRAW_LOG_ERRNO("rtsp_client_pause", -ret); - return ret; - } - mEndOfRangeNotified = false; - } - - return 0; -} - - -int StreamDemuxer::play(float speed) -{ - if (mState != STARTED) { - PDRAW_LOGE("demuxer is not started"); - return -EPROTO; - } - - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - (*p)->play(); - p++; - } - - if (speed == 0.) - return internalPause(); - else - return internalPlay(speed); -} - - -bool StreamDemuxer::isReadyToPlay(void) -{ - if (mState != STARTED) { - PDRAW_LOG_ERRNO("demuxer is not started", EPROTO); - return false; - } - - return mReadyToPlay; -} - - -bool StreamDemuxer::isPaused(void) -{ - if (mState != STARTED) { - PDRAW_LOG_ERRNO("demuxer is not started", EPROTO); - return false; - } - - bool running = mRunning && !mFrameByFrame; - - return !running; -} - - -int StreamDemuxer::previous(void) -{ - if (mState != STARTED) { - PDRAW_LOGE("demuxer is not started"); - return -EPROTO; - } - - if (!mFrameByFrame) { - PDRAW_LOGE("demuxer is not paused"); - return -EPROTO; - } - - if (mSessionProtocol != RTSP) - return -ENOSYS; - - if (mRtspState != SETUP_DONE) - return -EAGAIN; - -#if 0 /* this code is disabled because previousframe is not working yet on the \ - * server \ - */ - int ret = 0; - float scale = mSpeed; - struct rtsp_range range; - memset(&range, 0, sizeof(range)); - range.start.format = RTSP_TIME_FORMAT_NPT; - range.start.npt.sec = mPausePoint / 1000000; - range.start.npt.usec = mPausePoint - range.start.npt.sec * 1000000; - /* TODO: SMPTE timestamps*/ - int32_t start_usec = (int32_t)range.start.npt.usec - 2 * 34000; - if (start_usec < 0) { - if (range.start.npt.sec > 0) { - range.start.npt.sec--; - range.start.npt.usec = start_usec + 1000000; - } else { - range.start.npt.sec = 0; - range.start.npt.usec = 0; - } - } else { - range.start.npt.usec = start_usec; - } - range.stop = range.start; - range.stop.npt.usec += 4000; - if (range.stop.npt.usec >= 1000000) { - range.stop.npt.sec++; - range.stop.npt.usec -= 1000000; - } - ret = rtsp_client_play(mRtspClient, - mRtspSessionId, - &range, - scale, - nullptr, - RTSP_CLIENT_DEFAULT_RESP_TIMEOUT_MS); - if (ret < 0) { - PDRAW_LOG_ERRNO("rtsp_client_play", -ret); - return ret; - } - mSeeking = true; - ret = pomp_timer_clear(mRangeTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); - mEndOfRangeNotified = false; - - return 0; -#else - return -ENOSYS; -#endif -} - - -int StreamDemuxer::next(void) -{ - if (mState != STARTED) { - PDRAW_LOGE("demuxer is not started"); - return -EPROTO; - } - - if (!mFrameByFrame) { - PDRAW_LOGE("demuxer is not paused"); - return -EPROTO; - } - - if (mSessionProtocol != RTSP) - return -ENOSYS; - - if (mRtspState != SETUP_DONE) - return -EAGAIN; - -#if 0 /* this code is disabled because nextframe is not working yet on \ - * the server \ - */ - float scale = mSpeed; - struct rtsp_range range; - memset(&range, 0, sizeof(range)); - range.start.format = RTSP_TIME_FORMAT_NPT; - range.start.npt.sec = mPausePoint / 1000000; - range.start.npt.usec = mPausePoint - range.start.npt.sec * 1000000; - range.stop = range.start; - /* TODO: SMPTE timestamps*/ - range.stop.npt.usec += 1000; - if (range.stop.npt.usec >= 1000000) { - range.stop.npt.sec++; - range.stop.npt.usec -= 1000000; - } - int ret = rtsp_client_play(mRtspClient, - mRtspSessionId, - &range, - scale, - nullptr, - RTSP_CLIENT_DEFAULT_RESP_TIMEOUT_MS); - if (ret < 0) { - PDRAW_LOG_ERRNO("rtsp_client_play", -ret); - return ret; - } - mSeeking = true; - ret = pomp_timer_clear(mRangeTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); - mEndOfRangeNotified = false; - - return 0; -#else - return -ENOSYS; -#endif -} - - -int StreamDemuxer::seek(int64_t delta, bool exact) -{ - if (mState != STARTED) { - PDRAW_LOGE("demuxer is not started"); - return -EPROTO; - } - - int64_t ts = (int64_t)mCurrentTime + delta; - if (ts < 0) - ts = 0; - if (ts > (int64_t)mTrackDuration) - ts = mTrackDuration; - - return seekTo(ts, exact); -} - - -int StreamDemuxer::seekTo(uint64_t timestamp, bool exact) -{ - if (mState != STARTED) { - PDRAW_LOGE("demuxer is not started"); - return -EPROTO; - } - - if (mSessionProtocol != RTSP) - return -ENOSYS; - - if (mRtspState != SETUP_DONE) - return -EAGAIN; - - float scale = mSpeed; - struct rtsp_range range; - memset(&range, 0, sizeof(range)); - range.start.format = RTSP_TIME_FORMAT_NPT; - range.start.npt.sec = timestamp / 1000000; - range.start.npt.usec = timestamp - range.start.npt.sec * 1000000; - if (mRunning) { - range.stop.format = RTSP_TIME_FORMAT_NPT; - range.stop.npt.infinity = 1; - } else { - /* Only play a single frame if the seek was requested - * while in pause */ - range.stop = range.start; - /* TODO: SMPTE timestamps*/ - range.stop.npt.usec += 1000; - if (range.stop.npt.usec >= 1000000) { - range.stop.npt.sec++; - range.stop.npt.usec -= 1000000; - } - } - mUpdateTrackDuration = range.stop.npt.infinity ? true : false; - int ret = rtsp_client_play(mRtspClient, - mRtspSessionId, - &range, - scale, - nullptr, - 0, - nullptr, - RTSP_CLIENT_DEFAULT_RESP_TIMEOUT_MS); - if (ret < 0) { - PDRAW_LOG_ERRNO("rtsp_client_play", -ret); - return ret; - } - mSeeking = true; - mEndOfRangeNotified = false; - auto p = mVideoMedias.begin(); - while (p != mVideoMedias.end()) { - (*p)->play(); - p++; - } - - return 0; -} - - -uint64_t StreamDemuxer::getDuration(void) -{ - return (mDuration != 0) ? mDuration : (uint64_t)-1; -} - - -uint64_t StreamDemuxer::getCurrentTime(void) -{ - if (mSessionProtocol == RTSP) - return mCurrentTime; - else - return (mStartTime != 0) ? mCurrentTime - mStartTime : 0; -} - - -const char *StreamDemuxer::getRtspStateStr(StreamDemuxer::RtspState val) -{ - switch (val) { - case StreamDemuxer::RtspState::DISCONNECTED: - return "DISCONNECTED"; - case StreamDemuxer::RtspState::CONNECTED: - return "CONNECTED"; - case StreamDemuxer::RtspState::OPTIONS_DONE: - return "OPTIONS_DONE"; - case StreamDemuxer::RtspState::DESCRIBE_DONE: - return "DESCRIBE_DONE"; - case StreamDemuxer::RtspState::SETUP_DONE: - return "SETUP_DONE"; - default: - return nullptr; - } -} - - -StreamDemuxer::VideoMedia::VideoMedia(StreamDemuxer *demuxer) : - mDemuxer(demuxer), mReceiver(nullptr), mLocalStreamPort(0), - mLocalControlPort(0), mRemoteStreamPort(0), - mRemoteControlPort(0), mVideoMedias(nullptr), mNbVideoMedias(0), - mSdpMedia(nullptr), mH264Reader(nullptr), mFrameTimer(nullptr), - mRangeTimer(nullptr), mSsrc(0), mFlushing(false), - mFlushChannelCount(0), mFirstFrame(true), - mLastFrameReceiveTime(0), mFrameIndex(0), mCodecInfo({}), - mWaitForCodecInfo(false), mCodecInfoChanging(false), - mWaitForSync(false), mRecoveryFrameCount(0), - mCurrentFrame(nullptr), mCurrentMem(nullptr), - mCurrentMemOffset(0), mCurrentFrameCaptureTs(0), - mSessionMetaFromSdp({}) -{ - std::string name = demuxer->getName() + "#VideoMedia"; - Loggable::setName(name); -} - - -StreamDemuxer::VideoMedia::~VideoMedia(void) -{ - int ret; - - teardownMedia(); - - sdp_media_destroy(mSdpMedia); - - /* Clear any pending frames (received while the previous media - * was being torn down */ - while (!mTempQueue.empty()) { - struct vstrm_frame *frame = mTempQueue.front(); - mTempQueue.pop(); - vstrm_frame_unref(frame); - } - - if (mCurrentFrame != nullptr) { - ret = mbuf_coded_video_frame_unref(mCurrentFrame); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_unref", -ret); - } - - if (mCurrentMem != nullptr) { - ret = mbuf_mem_unref(mCurrentMem); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_mem_unref", -ret); - } - - if (mFrameTimer != nullptr) { - ret = pomp_timer_clear(mFrameTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); - ret = pomp_timer_destroy(mFrameTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_destroy", -ret); - } - - if (mRangeTimer != nullptr) { - ret = pomp_timer_clear(mRangeTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); - ret = pomp_timer_destroy(mRangeTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_destroy", -ret); - } -} - - -bool StreamDemuxer::VideoMedia::hasMedia(CodedVideoMedia *media) -{ - for (unsigned int i = 0; i < mNbVideoMedias; i++) { - if (mVideoMedias[i] == media) - return true; - } - return false; -} - - -int StreamDemuxer::VideoMedia::setup(const struct sdp_media *media) -{ - int ret; - - std::string name = - mDemuxer->getName() + - ((media != nullptr) ? "#" + std::string(media->control_url) - : "#NULL"); - Loggable::setName(name); - - /* Create the frame timer */ - mFrameTimer = pomp_timer_new( - mDemuxer->mSession->getLoop(), &frameTimeoutCb, this); - if (mFrameTimer == nullptr) { - ret = -ENOMEM; - PDRAW_LOG_ERRNO("pomp_timer_new", -ret); - return ret; - } - - /* Create the end of range timer */ - mRangeTimer = pomp_timer_new( - mDemuxer->mSession->getLoop(), &rangeTimerCb, this); - if (mRangeTimer == nullptr) { - ret = -ENOMEM; - PDRAW_LOG_ERRNO("pomp_timer_new", -ret); - return ret; - } - - if (media != nullptr && mDemuxer->mSessionProtocol == RTSP) { - sdp_media_destroy(mSdpMedia); - mSdpMedia = sdp_media_new(); - if (mSdpMedia == nullptr) { - ret = -ENOMEM; - PDRAW_LOG_ERRNO("sdp_media_new", -ret); - return ret; - } - ret = sdp_media_copy(media, mSdpMedia); - if (ret != 0) { - PDRAW_LOG_ERRNO("sdp_media_copy", -ret); - return ret; - } - } - - mDemuxer->mSetupRequestsCount++; - ret = prepareSetup(); - if (ret == -EINPROGRESS) { - /* Nothing to do, subclass will call finishSetup when needed */ - return 0; - } else if (ret != 0) { - PDRAW_LOG_ERRNO("prepareSetup", -ret); - return ret; - } - finishSetup(); - return 0; -} - -void StreamDemuxer::VideoMedia::finishSetup(void) -{ - if (mSdpMedia != nullptr) { - /* Media-level metadata */ - sessionMetadataFromSdp(mSdpMedia, - &mDemuxer->mSessionMetaFromSdp, - &mSessionMetaFromSdp); - - SetupRequest req = { - .media = this, - .controlUrl = strdup(mSdpMedia->control_url), - .lowerTransport = getLowerTransport(), - .localStreamPort = getLocalStreamPort(), - .localControlPort = getLocalControlPort(), - .headerExt = getHeaderExt(), - .headerExtCount = getHeaderExtCount(), - }; - mDemuxer->mSetupRequests.push(req); - - (void)mDemuxer->processSetupRequests(); - } -} - - -int StreamDemuxer::VideoMedia::setupMedia(void) -{ - int ret; - CodedSource::OutputPort *basePort, *mediaPort; - - if (mDemuxer->mState != STARTED) { - PDRAW_LOGE("demuxer is not started"); - return -EPROTO; - } - /* Note: H.265 streaming is not supported */ - if (mCodecInfo.codec != VSTRM_CODEC_VIDEO_H264) { - PDRAW_LOGE("invalid codec info"); - return -EPROTO; - } - - mDemuxer->CodedSource::lock(); - - if (mNbVideoMedias > 0) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOGE("media already defined"); - return -EBUSY; - } - - mNbVideoMedias = 2; - mVideoMedias = (CodedVideoMedia **)calloc(mNbVideoMedias, - sizeof(*mVideoMedias)); - if (mVideoMedias == nullptr) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOGE("media allocation failed"); - return -ENOMEM; - } - for (unsigned int i = 0; i < mNbVideoMedias; i++) { - mVideoMedias[i] = new CodedVideoMedia(mDemuxer->mSession); - if (mVideoMedias[i] == nullptr) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOGE("media allocation failed"); - return -ENOMEM; - } - switch (i) { - case 0: - mVideoMedias[i]->format = vdef_h264_avcc; - break; - case 1: - mVideoMedias[i]->format = vdef_h264_byte_stream; - break; - default: - break; - } - ret = mDemuxer->addOutputPort(mVideoMedias[i]); - if (ret < 0) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOG_ERRNO("addOutputPort", -ret); - return ret; - } - std::string path = mDemuxer->Element::getName() + "$" + - mVideoMedias[i]->getName(); - mVideoMedias[i]->setPath(path); - ret = mVideoMedias[i]->setPs(nullptr, - 0, - mCodecInfo.h264.sps, - mCodecInfo.h264.spslen, - mCodecInfo.h264.pps, - mCodecInfo.h264.ppslen); - if (ret < 0) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOG_ERRNO("media->setPs", -ret); - return ret; - } - mVideoMedias[i]->sessionMeta = mSessionMetaFromSdp; - mVideoMedias[i]->playbackType = - PDRAW_PLAYBACK_TYPE_LIVE; /* TODO: live/replay */ - mVideoMedias[i]->duration = mDemuxer->mDuration; - } - - /* Create the H.264 reader - * Note: H.265 streaming is not supported */ - ret = h264_reader_new(&mH264Cbs, this, &mH264Reader); - if (ret < 0) { - PDRAW_LOG_ERRNO("h264_reader_new", -ret); - return ret; - } - - ret = h264_reader_parse_nalu( - mH264Reader, 0, mCodecInfo.h264.sps, mCodecInfo.h264.spslen); - if (ret < 0) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOG_ERRNO("h264_reader_parse_nalu:sps", -ret); - return ret; - } - - ret = h264_reader_parse_nalu( - mH264Reader, 0, mCodecInfo.h264.pps, mCodecInfo.h264.ppslen); - if (ret < 0) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOG_ERRNO("h264_reader_parse_nalu:pps", -ret); - return ret; - } - - /* Create the output buffers pool on the last media. As the medias will - * be destroyed in creation order, this ensures that the media which - * owns the buffers pool will be the last destroyed */ - ret = mDemuxer->createOutputPortMemoryPool( - mVideoMedias[1], - DEMUXER_OUTPUT_BUFFER_COUNT, - mVideoMedias[1]->info.resolution.width * - mVideoMedias[1]->info.resolution.height * 3 / 4); - if (ret < 0) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOG_ERRNO("createOutputPortMemoryPool", -ret); - return ret; - } - /* Make the pool shared between all medias */ - basePort = mDemuxer->getOutputPort(mVideoMedias[1]); - mediaPort = mDemuxer->getOutputPort(mVideoMedias[0]); - if (basePort == nullptr || mediaPort == nullptr) { - PDRAW_LOGW("unable to share memory pool between medias"); - } else { - mediaPort->pool = basePort->pool; - mediaPort->sharedPool = true; - } - - /* New synchronization is needed */ - mWaitForSync = true; - mRecoveryFrameCount = 0; - - mDemuxer->CodedSource::unlock(); - - if (mDemuxer->CodedSource::mListener) { - for (unsigned int i = 0; i < mNbVideoMedias; i++) - mDemuxer->CodedSource::mListener->onOutputMediaAdded( - mDemuxer, mVideoMedias[i]); - } - - /* Process any pending frames (received while the previous media - * was being torn down */ - while (!mTempQueue.empty()) { - struct vstrm_frame *frame = mTempQueue.front(); - mTempQueue.pop(); - ret = processFrame(frame); - if ((ret < 0) && (ret != -EAGAIN)) - PDRAW_LOG_ERRNO("processFrame", -ret); - vstrm_frame_unref(frame); - } - - return 0; -} - - -void StreamDemuxer::VideoMedia::teardownMedia(void) -{ - /* Destroy the H.264 reader */ - if (mH264Reader != nullptr) { - int ret = h264_reader_destroy(mH264Reader); - if (ret < 0) - PDRAW_LOG_ERRNO("h264_reader_destroy", -ret); - mH264Reader = nullptr; - } - - /* Remove the output ports */ - for (unsigned int i = 0; i < mNbVideoMedias; i++) { - if (mDemuxer->CodedSource::mListener) { - mDemuxer->CodedSource::mListener->onOutputMediaRemoved( - mDemuxer, mVideoMedias[i]); - } - int ret = mDemuxer->removeOutputPort(mVideoMedias[i]); - if (ret < 0) { - PDRAW_LOG_ERRNO("removeOutputPort", -ret); - } else { - delete mVideoMedias[i]; - } - } - free(mVideoMedias); - mVideoMedias = nullptr; - mNbVideoMedias = 0; -} - - -int StreamDemuxer::VideoMedia::createReceiver(void) -{ - struct vstrm_receiver_cfg *cfg; - std::string fn; - std::string sn; - std::string sv; - int ret; - - cfg = (struct vstrm_receiver_cfg *)calloc(1, sizeof(*cfg)); - if (cfg == nullptr) { - ret = -ENOMEM; - PDRAW_LOG_ERRNO("calloc", -ret); - goto error; - } - - /* Create the stream receiver */ - cfg->loop = mDemuxer->mSession->getLoop(); - cfg->flags = VSTRM_RECEIVER_FLAGS_H264_GEN_CONCEALMENT_SLICE | - VSTRM_RECEIVER_FLAGS_ENABLE_RTCP | - VSTRM_RECEIVER_FLAGS_ENABLE_RTCP_EXT; - mDemuxer->mSession->getSettings()->getFriendlyName(&fn); - strncpy(cfg->self_meta.friendly_name, - fn.c_str(), - sizeof(cfg->self_meta.friendly_name)); - cfg->self_meta.friendly_name[sizeof(cfg->self_meta.friendly_name) - 1] = - '\0'; - mDemuxer->mSession->getSettings()->getSerialNumber(&sn); - strncpy(cfg->self_meta.serial_number, - sn.c_str(), - sizeof(cfg->self_meta.serial_number)); - cfg->self_meta.serial_number[sizeof(cfg->self_meta.serial_number) - 1] = - '\0'; - mDemuxer->mSession->getSettings()->getSoftwareVersion(&sv); - strncpy(cfg->self_meta.software_version, - sv.c_str(), - sizeof(cfg->self_meta.software_version)); - cfg->self_meta - .software_version[sizeof(cfg->self_meta.software_version) - 1] = - '\0'; - ret = vstrm_receiver_new(cfg, &mReceiverCbs, this, &mReceiver); - if (ret < 0) { - mReceiver = nullptr; - PDRAW_LOG_ERRNO("vstrm_receiver_new", -ret); - goto error; - } - - /* Provide the SPS/PPS out of band if available */ - if (mCodecInfo.codec == VSTRM_CODEC_VIDEO_H264) { - ret = vstrm_receiver_set_codec_info( - mReceiver, &mCodecInfo, mSsrc); - if (ret < 0) - PDRAW_LOG_ERRNO("vstrm_receiver_set_codec_info", -ret); - } - - free(cfg); - return 0; - -error: - destroyReceiver(); - free(cfg); - return ret; -} - - -int StreamDemuxer::VideoMedia::destroyReceiver(void) -{ - int res; - if (mReceiver != nullptr) { - /* Destroy the receiver */ - res = vstrm_receiver_destroy(mReceiver); - if (res < 0) - PDRAW_LOG_ERRNO("vstrm_receiver_destroy", -res); - mReceiver = nullptr; - } - return 0; -} - - -void StreamDemuxer::VideoMedia::play(void) -{ - int ret; - ret = pomp_timer_clear(mFrameTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); - ret = pomp_timer_clear(mRangeTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); -} - - -void StreamDemuxer::VideoMedia::stop(void) -{ - pomp_timer_clear(mFrameTimer); - - if (mCurrentFrame != nullptr) { - int ret = mbuf_coded_video_frame_unref(mCurrentFrame); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_unref", -ret); - mCurrentFrame = nullptr; - } - - if (mCurrentMem != nullptr) { - int ret = mbuf_mem_unref(mCurrentMem); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_mem_unref", -ret); - mCurrentMem = nullptr; - } - - mWaitForSync = true; - mRecoveryFrameCount = 0; -} - - -void StreamDemuxer::VideoMedia::flush(void) -{ - mDemuxer->CodedSource::lock(); - - stop(); - mFlushing = true; - mFlushChannelCount = 0; - - for (unsigned int i = 0; i < mNbVideoMedias; i++) { - unsigned int outputChannelCount = - mDemuxer->getOutputChannelCount(mVideoMedias[i]); - mFlushChannelCount += outputChannelCount; - - /* Flush the output channels */ - for (unsigned int j = 0; j < outputChannelCount; j++) { - CodedChannel *channel = - mDemuxer->getOutputChannel(mVideoMedias[i], j); - if (channel == nullptr) { - PDRAW_LOGW("failed to get channel at index %d", - j); - continue; - } - int ret = channel->flush(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->flush", -ret); - } - } - - mDemuxer->CodedSource::unlock(); -} - - -void StreamDemuxer::VideoMedia::channelFlushed(CodedChannel *channel) -{ - mFlushChannelCount--; - if (mFlushChannelCount <= 0) - mFlushing = false; -} - - -void StreamDemuxer::VideoMedia::channelUnlink(CodedChannel *channel) -{ - mDemuxer->CodedSource::lock(); - - for (unsigned int i = 0; i < mNbVideoMedias; i++) { - unsigned int outputChannelCount = - mDemuxer->getOutputChannelCount(mVideoMedias[i]); - if (outputChannelCount > 0) { - mDemuxer->CodedSource::unlock(); - return; - } - } - - mDemuxer->CodedSource::unlock(); - - if (mCodecInfoChanging) { - teardownMedia(); - PDRAW_LOGI("new output media"); - mCodecInfoChanging = false; - int ret = setupMedia(); - if (ret < 0) { - PDRAW_LOG_ERRNO("setupMedia", -ret); - return; - } - } -} - - -void StreamDemuxer::VideoMedia::sendDownstreamEvent( - CodedChannel::DownstreamEvent event) -{ - for (unsigned int i = 0; i < mNbVideoMedias; i++) { - int res = mDemuxer->CodedSource::sendDownstreamEvent( - mVideoMedias[i], event); - if (res < 0) - PDRAW_LOG_ERRNO("CodedSource::sendDownstreamEvent", - -res); - } -} - - -int StreamDemuxer::VideoMedia::processFrame(struct vstrm_frame *frame) -{ - int ret = 0, err; - unsigned int outputChannelCount = 0; - CodedChannel *channel; - CodedVideoMedia::Frame data = {}; - uint32_t flags = 0; - size_t frameSize = 0, bufSize; - uint8_t *buf; - bool isIdr = false; - struct timespec ts = {0, 0}; - uint64_t curTime = 0; - uint64_t remainingPlayTime = 0; - unsigned int requiredMediaIndex; - CodedVideoMedia *requiredMedia; - struct vdef_coded_frame frameInfo; - struct mbuf_coded_video_frame *outputFrame = nullptr; - unsigned int sliceCount = 0; - int64_t clock_delta = 0; - uint32_t precision = UINT32_MAX; - - mDemuxer->CodedSource::lock(); - - /* Get an output memory */ - if (mCurrentFrame != nullptr) { - ret = mbuf_coded_video_frame_unref(mCurrentFrame); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_unref", -ret); - mCurrentFrame = nullptr; - } - if (mCurrentMem != nullptr) { - ret = mbuf_mem_unref(mCurrentMem); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_mem_unref", -ret); - mCurrentMem = nullptr; - } - ret = mDemuxer->getOutputMemory(mVideoMedias, - mNbVideoMedias, - &mCurrentMem, - &requiredMediaIndex); - if ((ret < 0) || (mCurrentMem == nullptr)) { - mDemuxer->CodedSource::unlock(); - PDRAW_LOGW("failed to get an output memory (%d)", ret); - flush(); - return ret; - } - requiredMedia = mVideoMedias[requiredMediaIndex]; - ret = mbuf_mem_get_data(mCurrentMem, (void **)&buf, &bufSize); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_mem_get_data", -ret); - goto out; - } - mCurrentFrameCaptureTs = 0; - mCurrentMemOffset = 0; - - /* Get the size of the frame */ - flags = (requiredMedia->format.data_format == - VDEF_CODED_DATA_FORMAT_BYTE_STREAM) - ? VSTRM_FRAME_COPY_FLAGS_INSERT_NALU_START_CODE - : VSTRM_FRAME_COPY_FLAGS_INSERT_NALU_SIZE; - flags |= VSTRM_FRAME_COPY_FLAGS_FILTER_SPS_PPS; - ret = vstrm_frame_get_size(frame, &frameSize, flags); - if (ret < 0) { - PDRAW_LOG_ERRNO("vstrm_frame_get_size", -ret); - goto out; - } - if (bufSize < frameSize) { - PDRAW_LOGW("input buffer too small (%zu vs. %zu", - bufSize, - frameSize); - ret = -EPROTO; - goto out; - } - - vdef_format_to_frame_info(&requiredMedia->info, &frameInfo.info); - frameInfo.info.timestamp = frame->timestamps.ntp_raw; - frameInfo.info.timescale = 1000000; - frameInfo.info.index = mFrameIndex++; - frameInfo.format = requiredMedia->format; - ret = mbuf_coded_video_frame_new(&frameInfo, &mCurrentFrame); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_new", -ret); - goto out; - } - - /* Copy the frame */ - /* TODO: avoid copy? */ - ret = vstrm_frame_copy(frame, buf, frameSize, flags); - if (ret < 0) { - PDRAW_LOG_ERRNO("vstrm_frame_copy", -ret); - goto out; - } - - frameInfo.type = VDEF_CODED_FRAME_TYPE_I; - for (uint32_t i = 0; i < frame->nalu_count; i++) { - enum h264_nalu_type naluType; - enum h264_slice_type sliceType = H264_SLICE_TYPE_UNKNOWN; - naluType = (enum h264_nalu_type)(*frame->nalus[i].cdata & 0x1F); - switch (naluType) { - /* Ignored NALUs */ - case H264_NALU_TYPE_SPS: - case H264_NALU_TYPE_PPS: - /* Ignore SPS/PPS */ - continue; - case H264_NALU_TYPE_SEI: - /* SEI NAL unit */ - ret = h264_reader_parse_nalu(mH264Reader, - 0, - frame->nalus[i].cdata, - frame->nalus[i].len); - if (ret < 0) - PDRAW_LOG_ERRNO("h264_reader_parse_nalu:sei", - -ret); - sliceType = H264_SLICE_TYPE_UNKNOWN; - break; - /* Slices */ - case H264_NALU_TYPE_SLICE_IDR: - /* IDR slice */ - isIdr = true; - frameInfo.type = VDEF_CODED_FRAME_TYPE_IDR; - /* fallthrough */ - case H264_NALU_TYPE_SLICE: - sliceType = /* TODO */ H264_SLICE_TYPE_UNKNOWN; - /* TODO (coverity complains that the code can - * never be reached) - * if (sliceType == H264_SLICE_TYPE_P) - * frameInfo.type = VDEF_CODED_FRAME_TYPE_P; */ - sliceCount++; - break; - /* Keep all other NALUs */ - default: - break; - } - struct vdef_nalu nalu = {}; - nalu.size = frame->nalus[i].len + 4; - nalu.h264.type = naluType; - nalu.h264.slice_type = sliceType; - /* TODO: h264.slice_mb_count */ - ret = mbuf_coded_video_frame_add_nalu( - mCurrentFrame, mCurrentMem, mCurrentMemOffset, &nalu); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_nalu", - -ret); - goto out; - } - mCurrentMemOffset += nalu.size; - } - - /* Ignore frames with 0 slices */ - if (sliceCount == 0) { - ret = 0; - PDRAW_LOGI("%s: empty frame, ignored", __func__); - goto out; - } - - /* If the frame is an IDR (excluding the generated gray IDR), - * sync is complete */ - if ((mWaitForSync) && (isIdr) && (!frame->info.gen_grey_idr)) { - mWaitForSync = false; - mRecoveryFrameCount = 0; - } - - /* If sync is in progress (intra refresh + recovery point received), - * decrement recoveryFrameCount for every ref frame until 0 */ - if ((!mWaitForSync) && (mRecoveryFrameCount > 0) && (frame->info.ref)) - mRecoveryFrameCount--; - data.isSync = isIdr; - data.isRef = frame->info.ref; - /* TODO: use unskewed timestamps */ - data.ntpTimestamp = frame->timestamps.ntp; - data.ntpUnskewedTimestamp = frame->timestamps.ntp_unskewed; - data.ntpRawTimestamp = frame->timestamps.ntp_raw; - data.ntpRawUnskewedTimestamp = frame->timestamps.ntp_raw_unskewed; - data.captureTimestamp = mCurrentFrameCaptureTs; - err = vstrm_receiver_get_clock_delta( - mReceiver, &clock_delta, &precision); - if (err < 0) { - if (err != -EAGAIN) - PDRAW_LOG_ERRNO("vstrm_receiver_get_clock_delta", -err); - } else { - data.localTimestamp = data.captureTimestamp - clock_delta; - data.localTimestampPrecision = precision; - } - data.recvStartTimestamp = frame->timestamps.recv_start; - data.recvEndTimestamp = frame->timestamps.recv_end; - - frameInfo.info.capture_timestamp = mCurrentFrameCaptureTs; - - if (frame->info.error || !frame->info.complete) - frameInfo.info.flags |= VDEF_FRAME_FLAG_VISUAL_ERROR; - if (mWaitForSync || mRecoveryFrameCount != 0) - frameInfo.info.flags |= VDEF_FRAME_FLAG_SILENT; - if (frame->info.uses_ltr) - frameInfo.info.flags |= VDEF_FRAME_FLAG_USES_LTR; - - /* Frame metadata */ - if (frame->metadata) { - ret = mbuf_coded_video_frame_set_metadata(mCurrentFrame, - frame->metadata); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_set_metadata", - -ret); - goto out; - } - } - - time_get_monotonic(&ts); - time_timespec_to_us(&ts, &curTime); - data.demuxOutputTimestamp = curTime; - mLastFrameReceiveTime = curTime; - - if (mDemuxer->mSessionProtocol == RTSP) { - mDemuxer->mCurrentTime = - frame->timestamps.ntp * mDemuxer->mSpeed - - mDemuxer->mNtpToNptOffset; - } else { - mDemuxer->mCurrentTime = frame->timestamps.ntp; - if (mDemuxer->mStartTime == 0) - mDemuxer->mStartTime = frame->timestamps.ntp; - } - data.playTimestamp = mDemuxer->mCurrentTime; - if (mDemuxer->mTrackDuration > 0) { - remainingPlayTime = - mDemuxer->mTrackDuration - mDemuxer->mCurrentTime; - if (remainingPlayTime < 1000000) { - /* Less than 1s of play time remaining */ - - /* Take the speed into account */ - remainingPlayTime = - (mDemuxer->mSpeed != 0.f) - ? remainingPlayTime / mDemuxer->mSpeed - : UINT64_MAX; - - if (remainingPlayTime < 1000000) { - /* Add 50ms margin */ - pomp_timer_set(mRangeTimer, - remainingPlayTime / 1000 + 50); - } - } - } - - /* update the frame info */ - ret = mbuf_coded_video_frame_set_frame_info(mCurrentFrame, &frameInfo); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_set_frame_info", -ret); - goto out; - } - - ret = mbuf_coded_video_frame_add_ancillary_buffer( - mCurrentFrame, - PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME, - &data, - sizeof(data)); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_ancillary_buffer", - -ret); - goto out; - } - - ret = mbuf_coded_video_frame_finalize(mCurrentFrame); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_finalize", -ret); - goto out; - } - - /* Queue the buffer in the output channels */ - for (unsigned int i = 0; i < mNbVideoMedias; i++) { - outputChannelCount = - mDemuxer->getOutputChannelCount(mVideoMedias[i]); - if (outputChannelCount == 0) - continue; - if (outputFrame != nullptr) - mbuf_coded_video_frame_unref(outputFrame); - outputFrame = mCurrentFrame; - if (!vdef_coded_format_cmp(&requiredMedia->format, - &mVideoMedias[i]->format)) { - /* The format is different, we need to pick another - * frame */ - int copy_ret = - mDemuxer->copyOutputFrame(requiredMedia, - mCurrentFrame, - mVideoMedias[i], - &outputFrame); - if (copy_ret < 0) { - PDRAW_LOG_ERRNO("copyOutputFrame", -copy_ret); - outputFrame = nullptr; - continue; - } - } else { - mbuf_coded_video_frame_ref(outputFrame); - } - ret = mbuf_coded_video_frame_get_frame_info(outputFrame, - &frameInfo); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_frame_info", - -ret); - goto out; - } - for (unsigned int j = 0; j < outputChannelCount; j++) { - const struct vdef_coded_format *caps; - int capsCount; - - channel = - mDemuxer->getOutputChannel(mVideoMedias[i], j); - if (channel == nullptr) { - PDRAW_LOGW("invalid channel"); - continue; - } - - capsCount = - channel->getCodedVideoMediaFormatCaps(&caps); - if (capsCount < 0) { - PDRAW_LOGW("invalid channel (no caps)"); - continue; - } - - if (!vdef_coded_format_intersect( - &frameInfo.format, caps, capsCount)) { - PDRAW_LOGW( - "incompatible coded video format " - "on channel"); - continue; - } - - int queue_ret = channel->queue(outputFrame); - if (queue_ret < 0) - PDRAW_LOG_ERRNO("channel->queue", -queue_ret); - } - } - if (outputFrame != nullptr) - mbuf_coded_video_frame_unref(outputFrame); - if ((mFirstFrame) && - (!(frameInfo.info.flags & VDEF_FRAME_FLAG_SILENT))) { - sendDownstreamEvent(CodedChannel::DownstreamEvent::SOS); - mFirstFrame = false; - } - -out: - mbuf_mem_unref(mCurrentMem); - mCurrentMem = nullptr; - mbuf_coded_video_frame_unref(mCurrentFrame); - mCurrentFrame = nullptr; - - mDemuxer->CodedSource::unlock(); - return ret; -} - - -void StreamDemuxer::VideoMedia::channelSendVideoPresStats(CodedChannel *channel, - VideoPresStats *stats) -{ - vstrm_video_stats vstrm_stats = {}; - - vstrm_stats.version = VSTRM_VIDEO_STATS_VERSION_2; - vstrm_stats.timestamp = stats->timestamp; - vstrm_stats.v2.presentation_frame_count = stats->presentationFrameCount; - vstrm_stats.v2.presentation_timestamp_delta_integral = - stats->presentationTimestampDeltaIntegral; - vstrm_stats.v2.presentation_timestamp_delta_integral_sq = - stats->presentationTimestampDeltaIntegralSq; - vstrm_stats.v2.presentation_timing_error_integral = - stats->presentationTimingErrorIntegral; - vstrm_stats.v2.presentation_timing_error_integral_sq = - stats->presentationTimingErrorIntegralSq; - vstrm_stats.v2.presentation_estimated_latency_integral = - stats->presentationEstimatedLatencyIntegral; - vstrm_stats.v2.presentation_estimated_latency_integral_sq = - stats->presentationEstimatedLatencyIntegralSq; - vstrm_stats.v2.player_latency_integral = stats->playerLatencyIntegral; - vstrm_stats.v2.player_latency_integral_sq = - stats->playerLatencyIntegralSq; - vstrm_stats.v2.estimated_latency_precision_integral = - stats->estimatedLatencyPrecisionIntegral; - - int err = vstrm_receiver_set_video_stats(mReceiver, &vstrm_stats); - if (err < 0) - PDRAW_LOG_ERRNO("vstrm_receiver_set_video_stats", -err); -} - - -void StreamDemuxer::VideoMedia::sessionMetadataFromSdp( - const struct sdp_media *media, - const struct vmeta_session *sessionMeta, - struct vmeta_session *meta) -{ - int err; - struct sdp_attr *attr = nullptr; - - *meta = *sessionMeta; - - if (media->media_title != nullptr) { - err = vmeta_session_streaming_sdp_read( - VMETA_STRM_SDP_TYPE_MEDIA_INFO, - media->media_title, - nullptr, - meta); - if (err < 0) - ULOG_ERRNO("vmeta_session_streaming_sdp_read", -err); - } - list_walk_entry_forward(&media->attrs, attr, node) - { - err = vmeta_session_streaming_sdp_read( - VMETA_STRM_SDP_TYPE_MEDIA_ATTR, - attr->value, - attr->key, - meta); - if (err < 0) - ULOG_ERRNO("vmeta_session_streaming_sdp_read", -err); - } -} - - -void StreamDemuxer::VideoMedia::h264UserDataSeiCb( - struct h264_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h264_sei_user_data_unregistered *sei, - void *userdata) -{ - VideoMedia *self = (VideoMedia *)userdata; - int ret = 0; - - if (self == nullptr) - return; - if ((buf == nullptr) || (len == 0)) - return; - if (sei == nullptr) - return; - if (self->mCurrentFrame == nullptr) - return; - - /* Ignore "Parrot Streaming" user data SEI */ - if (vstrm_h264_is_sei_streaming(sei->uuid)) - return; - - ret = mbuf_coded_video_frame_add_ancillary_buffer( - self->mCurrentFrame, MBUF_ANCILLARY_KEY_USERDATA_SEI, buf, len); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_ancillary_buffer", - -ret); - return; - } -} - - -void StreamDemuxer::VideoMedia::h264PicTimingSeiCb( - struct h264_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h264_sei_pic_timing *sei, - void *userdata) -{ - VideoMedia *self = (VideoMedia *)userdata; - - if (self == nullptr) - return; - if (ctx == nullptr) - return; - if (sei == nullptr) - return; - if (self->mCurrentFrame == nullptr) - return; - - self->mCurrentFrameCaptureTs = h264_ctx_sei_pic_timing_to_us(ctx, sei); -} - - -void StreamDemuxer::VideoMedia::h264RecoveryPointSeiCb( - struct h264_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h264_sei_recovery_point *sei, - void *userdata) -{ - VideoMedia *self = (VideoMedia *)userdata; - - if (self == nullptr) - return; - if (self->mCurrentFrame == nullptr) - return; - - if (self->mWaitForSync) { - self->mWaitForSync = false; - self->mRecoveryFrameCount = sei->recovery_frame_cnt + 1; - } -} - - -int StreamDemuxer::VideoMedia::sendCtrlCb(struct vstrm_receiver *stream, - struct tpkt_packet *pkt, - void *userdata) -{ - VideoMedia *self = (VideoMedia *)userdata; - - if (self == nullptr) - return -EINVAL; - - return self->sendCtrl(stream, pkt); -} - - -void StreamDemuxer::VideoMedia::codecInfoChangedCb( - struct vstrm_receiver *stream, - const struct vstrm_codec_info *info, - void *userdata) -{ - VideoMedia *self = (VideoMedia *)userdata; - int outputChannelCount = 0; - CodedChannel *channel; - int ret; - - if ((self == nullptr) || (info == nullptr)) - return; - if (info->codec != VSTRM_CODEC_VIDEO_H264) { - PDRAW_LOG_ERRNO("info->codec", EPROTO); - return; - } - - StreamDemuxer *demuxer = self->mDemuxer; - - if (demuxer->mState != STARTED) { - PDRAW_LOGE("demuxer is not started"); - return; - } - - PDRAW_LOGD("codec info changed"); - self->mWaitForCodecInfo = false; - - if ((!self->mCodecInfoChanging) && - (!memcmp(&self->mCodecInfo, info, sizeof(self->mCodecInfo)))) { - PDRAW_LOGI( - "codec info changed; no change in PS, " - "just flush and resync"); - self->flush(); - return; - } - self->mCodecInfo = *info; - - demuxer->CodedSource::lock(); - - if (self->mCurrentFrame != nullptr) { - ret = mbuf_coded_video_frame_unref(self->mCurrentFrame); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_unref", -ret); - self->mCurrentFrame = nullptr; - } - if (self->mCurrentMem != nullptr) { - ret = mbuf_mem_unref(self->mCurrentMem); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_mem_unref", -ret); - self->mCurrentMem = nullptr; - } - - if (self->mNbVideoMedias > 0) { - PDRAW_LOGI("change of output media"); - self->mCodecInfoChanging = true; - for (unsigned int i = 0; i < self->mNbVideoMedias; i++) { - outputChannelCount = demuxer->getOutputChannelCount( - self->mVideoMedias[i]); - - /* Teardown the output channels - * Note: loop downwards because calling teardown on a - * channel may or may not synchronously remove the - * channel from the output port */ - for (int j = outputChannelCount - 1; j >= 0; j--) { - channel = demuxer->getOutputChannel( - self->mVideoMedias[i], j); - if (channel == nullptr) { - PDRAW_LOGW( - "failed to get channel " - "at index %d", - j); - continue; - } - ret = channel->teardown(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->teardown", - -ret); - } - } - } else { - PDRAW_LOGI("new output media"); - self->mCodecInfoChanging = false; - ret = self->setupMedia(); - if (ret < 0) { - demuxer->CodedSource::unlock(); - PDRAW_LOG_ERRNO("setupMedia", -ret); - return; - } - } - - demuxer->CodedSource::unlock(); -} - - -void StreamDemuxer::VideoMedia::recvFrameCb(struct vstrm_receiver *stream, - struct vstrm_frame *frame, - void *userdata) -{ - VideoMedia *self = (VideoMedia *)userdata; - int ret; - - if ((self == nullptr) || (frame == nullptr)) - return; - - StreamDemuxer *demuxer = self->mDemuxer; - - if (demuxer->mState != STARTED) - return; - - if (demuxer->mRunning) { - pomp_timer_set(self->mFrameTimer, - DEMUXER_STREAM_TIMER_INTERVAL_MS); - } else if (demuxer->mSessionProtocol != RTSP) { - /* just ignore the frames */ - return; - } - - if (self->mCodecInfoChanging) { - /* Queue the frame to process later */ - vstrm_frame_ref(frame); - self->mTempQueue.push(frame); - return; - } - - if ((self->mWaitForCodecInfo) || (self->mFlushing)) - return; - - /* Process the incoming frame */ - ret = self->processFrame(frame); - if (ret < 0) { - if (ret != -EAGAIN) - PDRAW_LOG_ERRNO("processFrame", -ret); - return; - } -} - - -void StreamDemuxer::VideoMedia::sessionMetadataPeerChangedCb( - struct vstrm_receiver *stream, - const struct vmeta_session *meta, - void *userdata) -{ - VideoMedia *self = (VideoMedia *)userdata; - - if ((self == nullptr) || (meta == nullptr)) - return; - - PDRAW_LOGD("session metadata changed"); - - for (unsigned int i = 0; i < self->mNbVideoMedias; i++) - self->mVideoMedias[i]->sessionMeta = *meta; -} - - -void StreamDemuxer::VideoMedia::eventCb(struct vstrm_receiver *stream, - enum vstrm_event event, - void *userdata) -{ - VideoMedia *self = (VideoMedia *)userdata; - CodedChannel::DownstreamEvent evt; - bool sendEvent = false; - - if (self == nullptr) - return; - - PDRAW_LOGI("received custom RTCP event '%s'", - vstrm_event_to_str(event)); - - StreamDemuxer *demuxer = self->mDemuxer; - - if (demuxer->mState != STARTED) - return; - - switch (event) { - case VSTRM_EVENT_RECONFIGURE: - evt = CodedChannel::DownstreamEvent::RECONFIGURE; - sendEvent = true; - break; - case VSTRM_EVENT_RESOLUTION_CHANGE: - evt = CodedChannel::DownstreamEvent::TIMEOUT; - sendEvent = true; - break; - case VSTRM_EVENT_PHOTO_TRIGGER: - evt = CodedChannel::DownstreamEvent::PHOTO_TRIGGER; - sendEvent = true; - break; - default: - break; - } - - if (sendEvent) { - demuxer->CodedSource::lock(); - self->sendDownstreamEvent(evt); - demuxer->CodedSource::unlock(); - } -} - - -void StreamDemuxer::VideoMedia::goodbyeCb(struct vstrm_receiver *stream, - const char *reason, - void *userdata) -{ - VideoMedia *self = (VideoMedia *)userdata; - CodedChannel::DownstreamEvent event; - bool sendEvent = false; - - if (self == nullptr) - return; - - PDRAW_LOGI("received RTCP goodbye%s%s", - reason ? ", reason: " : "", - reason ? reason : ""); - - StreamDemuxer *demuxer = self->mDemuxer; - - if (demuxer->mState != STARTED) - return; - - pomp_timer_clear(self->mFrameTimer); - - /* Wait for new codec info */ - self->mWaitForCodecInfo = true; - - if (reason != nullptr) { - if (strcmp(reason, DEMUXER_STREAM_GOODBYE_REASON_RECONFIGURE) == - 0) { - event = CodedChannel::DownstreamEvent::RECONFIGURE; - sendEvent = true; - } else if ( - strcmp(reason, - DEMUXER_STREAM_GOODBYE_REASON_PHOTO_TRIGGER) == - 0) { - event = CodedChannel::DownstreamEvent::PHOTO_TRIGGER; - sendEvent = true; - } else { - event = CodedChannel::DownstreamEvent::EOS; - sendEvent = true; - self->mFirstFrame = true; - if (demuxer->mSessionProtocol == RTSP && - (strcmp(reason, - DEMUXER_STREAM_GOODBYE_REASON_USER) || - (demuxer->mState != STOPPING && - demuxer->mState != STOPPED))) { - /* We either received an unknown RTCP goodbye - * packet, or an unexpected (not initiated by a - * teardown) user_disconnection packet. Notify - * the application of an unrecoverable error */ - demuxer->onUnrecoverableError(); - } - } - - if (sendEvent) { - demuxer->CodedSource::lock(); - self->sendDownstreamEvent(event); - demuxer->CodedSource::unlock(); - } - } -} - - -void StreamDemuxer::VideoMedia::frameTimeoutCb(struct pomp_timer *timer, - void *userdata) -{ - VideoMedia *self = (VideoMedia *)userdata; - int res; - struct timespec ts = {0, 0}; - uint64_t curTime = 0; - - if (self == nullptr) - return; - - StreamDemuxer *demuxer = self->mDemuxer; - - if (demuxer->mState != STARTED) - return; - - res = time_get_monotonic(&ts); - if (res < 0) - PDRAW_LOG_ERRNO("time_get_monotonic", -res); - res = time_timespec_to_us(&ts, &curTime); - if (res < 0) - PDRAW_LOG_ERRNO("time_timespec_to_us", -res); - - demuxer->CodedSource::lock(); - if (curTime > - self->mLastFrameReceiveTime + DEMUXER_STREAM_FRAME_TIMEOUT_US) { - self->sendDownstreamEvent( - CodedChannel::DownstreamEvent::TIMEOUT); - } - demuxer->CodedSource::unlock(); -} - - -void StreamDemuxer::VideoMedia::rangeTimerCb(struct pomp_timer *timer, - void *userdata) -{ - VideoMedia *self = (VideoMedia *)userdata; - - if (self == nullptr) - return; - - StreamDemuxer *demuxer = self->mDemuxer; - - if (!demuxer->mEndOfRangeNotified) { - PDRAW_LOGI("end of range reached"); - self->sendDownstreamEvent(CodedChannel::DownstreamEvent::EOS); - demuxer->onEndOfRange(demuxer->mCurrentTime); - demuxer->mEndOfRangeNotified = true; - } -} - - -void StreamDemuxer::idleEndOfRangeNotification(void *userdata) -{ - StreamDemuxer *self = reinterpret_cast(userdata); - - if (self == nullptr) - return; - - if (!self->mEndOfRangeNotified) { - auto p = self->mVideoMedias.begin(); - while (p != self->mVideoMedias.end()) { - (*p)->sendDownstreamEvent( - CodedChannel::DownstreamEvent::EOS); - p++; - } - - PDRAW_LOGI("end of range reached"); - self->onEndOfRange(self->mCurrentTime); - self->mEndOfRangeNotified = true; - } -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Streaming demuxer + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_dmxstrm +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_demuxer_stream.hpp" +#include "pdraw_session.hpp" +#include "pdraw_utils.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace Pdraw { + + +#define DEMUXER_STREAM_TIMER_INTERVAL_MS 50 +#define DEMUXER_STREAM_FRAME_TIMEOUT_US 2000000 + +#define DEMUXER_STREAM_GOODBYE_REASON_USER "user disconnection" +#define DEMUXER_STREAM_GOODBYE_REASON_RECONFIGURE "configuration change" +#define DEMUXER_STREAM_GOODBYE_REASON_PHOTO_TRIGGER "photo trigger" + + +const struct rtsp_client_cbs StreamDemuxer::mRtspClientCbs = { + .socket_cb = &StreamDemuxer::onRtspSocketCreated, + .connection_state = &StreamDemuxer::onRtspConnectionState, + .session_removed = &StreamDemuxer::onRtspSessionRemoved, + .options_resp = &StreamDemuxer::onRtspOptionsResp, + .describe_resp = &StreamDemuxer::onRtspDescribeResp, + .setup_resp = &StreamDemuxer::onRtspSetupResp, + .play_resp = &StreamDemuxer::onRtspPlayResp, + .pause_resp = &StreamDemuxer::onRtspPauseResp, + .teardown_resp = &StreamDemuxer::onRtspTeardownResp, + .announce = &StreamDemuxer::onRtspAnnounce, +}; + + +const struct vstrm_receiver_cbs StreamDemuxer::VideoMedia::mReceiverCbs = { + .send_ctrl = &StreamDemuxer::VideoMedia::sendCtrlCb, + .codec_info_changed = &StreamDemuxer::VideoMedia::codecInfoChangedCb, + .recv_frame = &StreamDemuxer::VideoMedia::recvFrameCb, + .recv_rtp_pkt = nullptr, + .session_metadata_peer_changed = + &StreamDemuxer::VideoMedia::sessionMetadataPeerChangedCb, + .event = &StreamDemuxer::VideoMedia::eventCb, + .goodbye = &StreamDemuxer::VideoMedia::goodbyeCb, +}; + + +const struct h264_ctx_cbs StreamDemuxer::VideoMedia::mH264Cbs = { + .au_end = nullptr, + .nalu_begin = nullptr, + .nalu_end = nullptr, + .slice = nullptr, + .slice_data_begin = nullptr, + .slice_data_end = nullptr, + .slice_data_mb = nullptr, + .sps = nullptr, + .pps = nullptr, + .aud = nullptr, + .sei = nullptr, + .sei_buffering_period = nullptr, + .sei_pic_timing = &StreamDemuxer::VideoMedia::h264PicTimingSeiCb, + .sei_pan_scan_rect = nullptr, + .sei_filler_payload = nullptr, + .sei_user_data_registered = nullptr, + .sei_user_data_unregistered = + &StreamDemuxer::VideoMedia::h264UserDataSeiCb, + .sei_recovery_point = + &StreamDemuxer::VideoMedia::h264RecoveryPointSeiCb, +}; + + +StreamDemuxer::StreamDemuxer(Session *session, + Element::Listener *elementListener, + CodedSource::Listener *sourceListener, + IPdraw::IDemuxer *demuxer, + IPdraw::IDemuxer::Listener *demuxerListener) : + Demuxer(session, + elementListener, + sourceListener, + demuxer, + demuxerListener), + mContentBase(nullptr), mSessionProtocol(NONE), + mSetupRequestsCount(0), mSessionMetaFromSdp({}), + mChannelsReadyForStop(false), mNetworkReadyForStop(false), + mRtspState(DISCONNECTED), mRtspClient(nullptr), + mRtspSessionId(nullptr), mRunning(false), mFlushing(false), + mDestroyMediasAfterFlush(false), mFlushChannelCount(0), + mStartTime(0), mDuration(0), mTrackDuration(0), + mUpdateTrackDuration(false), mCurrentTime(0), mPausePoint(0), + mNtpToNptOffset(0), mRtpClockRate(0), mSpeed(1.f), + mFrameByFrame(true), mEndOfRangeNotified(false), mSeeking(false) +{ + Element::setClassName(__func__); +} + + +StreamDemuxer::~StreamDemuxer(void) +{ + int ret; + + if (mState != STOPPED && mState != CREATED) + PDRAW_LOGW("demuxer is still running"); + + destroyAllVideoMedias(); + + if (mRtspClient != nullptr) { + ret = rtsp_client_destroy(mRtspClient); + if (ret < 0) + PDRAW_LOG_ERRNO("rtsp_client_destroy", -ret); + } + + ret = pomp_loop_idle_remove_by_cookie(mSession->getLoop(), this); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_loop_idle_remove_by_cookie", -ret); + + free((void *)mContentBase); + free((void *)mRtspSessionId); +} + + +void StreamDemuxer::sessionMetadataFromSdp(const struct sdp_session *session, + struct vmeta_session *meta) +{ + int err; + struct sdp_attr *attr = nullptr; + + memset(meta, 0, sizeof(*meta)); + + if (session->session_name != nullptr) { + err = vmeta_session_streaming_sdp_read( + VMETA_STRM_SDP_TYPE_SESSION_NAME, + session->session_name, + nullptr, + meta); + if (err < 0) + ULOG_ERRNO("vmeta_session_streaming_sdp_read", -err); + } + if (session->session_info != nullptr) { + err = vmeta_session_streaming_sdp_read( + VMETA_STRM_SDP_TYPE_SESSION_INFO, + session->session_info, + nullptr, + meta); + if (err < 0) + ULOG_ERRNO("vmeta_session_streaming_sdp_read", -err); + } + if (session->tool != nullptr) { + err = vmeta_session_streaming_sdp_read( + VMETA_STRM_SDP_TYPE_SESSION_TOOL, + session->tool, + nullptr, + meta); + if (err < 0) + ULOG_ERRNO("vmeta_session_streaming_sdp_read", -err); + } + list_walk_entry_forward(&session->attrs, attr, node) + { + err = vmeta_session_streaming_sdp_read( + VMETA_STRM_SDP_TYPE_SESSION_ATTR, + attr->value, + attr->key, + meta); + if (err < 0) + ULOG_ERRNO("vmeta_session_streaming_sdp_read", -err); + } +} + + +int StreamDemuxer::processSetupRequests(void) +{ + int res; + + if (mSetupRequests.empty()) { + if (mSetupRequestsCount > 0) { + /* Returning -EBUSY here to notify that there are still + * setup requests in progress (for example asynchronous + * requests from the StreamDemuxerMux subclass that are + * not yet in the queue) */ + return -EBUSY; + } else { + /* All setup requests have benn processed; nothing + * more to do */ + return 0; + } + } + + /* Process the next setup request */ + SetupRequest req = mSetupRequests.front(); + + res = rtsp_client_setup(mRtspClient, + mContentBase, + req.controlUrl, + mRtspSessionId, + RTSP_DELIVERY_UNICAST, + req.lowerTransport, + req.localStreamPort, + req.localControlPort, + req.headerExt, + req.headerExtCount, + req.media, + RTSP_CLIENT_DEFAULT_RESP_TIMEOUT_MS); + if (res == -EBUSY) { + /* Another setup request is already in progress, the current + * setup request will be processed later (i.e. chained in the + * setup response callback function) */ + return 0; + } else if (res < 0) { + PDRAW_LOG_ERRNO("rtsp_client_setup", -res); + } else { + /* Success: returning -EBUSY here to notify that there are + * still setup requests in progress */ + res = -EBUSY; + } + + mSetupRequests.pop(); + mSetupRequestsCount--; + free(req.controlUrl); + return res; +} + + +void StreamDemuxer::destroyAllVideoMedias(void) +{ + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + delete *p; + p++; + } + mVideoMedias.clear(); +} + + +void StreamDemuxer::onRtspSocketCreated(int fd, void *userdata) +{ + StreamDemuxer *self = reinterpret_cast(userdata); + + self->mSession->socketCreated(fd); +} + + +void StreamDemuxer::onRtspConnectionState(struct rtsp_client *client, + enum rtsp_client_conn_state state, + void *userdata) +{ + StreamDemuxer *self = reinterpret_cast(userdata); + int res = 0; + + PDRAW_LOGI("RTSP client %s", rtsp_client_conn_state_str(state)); + + switch (state) { + case RTSP_CLIENT_CONN_STATE_DISCONNECTED: + self->mRtspState = DISCONNECTED; + PDRAW_LOGD("RTSP state change to %s", + getRtspStateStr(self->mRtspState)); + /* TODO? pomp_timer_clear(self->mFrameTimer); */ + self->mRunning = false; + self->mNetworkReadyForStop = true; + + /* If channels are also ready, set the state to stopped */ + if (self->mChannelsReadyForStop) { + self->mChannelsReadyForStop = false; + self->mNetworkReadyForStop = false; + self->closeResponse(0); + self->setStateAsyncNotify(STOPPED); + } + break; + case RTSP_CLIENT_CONN_STATE_CONNECTED: + /* If previous RTSP state is not DISCONNECTED, do not + * send OPTIONS request */ + if (self->mRtspState != DISCONNECTED) + break; + self->mRtspState = CONNECTED; + PDRAW_LOGD("RTSP state change to %s", + getRtspStateStr(self->mRtspState)); + + res = rtsp_client_options(self->mRtspClient, + nullptr, + 0, + nullptr, + RTSP_CLIENT_DEFAULT_RESP_TIMEOUT_MS); + if (res < 0) { + PDRAW_LOG_ERRNO("rtsp_client_options", -res); + } + break; + case RTSP_CLIENT_CONN_STATE_CONNECTING: + case RTSP_CLIENT_CONN_STATE_DISCONNECTING: + break; + default: + PDRAW_LOGW("unhandled RTSP connection state: (%d: %s)", + state, + rtsp_client_conn_state_str(state)); + break; + } +} + + +void StreamDemuxer::onRtspSessionRemoved(struct rtsp_client *client, + const char *session_id, + int status, + void *userdata) +{ + StreamDemuxer *self = reinterpret_cast(userdata); + + if (xstrcmp(session_id, self->mRtspSessionId) != 0) { + PDRAW_LOGD("wrong session removed (%s, expected %s)", + session_id, + self->mRtspSessionId); + return; + } else { + free((void *)self->mRtspSessionId); + self->mRtspSessionId = nullptr; + } + + ULOG_EVT("STREAM", + "event='client_session_removed';element='%s';" + "status=%d;status_str='%s';session='%s';res='%s'", + self->getCName(), + status, + strerror(-status), + session_id ? session_id : "", + self->mRtspPath.c_str()); + + auto p = self->mVideoMedias.begin(); + while (p != self->mVideoMedias.end()) { + (*p)->sendDownstreamEvent(CodedChannel::DownstreamEvent::EOS); + p++; + } + + + if (self->mState == STOPPING) { + /* If we are stopping, continue with rtsp disconnection */ + self->mRtspState = OPTIONS_DONE; + PDRAW_LOGD("RTSP state change to %s", + getRtspStateStr(self->mRtspState)); + + int res = pomp_loop_idle_add_with_cookie( + self->mSession->getLoop(), + &idleRtspDisconnect, + self, + self); + if (res < 0) + PDRAW_LOG_ERRNO("pomp_loop_idle_add_with_cookie", -res); + } else { + /* We used to call stop() here; this is no longer the case + * because now a demuxer must not stop itself; we call + * unrecoverableError instead, and it is the application's + * responsibility to stop and destroy the demuxer and create a + * new one. */ + self->onUnrecoverableError(); + } +} + + +void StreamDemuxer::onRtspOptionsResp(struct rtsp_client *client, + enum rtsp_client_req_status req_status, + int status, + uint32_t methods, + const struct rtsp_header_ext *ext, + size_t ext_count, + void *userdata, + void *req_userdata) +{ + StreamDemuxer *self = reinterpret_cast(userdata); + int res = 0; + + if (req_status != RTSP_CLIENT_REQ_STATUS_OK) { + switch (req_status) { + case RTSP_CLIENT_REQ_STATUS_FAILED: + PDRAW_LOGE("RTSP options request failed (%d: %s)", + status, + strerror(-status)); + res = status; + break; + case RTSP_CLIENT_REQ_STATUS_ABORTED: + PDRAW_LOGE("RTSP options request aborted"); + res = -EPROTO; + break; + case RTSP_CLIENT_REQ_STATUS_CANCELED: + PDRAW_LOGE("RTSP options request canceled"); + res = -ECANCELED; + break; + case RTSP_CLIENT_REQ_STATUS_TIMEOUT: + PDRAW_LOGE("timeout on RTSP options request"); + res = -ETIMEDOUT; + break; + default: + /* This should not happen */ + PDRAW_LOGE("unexpected status on options request: %d", + req_status); + res = -EPROTO; + break; + } + + ULOG_EVT("STREAM", + "event='client_options_resp';element='%s';" + "status=%d;status_str='%s';res='%s'", + self->getCName(), + res, + strerror(-res), + self->mRtspPath.c_str()); + + self->onUnrecoverableError(res); + return; + } + + ULOG_EVT("STREAM", + "event='client_options_resp';element='%s';" + "status=%d;status_str='%s';res='%s'", + self->getCName(), + res, + strerror(-res), + self->mRtspPath.c_str()); + + self->mRtspState = OPTIONS_DONE; + PDRAW_LOGD("RTSP state change to %s", + getRtspStateStr(self->mRtspState)); + + res = rtsp_client_describe(self->mRtspClient, + self->mRtspPath.c_str(), + nullptr, + 0, + nullptr, + RTSP_CLIENT_DEFAULT_RESP_TIMEOUT_MS); + if (res < 0) + PDRAW_LOG_ERRNO("rtsp_client_describe", -res); +} + + +void StreamDemuxer::onRtspDescribeResp(struct rtsp_client *client, + enum rtsp_client_req_status req_status, + int status, + const char *content_base, + const struct rtsp_header_ext *ext, + size_t ext_count, + const char *sdp, + void *userdata, + void *req_userdata) +{ + StreamDemuxer *self = reinterpret_cast(userdata); + int res = 0; + + if (req_status != RTSP_CLIENT_REQ_STATUS_OK) { + switch (req_status) { + case RTSP_CLIENT_REQ_STATUS_FAILED: + PDRAW_LOGE("RTSP describe request failed (%d: %s)", + status, + strerror(-status)); + res = status; + break; + case RTSP_CLIENT_REQ_STATUS_ABORTED: + PDRAW_LOGE("RTSP describe request aborted"); + res = -EPROTO; + break; + case RTSP_CLIENT_REQ_STATUS_CANCELED: + PDRAW_LOGE("RTSP describe request canceled"); + res = -ECANCELED; + break; + case RTSP_CLIENT_REQ_STATUS_TIMEOUT: + PDRAW_LOGE("timeout on RTSP describe request"); + res = -ETIMEDOUT; + break; + default: + /* This should not happen */ + PDRAW_LOGE("unexpected status on describe request: %d", + req_status); + res = -EPROTO; + break; + } + + ULOG_EVT("STREAM", + "event='client_describe_resp';element='%s';" + "status=%d;status_str='%s';res='%s'", + self->getCName(), + res, + strerror(-res), + self->mRtspPath.c_str()); + + self->onUnrecoverableError(res); + return; + } + + ULOG_EVT("STREAM", + "event='client_describe_resp';element='%s';" + "status=%d;status_str='%s';res='%s'", + self->getCName(), + res, + strerror(-res), + self->mRtspPath.c_str()); + + self->onNewSdp(content_base, sdp); +} + + +void StreamDemuxer::onRtspSetupResp(struct rtsp_client *client, + const char *session_id, + enum rtsp_client_req_status req_status, + int status, + uint16_t server_stream_port, + uint16_t server_control_port, + int ssrc_valid, + uint32_t ssrc, + const struct rtsp_header_ext *ext, + size_t ext_count, + void *userdata, + void *req_userdata) +{ + StreamDemuxer *self = reinterpret_cast(userdata); + VideoMedia *media = (VideoMedia *)req_userdata; + int res = 0; + const char *proxy_session = nullptr; + + for (size_t i = 0; i < ext_count; i++) { + if (strcasecmp(ext[i].key, + RTSP_HEADER_EXT_PARROT_PROXY_SESSION) == 0) { + proxy_session = ext[i].value; + break; + } + } + + if (req_status != RTSP_CLIENT_REQ_STATUS_OK) { + switch (req_status) { + case RTSP_CLIENT_REQ_STATUS_FAILED: + PDRAW_LOGE("RTSP setup request failed (%d: %s)", + status, + strerror(-status)); + res = status; + break; + case RTSP_CLIENT_REQ_STATUS_ABORTED: + PDRAW_LOGE("RTSP setup request aborted"); + res = -EPROTO; + break; + case RTSP_CLIENT_REQ_STATUS_CANCELED: + PDRAW_LOGE("RTSP setup request canceled"); + res = -ECANCELED; + break; + case RTSP_CLIENT_REQ_STATUS_TIMEOUT: + PDRAW_LOGE("timeout on RTSP setup request"); + res = -ETIMEDOUT; + break; + default: + /* This should not happen */ + PDRAW_LOGE("unexpected status on setup request: %d", + req_status); + res = -EPROTO; + break; + } + + ULOG_EVT("STREAM", + "event='client_setup_resp';element='%s';" + "status=%d;status_str='%s';session='%s'%s%s%s;" + "res='%s';media='%s';src='%s:%" PRIu16 ",%" PRIu16 + "';dst='%s:%" PRIu16 ",%" PRIu16 "'", + self->getCName(), + res, + strerror(-res), + session_id ? session_id : "", + proxy_session ? ";proxy_session='" : "", + proxy_session ? proxy_session : "", + proxy_session ? "'" : "", + self->mRtspPath.c_str(), + media ? media->getControlUrl() : "", + self->mRemoteAddr.c_str(), + (uint16_t)0, + (uint16_t)0, + self->mLocalAddr.c_str(), + (uint16_t)0, + (uint16_t)0); + + self->onUnrecoverableError(res); + return; + } + + free((void *)self->mRtspSessionId); + self->mRtspSessionId = xstrdup(session_id); + + if (media != nullptr) { + media->setSsrc((ssrc_valid) ? ssrc : 0); + media->setRemoteStreamPort(server_stream_port); + media->setRemoteControlPort(server_control_port); + + res = media->startRtpAvp(); + if (res < 0) + PDRAW_LOG_ERRNO("startRtpAvp", -res); + } + + ULOG_EVT("STREAM", + "event='client_setup_resp';element='%s';" + "status=%d;status_str='%s';session='%s'%s%s%s;" + "res='%s';media='%s';src='%s:%" PRIu16 ",%" PRIu16 + "';dst='%s:%" PRIu16 ",%" PRIu16 "'", + self->getCName(), + res, + strerror(-res), + session_id ? session_id : "", + proxy_session ? ";proxy_session='" : "", + proxy_session ? proxy_session : "", + proxy_session ? "'" : "", + self->mRtspPath.c_str(), + media ? media->getControlUrl() : "", + self->mRemoteAddr.c_str(), + server_stream_port, + server_control_port, + self->mLocalAddr.c_str(), + media ? media->getLocalStreamPort() : (uint16_t)0, + media ? media->getLocalControlPort() : (uint16_t)0); + + if (res < 0) { + self->onUnrecoverableError(res); + return; + } + + res = self->processSetupRequests(); + if (res < 0) { + if (res != -EBUSY) + self->onUnrecoverableError(res); + return; + } + + self->mRtspState = SETUP_DONE; + PDRAW_LOGD("RTSP state change to %s", + getRtspStateStr(self->mRtspState)); + + /* If the setup is a success, we always need to be in STARTED state */ + self->setState(STARTED); + self->openResponse(0); + self->readyToPlay(true); +} + + +void StreamDemuxer::onRtspPlayResp(struct rtsp_client *client, + const char *session_id, + enum rtsp_client_req_status req_status, + int status, + const struct rtsp_range *range, + float scale, + int seq_valid, + uint16_t seq, + int rtptime_valid, + uint32_t rtptime, + const struct rtsp_header_ext *ext, + size_t ext_count, + void *userdata, + void *req_userdata) +{ + StreamDemuxer *self = reinterpret_cast(userdata); + uint64_t start = 0, stop = 0, ntptime = 0; + int res = 0; + const char *proxy_session = nullptr; + + for (size_t i = 0; i < ext_count; i++) { + if (strcasecmp(ext[i].key, + RTSP_HEADER_EXT_PARROT_PROXY_SESSION) == 0) { + proxy_session = ext[i].value; + break; + } + } + if (range != nullptr) { + if (range->start.format == RTSP_TIME_FORMAT_NPT) + rtsp_time_npt_to_us(&range->start.npt, &start); + if (range->stop.format == RTSP_TIME_FORMAT_NPT) { + rtsp_time_npt_to_us(&range->stop.npt, &stop); + if (self->mUpdateTrackDuration) { + /* RTSP server may update track duration, if + * it was requested to play the file to the end. + */ + self->mUpdateTrackDuration = false; + self->mTrackDuration = stop; + } + } + } + + if (req_status != RTSP_CLIENT_REQ_STATUS_OK) { + switch (req_status) { + case RTSP_CLIENT_REQ_STATUS_FAILED: + PDRAW_LOGE("RTSP play request failed (%d: %s)", + status, + strerror(-status)); + res = status; + break; + case RTSP_CLIENT_REQ_STATUS_ABORTED: + PDRAW_LOGE("RTSP play request aborted"); + res = -EPROTO; + break; + case RTSP_CLIENT_REQ_STATUS_CANCELED: + PDRAW_LOGE("RTSP play request canceled"); + res = -ECANCELED; + break; + case RTSP_CLIENT_REQ_STATUS_TIMEOUT: + /* TODO: strategy on RTSP play timeout to be + * defined. Retry RTSP play? */ + PDRAW_LOGE("timeout on RTSP play request"); + res = -ETIMEDOUT; + break; + default: + /* This should not happen */ + PDRAW_LOGE("unexpected status on play request: %d", + req_status); + res = -EPROTO; + break; + } + + ULOG_EVT("STREAM", + "event='client_play_resp';element='%s';" + "status=%d;status_str='%s';session='%s'%s%s%s;" + "res='%s';start_ts=%" PRIu64 ";stop_ts=%" PRIu64 + ";rtp_ts=%" PRIu32 ";seq=%" PRIu16, + self->getCName(), + res, + strerror(-res), + session_id ? session_id : "", + proxy_session ? ";proxy_session='" : "", + proxy_session ? proxy_session : "", + proxy_session ? "'" : "", + self->mRtspPath.c_str(), + start, + stop, + rtptime, + seq); + + if (self->mSeeking) + self->seekResponse( + status, self->mCurrentTime, self->mSpeed); + else + self->playResponse( + status, self->mCurrentTime, self->mSpeed); + self->mSeeking = false; + return; + } + + if (xstrcmp(session_id, self->mRtspSessionId) != 0) { + PDRAW_LOGE( + "RTSP play response for a wrong session" + " (%s instead of %s)", + session_id, + self->mRtspSessionId); + return; + } + + ULOG_EVT("STREAM", + "event='client_play_resp';element='%s';" + "status=%d;status_str='%s';session='%s'%s%s%s;" + "res='%s';start_ts=%" PRIu64 ";stop_ts=%" PRIu64 + ";rtp_ts=%" PRIu32 ";seq=%" PRIu16, + self->getCName(), + res, + strerror(-res), + session_id ? session_id : "", + proxy_session ? ";proxy_session='" : "", + proxy_session ? proxy_session : "", + proxy_session ? "'" : "", + self->mRtspPath.c_str(), + start, + stop, + rtptime, + seq); + + self->mSpeed = (scale != 0.) ? scale : 1.0; + if ((rtptime_valid) && (self->mRtpClockRate != 0)) { + ntptime = rtp_timestamp_to_us(rtptime, self->mRtpClockRate); + } + self->mNtpToNptOffset = + (int64_t)(ntptime * self->mSpeed) - (int64_t)start; + self->mPausePoint = stop; + if (self->mSeeking) + self->seekResponse(0, start, self->mSpeed); + else + self->playResponse(0, start, self->mSpeed); + self->mSeeking = false; + + if ((start == stop) && start && stop) { + /* The end of range is reached */ + int res = pomp_loop_idle_add_with_cookie( + self->mSession->getLoop(), + &idleEndOfRangeNotification, + self, + self); + if (res < 0) + PDRAW_LOG_ERRNO("pomp_loop_idle_add_with_cookie", -res); + self->mSeeking = false; + } +} + + +void StreamDemuxer::onRtspPauseResp(struct rtsp_client *client, + const char *session_id, + enum rtsp_client_req_status req_status, + int status, + const struct rtsp_range *range, + const struct rtsp_header_ext *ext, + size_t ext_count, + void *userdata, + void *req_userdata) +{ + StreamDemuxer *self = reinterpret_cast(userdata); + uint64_t start = 0; + int res = 0; + const char *proxy_session = nullptr; + + for (size_t i = 0; i < ext_count; i++) { + if (strcasecmp(ext[i].key, + RTSP_HEADER_EXT_PARROT_PROXY_SESSION) == 0) { + proxy_session = ext[i].value; + break; + } + } + if ((range != nullptr) && (range->start.format == RTSP_TIME_FORMAT_NPT)) + rtsp_time_npt_to_us(&range->start.npt, &start); + + if (req_status != RTSP_CLIENT_REQ_STATUS_OK) { + switch (req_status) { + case RTSP_CLIENT_REQ_STATUS_FAILED: + PDRAW_LOGE("RTSP pause request failed (%d: %s)", + status, + strerror(-status)); + res = status; + break; + case RTSP_CLIENT_REQ_STATUS_ABORTED: + PDRAW_LOGE("RTSP pause request aborted"); + res = -EPROTO; + break; + case RTSP_CLIENT_REQ_STATUS_CANCELED: + PDRAW_LOGE("RTSP pause request canceled"); + res = -ECANCELED; + break; + case RTSP_CLIENT_REQ_STATUS_TIMEOUT: + /* TODO: strategy on RTSP pause timeout to be + * defined. Retry RTSP pause? */ + PDRAW_LOGE("timeout on RTSP pause request"); + res = -ETIMEDOUT; + break; + default: + /* This should not happen */ + PDRAW_LOGE("unexpected status on pause request: %d", + req_status); + res = -EPROTO; + break; + } + + ULOG_EVT("STREAM", + "event='client_pause_resp';element='%s';" + "status=%d;status_str='%s';session='%s'%s%s%s;" + "res='%s';ts=%" PRIu64, + self->getCName(), + res, + strerror(-res), + session_id ? session_id : "", + proxy_session ? ";proxy_session='" : "", + proxy_session ? proxy_session : "", + proxy_session ? "'" : "", + self->mRtspPath.c_str(), + start); + + self->pauseResponse(status, self->mCurrentTime); + return; + } + + if (xstrcmp(session_id, self->mRtspSessionId) != 0) { + PDRAW_LOGE( + "RTSP pause response for a wrong session" + " (%s instead of %s)", + session_id, + self->mRtspSessionId); + return; + } + + ULOG_EVT("STREAM", + "event='client_pause_resp';element='%s';" + "status=%d;status_str='%s';session='%s'%s%s%s;" + "res='%s';ts=%" PRIu64, + self->getCName(), + res, + strerror(-res), + session_id ? session_id : "", + proxy_session ? ";proxy_session='" : "", + proxy_session ? proxy_session : "", + proxy_session ? "'" : "", + self->mRtspPath.c_str(), + start); + + self->mPausePoint = start; + self->pauseResponse(0, self->mPausePoint); +} + + +void StreamDemuxer::onRtspTeardownResp(struct rtsp_client *client, + const char *session_id, + enum rtsp_client_req_status req_status, + int status, + const struct rtsp_header_ext *ext, + size_t ext_count, + void *userdata, + void *req_userdata) +{ + StreamDemuxer *self = reinterpret_cast(userdata); + int res = 0; + const char *proxy_session = nullptr; + + for (size_t i = 0; i < ext_count; i++) { + if (strcasecmp(ext[i].key, + RTSP_HEADER_EXT_PARROT_PROXY_SESSION) == 0) { + proxy_session = ext[i].value; + break; + } + } + + + switch (req_status) { + case RTSP_CLIENT_REQ_STATUS_OK: + break; + case RTSP_CLIENT_REQ_STATUS_ABORTED: + /* Teardown effective by disconnection */ + PDRAW_LOGW("RTSP teardown request aborted"); + res = -EPROTO; + break; + case RTSP_CLIENT_REQ_STATUS_FAILED: + /* Force disconnection anyway */ + PDRAW_LOGE("RTSP teardown request failed (%d: %s)", + status, + strerror(-status)); + res = status; + break; + case RTSP_CLIENT_REQ_STATUS_CANCELED: + /* No disconnection */ + PDRAW_LOGI("RTSP teardown request canceled"); + res = -ECANCELED; + break; + case RTSP_CLIENT_REQ_STATUS_TIMEOUT: + /* Force disconnection anyway */ + PDRAW_LOGE("timeout on RTSP teardown request"); + res = -ETIMEDOUT; + break; + default: + /* This should not happen */ + PDRAW_LOGE("unexpected status on teardown request: %d", + req_status); + res = -EPROTO; + break; + } + + if (xstrcmp(session_id, self->mRtspSessionId) != 0) { + PDRAW_LOGE( + "RTSP teardown response for a wrong session" + " (%s instead of %s)", + session_id, + self->mRtspSessionId); + return; + } + + ULOG_EVT("STREAM", + "event='client_teardown_resp';element='%s';" + "status=%d;status_str='%s';session='%s'%s%s%s;" + "res='%s'", + self->getCName(), + res, + strerror(-res), + session_id ? session_id : "", + proxy_session ? ";proxy_session='" : "", + proxy_session ? proxy_session : "", + proxy_session ? "'" : "", + self->mRtspPath.c_str()); +} + + +void StreamDemuxer::onRtspAnnounce(struct rtsp_client *client, + const char *content_base, + const struct rtsp_header_ext *ext, + size_t ext_count, + const char *sdp, + void *userdata) +{ + StreamDemuxer *self = reinterpret_cast(userdata); + + if (!self->mContentBase || + strcmp(self->mContentBase, content_base) != 0) + return; + + const char *resource = nullptr; + if (strlen(content_base) > 7) { + const char *p = strchr(content_base + 7, '/'); + if (p) + resource = p + 1; + } + ULOG_EVT("STREAM", + "event='client_announce';element='%s';res='%s'", + self->getCName(), + resource ? resource : ""); + + self->onNewSdp(content_base, sdp); +} + + +void StreamDemuxer::idleRtspDisconnect(void *userdata) +{ + StreamDemuxer *self = reinterpret_cast(userdata); + int res; + + res = rtsp_client_disconnect(self->mRtspClient); + if (res < 0) { + PDRAW_LOG_ERRNO("rtsp_client_disconnect", -res); + return; + } +} + + +int StreamDemuxer::startRtsp(const std::string &url) +{ + int res; + std::string::size_type n; + + if (mRtspClient != nullptr) { + res = -EBUSY; + PDRAW_LOG_ERRNO("mRtspClient", -res); + return res; + } + + /* Check that we actually have something after 'rtsp://' */ + if (url.length() < 8) + return -EINVAL; + + /* Find first '/' in url */ + n = url.find("/", 7); + if (n == std::string::npos) + return -EINVAL; + mServerAddr = url.substr(7, n - 7); + mRtspAddr = url.substr(0, n); + mRtspPath = url.substr(n + 1); + + mSessionProtocol = RTSP; + + std::string userAgent; + mSession->getSettings()->getSoftwareVersion(&userAgent); + + /* Create the RTSP client */ + res = rtsp_client_new(mSession->getLoop(), + userAgent.c_str(), + &mRtspClientCbs, + this, + &mRtspClient); + if (res < 0) { + PDRAW_LOG_ERRNO("rtsp_client_new", -res); + return res; + } + + res = rtsp_client_connect(mRtspClient, mRtspAddr.c_str()); + if (res < 0) { + PDRAW_LOG_ERRNO("rtsp_client_connect", -res); + return res; + } + + return 0; +} + + +int StreamDemuxer::start(void) +{ + int res; + + if ((mState == STARTED) || (mState == STARTING)) { + return 0; + } + if (mState != CREATED) { + PDRAW_LOGE("demuxer is not created"); + return -EPROTO; + } + setState(STARTING); + + if (mUrl.length() > 0) { + std::string ext = mUrl.substr(mUrl.length() - 4, 4); + std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); + + if (mUrl.substr(0, 7) == "rtsp://") { + res = startRtsp(mUrl); + if (res < 0) { + PDRAW_LOG_ERRNO("startRtsp", -res); + return res; + } + } else { + PDRAW_LOGE("unsupported URL"); + return -ENOSYS; + } + + setState(STARTED); + } else { + /* TODO create VideoMedia and setup */ + if (mVideoMedias.size() >= 1) { + res = mVideoMedias.front()->startRtpAvp(); + if (res < 0) { + PDRAW_LOG_ERRNO("startRtpAvp", -res); + return res; + } + } + + setState(STARTED); + openResponse(0); + readyToPlay(true); + } + + return 0; +} + + +int StreamDemuxer::stop(void) +{ + int ret = 0; + + if ((mState == STOPPED) || (mState == STOPPING)) + return 0; + if (mState != STARTED && mState != STARTING) { + PDRAW_LOGE("demuxer is not started"); + return -EPROTO; + } + setState(STOPPING); + readyToPlay(false); + + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + (*p)->sendDownstreamEvent(CodedChannel::DownstreamEvent::EOS); + p++; + } + + mChannelsReadyForStop = false; + mNetworkReadyForStop = false; + if (mSessionProtocol == RTSP) { + if (mRtspState == SETUP_DONE) { + ret = rtsp_client_teardown( + mRtspClient, + mRtspSessionId, + nullptr, + 0, + nullptr, + RTSP_CLIENT_DEFAULT_RESP_TIMEOUT_MS); + if (ret < 0) { + PDRAW_LOG_ERRNO("rtsp_client_teardown", -ret); + /* disconnect directly the rstp */ + ret = rtsp_client_disconnect(mRtspClient); + if (ret < 0) { + PDRAW_LOG_ERRNO( + "rtsp_client_disconnect", -ret); + return ret; + } + } + } else { + ret = rtsp_client_disconnect(mRtspClient); + if (ret < 0) { + PDRAW_LOG_ERRNO("rtsp_client_disconnect", -ret); + return ret; + } + } + } else { + mRunning = false; + mNetworkReadyForStop = true; + if (mVideoMedias.size() >= 1) + mVideoMedias.front()->stopRtpAvp(); + } + + CodedSource::lock(); + + ret = flush(); + if (ret < 0) + PDRAW_LOG_ERRNO("flush", -ret); + + CodedSource::unlock(); + + if (mNetworkReadyForStop && mChannelsReadyForStop) { + mChannelsReadyForStop = false; + mNetworkReadyForStop = false; + closeResponse(0); + setStateAsyncNotify(STOPPED); + } + + return ret; +} + + +int StreamDemuxer::flush(void) +{ + return flush(false); +} + + +int StreamDemuxer::flush(bool destroyMedias) +{ + if ((mState != STARTED) && (mState != STOPPING)) { + PDRAW_LOGE("demuxer is not started"); + return -EPROTO; + } + + CodedSource::lock(); + + mDestroyMediasAfterFlush = destroyMedias; + + if (mFlushing) { + CodedSource::unlock(); + return -EALREADY; + } + + mFlushChannelCount = 0; + + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + (*p)->flush(); + p++; + } + + unsigned int outputMediaCount = getOutputMediaCount(); + for (unsigned int i = 0; i < outputMediaCount; i++) { + CodedVideoMedia *media = getOutputMedia(i); + if (media == nullptr) { + PDRAW_LOGW("failed to get media at index %d", i); + continue; + } + + mFlushChannelCount += getOutputChannelCount(media); + } + + if (mFlushChannelCount == 0) { + mChannelsReadyForStop = true; + mFlushing = false; + mDestroyMediasAfterFlush = false; + } + + CodedSource::unlock(); + + return 0; +} + + +void StreamDemuxer::onChannelFlushed(CodedChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + CodedSource::lock(); + + CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); + if (media == nullptr) { + PDRAW_LOGE("media not found"); + CodedSource::unlock(); + return; + } + PDRAW_LOGD("'%s': channel flushed media name=%s (channel key=%p)", + Element::getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + if ((*p)->hasMedia(media)) { + (*p)->channelFlushed(channel); + break; + } + p++; + } + + if (mState == STOPPING || mDestroyMediasAfterFlush) { + int ret = channel->teardown(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->teardown", -ret); + } + + if (--mFlushChannelCount == 0) { + mFlushing = false; + mDestroyMediasAfterFlush = false; + } + + CodedSource::unlock(); +} + + +void StreamDemuxer::onChannelUnlink(CodedChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); + if (media == nullptr) { + PDRAW_LOGE("media not found"); + return; + } + + int ret = removeOutputChannel(media, channel->getKey()); + if (ret < 0) + PDRAW_LOG_ERRNO("removeOutputChannel", -ret); + + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + if ((*p)->hasMedia(media)) { + (*p)->channelUnlink(channel); + break; + } + p++; + } + + ret = pomp_loop_idle_add_with_cookie( + this->mSession->getLoop(), completeTeardownAsync, this, this); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_loop_idle_add_with_cookie", -ret); +} + + +void StreamDemuxer::completeTeardownAsync(void *userdata) +{ + StreamDemuxer *self = (StreamDemuxer *)userdata; + self->completeTeardown(); +} + + +void StreamDemuxer::completeTeardown(void) +{ + CodedSource::lock(); + + unsigned int outputMediaCount = getOutputMediaCount(); + for (unsigned int i = 0; i < outputMediaCount; i++) { + CodedVideoMedia *media = getOutputMedia(i); + if (media && getOutputChannelCount(media) > 0) { + CodedSource::unlock(); + return; + } + } + + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + delete *p; + p++; + } + mVideoMedias.clear(); + + CodedSource::unlock(); + + if (mState == STOPPING) { + mChannelsReadyForStop = true; + + /* If network is also ready, set the state to stopped */ + if (mNetworkReadyForStop && mChannelsReadyForStop) { + mChannelsReadyForStop = false; + mNetworkReadyForStop = false; + closeResponse(0); + setStateAsyncNotify(STOPPED); + } +#if 0 /* TODO: this should be re-enabled on a per-media basis */ + } else if (mCodecInfoChanging) { + PDRAW_LOGI("new output media"); + mCodecInfoChanging = false; + ret = setupInputMedia(); + if (ret < 0) { + PDRAW_LOG_ERRNO("setupInputMedia", -ret); + return; + } +#endif + } +} + + +void StreamDemuxer::onChannelResync(CodedChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + CodedSource::lock(); + + CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); + if (media == nullptr) { + PDRAW_LOGE("media not found"); + CodedSource::unlock(); + return; + } + PDRAW_LOGD("'%s': channel resync media name=%s (channel key=%p)", + Element::getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + if ((*p)->hasMedia(media)) { + (*p)->stop(); + CodedSource::unlock(); + return; + } + p++; + } + + CodedSource::unlock(); +} + + +void StreamDemuxer::onChannelVideoPresStats(CodedChannel *channel, + VideoPresStats *stats) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + if (stats == nullptr) { + PDRAW_LOG_ERRNO("stats", EINVAL); + return; + } + + CodedSource::lock(); + + CodedSource::onChannelVideoPresStats(channel, stats); + + CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); + if (media == nullptr) { + PDRAW_LOGE("media not found"); + CodedSource::unlock(); + return; + } + + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + if ((*p)->hasMedia(media)) { + (*p)->channelSendVideoPresStats(channel, stats); + break; + } + p++; + } + + CodedSource::unlock(); +} + + +void StreamDemuxer::onNewSdp(const char *content_base, const char *sdp) +{ + int res = 0; + struct sdp_session *session = nullptr; + char *remoteAddr = nullptr; + struct pdraw_demuxer_media *medias = nullptr; + std::vector selectedMedias; + std::vector::iterator m; + size_t mediasCount = 0, i; + bool found, noError = false; + + if (mState == STOPPING) { + PDRAW_LOGI("new SDP while stopping, ignore it"); + return; + } + + res = sdp_description_read(sdp, &session); + if (res < 0) { + PDRAW_LOG_ERRNO("sdp_description_read", -res); + return; + } + if (session->deletion) + PDRAW_LOGW("sdp refers to a no longer existing session"); + + if ((!mContentBase) && (content_base)) + mContentBase = strdup(content_base); + + /* Session-level metadata */ + sessionMetadataFromSdp(session, &mSessionMetaFromSdp); + + struct sdp_media *media = nullptr; + + list_walk_entry_forward(&session->medias, media, node) + { + if (media->type != SDP_MEDIA_TYPE_VIDEO || !media->control_url) + continue; + mediasCount++; + } + i = 0; + medias = (struct pdraw_demuxer_media *)calloc(mediasCount, + sizeof(*medias)); + if (medias == nullptr) + goto stop; + list_walk_entry_forward(&session->medias, media, node) + { + if (media->type != SDP_MEDIA_TYPE_VIDEO || !media->control_url) + continue; + +#if 0 + /* TODO: this is wrong with multistream: the codec info + * and clock rate will be from the last media in the + * list instead of the chosen media */ + if ((media->h264_fmtp.valid) && + (media->h264_fmtp.sps != nullptr) && + (media->h264_fmtp.pps != nullptr)) { + memset(&mCodecInfo, 0, sizeof(mCodecInfo)); + mCodecInfo.codec = VSTRM_CODEC_VIDEO_H264; + if (media->h264_fmtp.sps_size <= + sizeof(mCodecInfo.h264.sps)) { + memcpy(mCodecInfo.h264.sps, + media->h264_fmtp.sps, + media->h264_fmtp.sps_size); + mCodecInfo.h264.spslen = + media->h264_fmtp.sps_size; + } + if (media->h264_fmtp.pps_size <= + sizeof(mCodecInfo.h264.pps)) { + memcpy(mCodecInfo.h264.pps, + media->h264_fmtp.pps, + media->h264_fmtp.pps_size); + mCodecInfo.h264.ppslen = + media->h264_fmtp.pps_size; + } + } +#endif + mRtpClockRate = media->clock_rate; + + struct pdraw_demuxer_media *current = &medias[i]; + current->media_id = i + 1; + current->idx = i; + current->name = xstrdup(media->media_title); + if (current->name == nullptr) + current->name = xstrdup(media->control_url); + current->uri = xstrdup(media->control_url); + StreamDemuxer::VideoMedia::sessionMetadataFromSdp( + media, &mSessionMetaFromSdp, ¤t->session_meta); + current->is_default = current->session_meta.default_media; + if (current->is_default) + selectedMedias.push_back(current); + i++; + } + + if (mediasCount == 1 && selectedMedias.empty()) { + medias[0].is_default = 1; + selectedMedias.push_back(&medias[0]); + } else if (mediasCount == 0) { + /* Empty SDP */ + PDRAW_LOGI("empty SDP, no stream"); + /* An empty SDP means that both the server & the URL are good, + * but there is currently no streams. In this case, we can set + * the demuxer as STARTED here, and wait for an ANNOUCE to get + * the media. Otherwise, wait for the setup response before + * setting the state. If we have any media, remove them */ + flush(true); + setState(STARTED); + openResponse(0); + noError = true; + goto stop; + } else if (selectedMedias.empty()) { + /* If no default media, check for a media with camera_type == + * VMETA_CAMERA_TYPE_FRONT, and flag it as the default media */ + for (i = 0; i < mediasCount; i++) { + struct pdraw_demuxer_media *current = &medias[i]; + if (current->session_meta.camera_type != + VMETA_CAMERA_TYPE_FRONT) + continue; + current->is_default = 1; + selectedMedias.push_back(current); + } + } + + res = selectMedia(medias, mediasCount); + if (res == 0 || res == -ENOSYS) { + if (selectedMedias.empty()) { + PDRAW_LOGE( + "application requested default media, " + "but no default media found"); + res = -ENOENT; + goto stop; + } + if (selectedMedias.size() == 1) { + PDRAW_LOGI("auto-selecting media %d (%s)", + selectedMedias.back()->media_id, + selectedMedias.back()->name); + } else { + PDRAW_LOGI("audo-selecting medias {"); + m = selectedMedias.begin(); + while (m != selectedMedias.end()) { + PDRAW_LOGI(" - %d (%s)", + (*m)->media_id, + (*m)->name); + m++; + } + PDRAW_LOGI("}"); + } + } else if (res == -ECANCELED) { + PDRAW_LOGI("application cancelled the media selection"); + setState(STARTED); + openResponse(0); + goto stop; + } else if (res < 0) { + PDRAW_LOGE("application failed to select a video media"); + /* Selecting a wrong video media is an error, stop the demuxer + * to either report an open response, or an unrecoverable error + * to the application */ + goto stop; + } else { + selectedMedias.clear(); + for (i = 0; i < mediasCount; i++) { + if (!(res & (1 << medias[i].media_id))) + continue; + selectedMedias.push_back(&medias[i]); + PDRAW_LOGI("application selected media %d (%s)", + selectedMedias.back()->media_id, + selectedMedias.back()->name); + } + if (selectedMedias.empty()) { + PDRAW_LOGE("the application requested no valid media"); + res = -ENOENT; + goto stop; + } + } + + if (session->connection_addr && + strcmp(session->connection_addr, "0.0.0.0") != 0) + remoteAddr = strdup(session->connection_addr); + else if (session->server_addr && + strcmp(session->server_addr, "0.0.0.0") != 0) + remoteAddr = strdup(session->server_addr); + else if (mServerAddr.length() > 0) + remoteAddr = strdup(mServerAddr.c_str()); + if (remoteAddr == nullptr) { + PDRAW_LOGE("failed to get server address"); + goto stop; + } + + if (session->range.start.format == SDP_TIME_FORMAT_NPT) { + mDuration = (uint64_t)session->range.stop.npt.sec * 1000000 + + (uint64_t)session->range.stop.npt.usec; + mTrackDuration = mDuration; + } + + mRtspState = DESCRIBE_DONE; + PDRAW_LOGD("RTSP state change to %s", getRtspStateStr(mRtspState)); + + mLocalAddr = "0.0.0.0"; + mRemoteAddr = std::string(remoteAddr); + + m = selectedMedias.begin(); + while (m != selectedMedias.end()) { + found = false; + media = nullptr; + int idx = 0; + list_walk_entry_forward(&session->medias, media, node) + { + if (idx == (*m)->idx) { + found = true; + break; + } + idx++; + } + if (!found) { + PDRAW_LOGE( + "failed to find the selected " + "media in the list"); + goto stop; + } + StreamDemuxer::VideoMedia *videoMedia = createVideoMedia(); + res = videoMedia->setup(media); + if (res < 0) { + PDRAW_LOG_ERRNO("VideoMedia::setup", -res); + delete videoMedia; + goto stop; + } + mVideoMedias.push_back(videoMedia); + m++; + } + + goto exit; + +stop: + if (!noError) + onUnrecoverableError(); + readyToPlay(false); + if (mRtspState == SETUP_DONE) { + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + (*p)->stopRtpAvp(); + p++; + } + } + mRtspState = OPTIONS_DONE; + PDRAW_LOGD("RTSP state change to %s", getRtspStateStr(mRtspState)); + +exit: + if (medias != nullptr) { + for (i = 0; i < mediasCount; i++) { + free((char *)medias[i].name); + free((char *)medias[i].uri); + } + } + free(medias); + sdp_session_destroy(session); + free(remoteAddr); +} + +int StreamDemuxer::internalPlay(float speed) +{ + mRunning = true; + mSpeed = speed; + mFrameByFrame = false; + + if ((mSessionProtocol == RTSP) && (mRtspState == SETUP_DONE)) { + float scale = mSpeed; + struct rtsp_range range; + memset(&range, 0, sizeof(range)); + range.start.format = RTSP_TIME_FORMAT_NPT; + range.start.npt.now = 1; + range.stop.format = RTSP_TIME_FORMAT_NPT; + range.stop.npt.infinity = 1; + mUpdateTrackDuration = true; + int ret = rtsp_client_play(mRtspClient, + mRtspSessionId, + &range, + scale, + nullptr, + 0, + nullptr, + RTSP_CLIENT_DEFAULT_RESP_TIMEOUT_MS); + if (ret < 0) { + PDRAW_LOG_ERRNO("rtsp_client_play", -ret); + return ret; + } + mEndOfRangeNotified = false; + } + + return 0; +} + + +int StreamDemuxer::internalPause(void) +{ + mRunning = false; + mFrameByFrame = true; + + if ((mSessionProtocol == RTSP) && (mRtspState == SETUP_DONE)) { + struct rtsp_range range; + memset(&range, 0, sizeof(range)); + int ret = + rtsp_client_pause(mRtspClient, + mRtspSessionId, + &range, + nullptr, + 0, + nullptr, + RTSP_CLIENT_DEFAULT_RESP_TIMEOUT_MS); + if (ret < 0) { + PDRAW_LOG_ERRNO("rtsp_client_pause", -ret); + return ret; + } + mEndOfRangeNotified = false; + } + + return 0; +} + + +int StreamDemuxer::play(float speed) +{ + if (mState != STARTED) { + PDRAW_LOGE("demuxer is not started"); + return -EPROTO; + } + + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + (*p)->play(); + p++; + } + + if (speed == 0.) + return internalPause(); + else + return internalPlay(speed); +} + + +bool StreamDemuxer::isReadyToPlay(void) +{ + if (mState != STARTED) { + PDRAW_LOG_ERRNO("demuxer is not started", EPROTO); + return false; + } + + return mReadyToPlay; +} + + +bool StreamDemuxer::isPaused(void) +{ + if (mState != STARTED) { + PDRAW_LOG_ERRNO("demuxer is not started", EPROTO); + return false; + } + + bool running = mRunning && !mFrameByFrame; + + return !running; +} + + +int StreamDemuxer::previous(void) +{ + if (mState != STARTED) { + PDRAW_LOGE("demuxer is not started"); + return -EPROTO; + } + + if (!mFrameByFrame) { + PDRAW_LOGE("demuxer is not paused"); + return -EPROTO; + } + + if (mSessionProtocol != RTSP) + return -ENOSYS; + + if (mRtspState != SETUP_DONE) + return -EAGAIN; + +#if 0 /* this code is disabled because previousframe is not working yet on the \ + * server \ + */ + int ret = 0; + float scale = mSpeed; + struct rtsp_range range; + memset(&range, 0, sizeof(range)); + range.start.format = RTSP_TIME_FORMAT_NPT; + range.start.npt.sec = mPausePoint / 1000000; + range.start.npt.usec = mPausePoint - range.start.npt.sec * 1000000; + /* TODO: SMPTE timestamps*/ + int32_t start_usec = (int32_t)range.start.npt.usec - 2 * 34000; + if (start_usec < 0) { + if (range.start.npt.sec > 0) { + range.start.npt.sec--; + range.start.npt.usec = start_usec + 1000000; + } else { + range.start.npt.sec = 0; + range.start.npt.usec = 0; + } + } else { + range.start.npt.usec = start_usec; + } + range.stop = range.start; + range.stop.npt.usec += 4000; + if (range.stop.npt.usec >= 1000000) { + range.stop.npt.sec++; + range.stop.npt.usec -= 1000000; + } + ret = rtsp_client_play(mRtspClient, + mRtspSessionId, + &range, + scale, + nullptr, + RTSP_CLIENT_DEFAULT_RESP_TIMEOUT_MS); + if (ret < 0) { + PDRAW_LOG_ERRNO("rtsp_client_play", -ret); + return ret; + } + mSeeking = true; + ret = pomp_timer_clear(mRangeTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); + mEndOfRangeNotified = false; + + return 0; +#else + return -ENOSYS; +#endif +} + + +int StreamDemuxer::next(void) +{ + if (mState != STARTED) { + PDRAW_LOGE("demuxer is not started"); + return -EPROTO; + } + + if (!mFrameByFrame) { + PDRAW_LOGE("demuxer is not paused"); + return -EPROTO; + } + + if (mSessionProtocol != RTSP) + return -ENOSYS; + + if (mRtspState != SETUP_DONE) + return -EAGAIN; + +#if 0 /* this code is disabled because nextframe is not working yet on \ + * the server \ + */ + float scale = mSpeed; + struct rtsp_range range; + memset(&range, 0, sizeof(range)); + range.start.format = RTSP_TIME_FORMAT_NPT; + range.start.npt.sec = mPausePoint / 1000000; + range.start.npt.usec = mPausePoint - range.start.npt.sec * 1000000; + range.stop = range.start; + /* TODO: SMPTE timestamps*/ + range.stop.npt.usec += 1000; + if (range.stop.npt.usec >= 1000000) { + range.stop.npt.sec++; + range.stop.npt.usec -= 1000000; + } + int ret = rtsp_client_play(mRtspClient, + mRtspSessionId, + &range, + scale, + nullptr, + RTSP_CLIENT_DEFAULT_RESP_TIMEOUT_MS); + if (ret < 0) { + PDRAW_LOG_ERRNO("rtsp_client_play", -ret); + return ret; + } + mSeeking = true; + ret = pomp_timer_clear(mRangeTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); + mEndOfRangeNotified = false; + + return 0; +#else + return -ENOSYS; +#endif +} + + +int StreamDemuxer::seek(int64_t delta, bool exact) +{ + if (mState != STARTED) { + PDRAW_LOGE("demuxer is not started"); + return -EPROTO; + } + + int64_t ts = (int64_t)mCurrentTime + delta; + if (ts < 0) + ts = 0; + if (ts > (int64_t)mTrackDuration) + ts = mTrackDuration; + + return seekTo(ts, exact); +} + + +int StreamDemuxer::seekTo(uint64_t timestamp, bool exact) +{ + if (mState != STARTED) { + PDRAW_LOGE("demuxer is not started"); + return -EPROTO; + } + + if (mSessionProtocol != RTSP) + return -ENOSYS; + + if (mRtspState != SETUP_DONE) + return -EAGAIN; + + float scale = mSpeed; + struct rtsp_range range; + memset(&range, 0, sizeof(range)); + range.start.format = RTSP_TIME_FORMAT_NPT; + range.start.npt.sec = timestamp / 1000000; + range.start.npt.usec = timestamp - range.start.npt.sec * 1000000; + if (mRunning) { + range.stop.format = RTSP_TIME_FORMAT_NPT; + range.stop.npt.infinity = 1; + } else { + /* Only play a single frame if the seek was requested + * while in pause */ + range.stop = range.start; + /* TODO: SMPTE timestamps*/ + range.stop.npt.usec += 1000; + if (range.stop.npt.usec >= 1000000) { + range.stop.npt.sec++; + range.stop.npt.usec -= 1000000; + } + } + mUpdateTrackDuration = range.stop.npt.infinity ? true : false; + int ret = rtsp_client_play(mRtspClient, + mRtspSessionId, + &range, + scale, + nullptr, + 0, + nullptr, + RTSP_CLIENT_DEFAULT_RESP_TIMEOUT_MS); + if (ret < 0) { + PDRAW_LOG_ERRNO("rtsp_client_play", -ret); + return ret; + } + mSeeking = true; + mEndOfRangeNotified = false; + auto p = mVideoMedias.begin(); + while (p != mVideoMedias.end()) { + (*p)->play(); + p++; + } + + return 0; +} + + +uint64_t StreamDemuxer::getDuration(void) +{ + return (mDuration != 0) ? mDuration : (uint64_t)-1; +} + + +uint64_t StreamDemuxer::getCurrentTime(void) +{ + if (mSessionProtocol == RTSP) + return mCurrentTime; + else + return (mStartTime != 0) ? mCurrentTime - mStartTime : 0; +} + + +const char *StreamDemuxer::getRtspStateStr(StreamDemuxer::RtspState val) +{ + switch (val) { + case StreamDemuxer::RtspState::DISCONNECTED: + return "DISCONNECTED"; + case StreamDemuxer::RtspState::CONNECTED: + return "CONNECTED"; + case StreamDemuxer::RtspState::OPTIONS_DONE: + return "OPTIONS_DONE"; + case StreamDemuxer::RtspState::DESCRIBE_DONE: + return "DESCRIBE_DONE"; + case StreamDemuxer::RtspState::SETUP_DONE: + return "SETUP_DONE"; + default: + return nullptr; + } +} + + +StreamDemuxer::VideoMedia::VideoMedia(StreamDemuxer *demuxer) : + mDemuxer(demuxer), mReceiver(nullptr), mLocalStreamPort(0), + mLocalControlPort(0), mRemoteStreamPort(0), + mRemoteControlPort(0), mVideoMedias(nullptr), mNbVideoMedias(0), + mSdpMedia(nullptr), mH264Reader(nullptr), mFrameTimer(nullptr), + mRangeTimer(nullptr), mSsrc(0), mFlushing(false), + mFlushChannelCount(0), mFirstFrame(true), + mLastFrameReceiveTime(0), mFrameIndex(0), mCodecInfo({}), + mWaitForCodecInfo(false), mCodecInfoChanging(false), + mWaitForSync(false), mRecoveryFrameCount(0), + mCurrentFrame(nullptr), mCurrentMem(nullptr), + mCurrentMemOffset(0), mCurrentFrameCaptureTs(0), + mSessionMetaFromSdp({}) +{ + std::string name = demuxer->getName() + "#VideoMedia"; + Loggable::setName(name); +} + + +StreamDemuxer::VideoMedia::~VideoMedia(void) +{ + int ret; + + teardownMedia(); + + sdp_media_destroy(mSdpMedia); + + /* Clear any pending frames (received while the previous media + * was being torn down */ + while (!mTempQueue.empty()) { + struct vstrm_frame *frame = mTempQueue.front(); + mTempQueue.pop(); + vstrm_frame_unref(frame); + } + + if (mCurrentFrame != nullptr) { + ret = mbuf_coded_video_frame_unref(mCurrentFrame); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_unref", -ret); + } + + if (mCurrentMem != nullptr) { + ret = mbuf_mem_unref(mCurrentMem); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_mem_unref", -ret); + } + + if (mFrameTimer != nullptr) { + ret = pomp_timer_clear(mFrameTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); + ret = pomp_timer_destroy(mFrameTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_destroy", -ret); + } + + if (mRangeTimer != nullptr) { + ret = pomp_timer_clear(mRangeTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); + ret = pomp_timer_destroy(mRangeTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_destroy", -ret); + } +} + + +bool StreamDemuxer::VideoMedia::hasMedia(CodedVideoMedia *media) +{ + for (unsigned int i = 0; i < mNbVideoMedias; i++) { + if (mVideoMedias[i] == media) + return true; + } + return false; +} + + +int StreamDemuxer::VideoMedia::setup(const struct sdp_media *media) +{ + int ret; + + std::string name = + mDemuxer->getName() + + ((media != nullptr) ? "#" + std::string(media->control_url) + : "#NULL"); + Loggable::setName(name); + + /* Create the frame timer */ + mFrameTimer = pomp_timer_new( + mDemuxer->mSession->getLoop(), &frameTimeoutCb, this); + if (mFrameTimer == nullptr) { + ret = -ENOMEM; + PDRAW_LOG_ERRNO("pomp_timer_new", -ret); + return ret; + } + + /* Create the end of range timer */ + mRangeTimer = pomp_timer_new( + mDemuxer->mSession->getLoop(), &rangeTimerCb, this); + if (mRangeTimer == nullptr) { + ret = -ENOMEM; + PDRAW_LOG_ERRNO("pomp_timer_new", -ret); + return ret; + } + + if (media != nullptr && mDemuxer->mSessionProtocol == RTSP) { + sdp_media_destroy(mSdpMedia); + mSdpMedia = sdp_media_new(); + if (mSdpMedia == nullptr) { + ret = -ENOMEM; + PDRAW_LOG_ERRNO("sdp_media_new", -ret); + return ret; + } + ret = sdp_media_copy(media, mSdpMedia); + if (ret != 0) { + PDRAW_LOG_ERRNO("sdp_media_copy", -ret); + return ret; + } + } + + mDemuxer->mSetupRequestsCount++; + ret = prepareSetup(); + if (ret == -EINPROGRESS) { + /* Nothing to do, subclass will call finishSetup when needed */ + return 0; + } else if (ret != 0) { + PDRAW_LOG_ERRNO("prepareSetup", -ret); + return ret; + } + finishSetup(); + return 0; +} + +void StreamDemuxer::VideoMedia::finishSetup(void) +{ + if (mSdpMedia != nullptr) { + /* Media-level metadata */ + sessionMetadataFromSdp(mSdpMedia, + &mDemuxer->mSessionMetaFromSdp, + &mSessionMetaFromSdp); + + SetupRequest req = { + .media = this, + .controlUrl = strdup(mSdpMedia->control_url), + .lowerTransport = getLowerTransport(), + .localStreamPort = getLocalStreamPort(), + .localControlPort = getLocalControlPort(), + .headerExt = getHeaderExt(), + .headerExtCount = getHeaderExtCount(), + }; + mDemuxer->mSetupRequests.push(req); + + (void)mDemuxer->processSetupRequests(); + } +} + + +int StreamDemuxer::VideoMedia::setupMedia(void) +{ + int ret; + CodedSource::OutputPort *basePort, *mediaPort; + + if (mDemuxer->mState != STARTED) { + PDRAW_LOGE("demuxer is not started"); + return -EPROTO; + } + /* Note: H.265 streaming is not supported */ + if (mCodecInfo.codec != VSTRM_CODEC_VIDEO_H264) { + PDRAW_LOGE("invalid codec info"); + return -EPROTO; + } + + mDemuxer->CodedSource::lock(); + + if (mNbVideoMedias > 0) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOGE("media already defined"); + return -EBUSY; + } + + mNbVideoMedias = 2; + mVideoMedias = (CodedVideoMedia **)calloc(mNbVideoMedias, + sizeof(*mVideoMedias)); + if (mVideoMedias == nullptr) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOGE("media allocation failed"); + return -ENOMEM; + } + for (unsigned int i = 0; i < mNbVideoMedias; i++) { + mVideoMedias[i] = new CodedVideoMedia(mDemuxer->mSession); + if (mVideoMedias[i] == nullptr) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOGE("media allocation failed"); + return -ENOMEM; + } + switch (i) { + case 0: + mVideoMedias[i]->format = vdef_h264_avcc; + break; + case 1: + mVideoMedias[i]->format = vdef_h264_byte_stream; + break; + default: + break; + } + ret = mDemuxer->addOutputPort(mVideoMedias[i]); + if (ret < 0) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOG_ERRNO("addOutputPort", -ret); + return ret; + } + std::string path = mDemuxer->Element::getName() + "$" + + mVideoMedias[i]->getName(); + mVideoMedias[i]->setPath(path); + ret = mVideoMedias[i]->setPs(nullptr, + 0, + mCodecInfo.h264.sps, + mCodecInfo.h264.spslen, + mCodecInfo.h264.pps, + mCodecInfo.h264.ppslen); + if (ret < 0) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOG_ERRNO("media->setPs", -ret); + return ret; + } + mVideoMedias[i]->sessionMeta = mSessionMetaFromSdp; + mVideoMedias[i]->playbackType = + PDRAW_PLAYBACK_TYPE_LIVE; /* TODO: live/replay */ + mVideoMedias[i]->duration = mDemuxer->mDuration; + } + + /* Create the H.264 reader + * Note: H.265 streaming is not supported */ + ret = h264_reader_new(&mH264Cbs, this, &mH264Reader); + if (ret < 0) { + PDRAW_LOG_ERRNO("h264_reader_new", -ret); + return ret; + } + + ret = h264_reader_parse_nalu( + mH264Reader, 0, mCodecInfo.h264.sps, mCodecInfo.h264.spslen); + if (ret < 0) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOG_ERRNO("h264_reader_parse_nalu:sps", -ret); + return ret; + } + + ret = h264_reader_parse_nalu( + mH264Reader, 0, mCodecInfo.h264.pps, mCodecInfo.h264.ppslen); + if (ret < 0) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOG_ERRNO("h264_reader_parse_nalu:pps", -ret); + return ret; + } + + /* Create the output buffers pool on the last media. As the medias will + * be destroyed in creation order, this ensures that the media which + * owns the buffers pool will be the last destroyed */ + ret = mDemuxer->createOutputPortMemoryPool( + mVideoMedias[1], + DEMUXER_OUTPUT_BUFFER_COUNT, + mVideoMedias[1]->info.resolution.width * + mVideoMedias[1]->info.resolution.height * 3 / 4); + if (ret < 0) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOG_ERRNO("createOutputPortMemoryPool", -ret); + return ret; + } + /* Make the pool shared between all medias */ + basePort = mDemuxer->getOutputPort(mVideoMedias[1]); + mediaPort = mDemuxer->getOutputPort(mVideoMedias[0]); + if (basePort == nullptr || mediaPort == nullptr) { + PDRAW_LOGW("unable to share memory pool between medias"); + } else { + mediaPort->pool = basePort->pool; + mediaPort->sharedPool = true; + } + + /* New synchronization is needed */ + mWaitForSync = true; + mRecoveryFrameCount = 0; + + mDemuxer->CodedSource::unlock(); + + if (mDemuxer->CodedSource::mListener) { + for (unsigned int i = 0; i < mNbVideoMedias; i++) + mDemuxer->CodedSource::mListener->onOutputMediaAdded( + mDemuxer, mVideoMedias[i]); + } + + /* Process any pending frames (received while the previous media + * was being torn down */ + while (!mTempQueue.empty()) { + struct vstrm_frame *frame = mTempQueue.front(); + mTempQueue.pop(); + ret = processFrame(frame); + if ((ret < 0) && (ret != -EAGAIN)) + PDRAW_LOG_ERRNO("processFrame", -ret); + vstrm_frame_unref(frame); + } + + return 0; +} + + +void StreamDemuxer::VideoMedia::teardownMedia(void) +{ + /* Destroy the H.264 reader */ + if (mH264Reader != nullptr) { + int ret = h264_reader_destroy(mH264Reader); + if (ret < 0) + PDRAW_LOG_ERRNO("h264_reader_destroy", -ret); + mH264Reader = nullptr; + } + + /* Remove the output ports */ + for (unsigned int i = 0; i < mNbVideoMedias; i++) { + if (mDemuxer->CodedSource::mListener) { + mDemuxer->CodedSource::mListener->onOutputMediaRemoved( + mDemuxer, mVideoMedias[i]); + } + int ret = mDemuxer->removeOutputPort(mVideoMedias[i]); + if (ret < 0) { + PDRAW_LOG_ERRNO("removeOutputPort", -ret); + } else { + delete mVideoMedias[i]; + } + } + free(mVideoMedias); + mVideoMedias = nullptr; + mNbVideoMedias = 0; +} + + +int StreamDemuxer::VideoMedia::createReceiver(void) +{ + struct vstrm_receiver_cfg *cfg; + std::string fn; + std::string sn; + std::string sv; + int ret; + + cfg = (struct vstrm_receiver_cfg *)calloc(1, sizeof(*cfg)); + if (cfg == nullptr) { + ret = -ENOMEM; + PDRAW_LOG_ERRNO("calloc", -ret); + goto error; + } + + /* Create the stream receiver */ + cfg->loop = mDemuxer->mSession->getLoop(); + cfg->flags = VSTRM_RECEIVER_FLAGS_H264_GEN_CONCEALMENT_SLICE | + VSTRM_RECEIVER_FLAGS_ENABLE_RTCP | + VSTRM_RECEIVER_FLAGS_ENABLE_RTCP_EXT; + mDemuxer->mSession->getSettings()->getFriendlyName(&fn); + strncpy(cfg->self_meta.friendly_name, + fn.c_str(), + sizeof(cfg->self_meta.friendly_name)); + cfg->self_meta.friendly_name[sizeof(cfg->self_meta.friendly_name) - 1] = + '\0'; + mDemuxer->mSession->getSettings()->getSerialNumber(&sn); + strncpy(cfg->self_meta.serial_number, + sn.c_str(), + sizeof(cfg->self_meta.serial_number)); + cfg->self_meta.serial_number[sizeof(cfg->self_meta.serial_number) - 1] = + '\0'; + mDemuxer->mSession->getSettings()->getSoftwareVersion(&sv); + strncpy(cfg->self_meta.software_version, + sv.c_str(), + sizeof(cfg->self_meta.software_version)); + cfg->self_meta + .software_version[sizeof(cfg->self_meta.software_version) - 1] = + '\0'; + ret = vstrm_receiver_new(cfg, &mReceiverCbs, this, &mReceiver); + if (ret < 0) { + mReceiver = nullptr; + PDRAW_LOG_ERRNO("vstrm_receiver_new", -ret); + goto error; + } + + /* Provide the SPS/PPS out of band if available */ + if (mCodecInfo.codec == VSTRM_CODEC_VIDEO_H264) { + ret = vstrm_receiver_set_codec_info( + mReceiver, &mCodecInfo, mSsrc); + if (ret < 0) + PDRAW_LOG_ERRNO("vstrm_receiver_set_codec_info", -ret); + } + + free(cfg); + return 0; + +error: + destroyReceiver(); + free(cfg); + return ret; +} + + +int StreamDemuxer::VideoMedia::destroyReceiver(void) +{ + int res; + if (mReceiver != nullptr) { + /* Destroy the receiver */ + res = vstrm_receiver_destroy(mReceiver); + if (res < 0) + PDRAW_LOG_ERRNO("vstrm_receiver_destroy", -res); + mReceiver = nullptr; + } + return 0; +} + + +void StreamDemuxer::VideoMedia::play(void) +{ + int ret; + ret = pomp_timer_clear(mFrameTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); + ret = pomp_timer_clear(mRangeTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); +} + + +void StreamDemuxer::VideoMedia::stop(void) +{ + pomp_timer_clear(mFrameTimer); + + if (mCurrentFrame != nullptr) { + int ret = mbuf_coded_video_frame_unref(mCurrentFrame); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_unref", -ret); + mCurrentFrame = nullptr; + } + + if (mCurrentMem != nullptr) { + int ret = mbuf_mem_unref(mCurrentMem); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_mem_unref", -ret); + mCurrentMem = nullptr; + } + + mWaitForSync = true; + mRecoveryFrameCount = 0; +} + + +void StreamDemuxer::VideoMedia::flush(void) +{ + mDemuxer->CodedSource::lock(); + + stop(); + mFlushing = true; + mFlushChannelCount = 0; + + for (unsigned int i = 0; i < mNbVideoMedias; i++) { + unsigned int outputChannelCount = + mDemuxer->getOutputChannelCount(mVideoMedias[i]); + mFlushChannelCount += outputChannelCount; + + /* Flush the output channels */ + for (unsigned int j = 0; j < outputChannelCount; j++) { + CodedChannel *channel = + mDemuxer->getOutputChannel(mVideoMedias[i], j); + if (channel == nullptr) { + PDRAW_LOGW("failed to get channel at index %d", + j); + continue; + } + int ret = channel->flush(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->flush", -ret); + } + } + + mDemuxer->CodedSource::unlock(); +} + + +void StreamDemuxer::VideoMedia::channelFlushed(CodedChannel *channel) +{ + mFlushChannelCount--; + if (mFlushChannelCount <= 0) + mFlushing = false; +} + + +void StreamDemuxer::VideoMedia::channelUnlink(CodedChannel *channel) +{ + mDemuxer->CodedSource::lock(); + + for (unsigned int i = 0; i < mNbVideoMedias; i++) { + unsigned int outputChannelCount = + mDemuxer->getOutputChannelCount(mVideoMedias[i]); + if (outputChannelCount > 0) { + mDemuxer->CodedSource::unlock(); + return; + } + } + + mDemuxer->CodedSource::unlock(); + + if (mCodecInfoChanging) { + teardownMedia(); + PDRAW_LOGI("new output media"); + mCodecInfoChanging = false; + int ret = setupMedia(); + if (ret < 0) { + PDRAW_LOG_ERRNO("setupMedia", -ret); + return; + } + } +} + + +void StreamDemuxer::VideoMedia::sendDownstreamEvent( + CodedChannel::DownstreamEvent event) +{ + for (unsigned int i = 0; i < mNbVideoMedias; i++) { + int res = mDemuxer->CodedSource::sendDownstreamEvent( + mVideoMedias[i], event); + if (res < 0) + PDRAW_LOG_ERRNO("CodedSource::sendDownstreamEvent", + -res); + } +} + + +int StreamDemuxer::VideoMedia::processFrame(struct vstrm_frame *frame) +{ + int ret = 0, err; + unsigned int outputChannelCount = 0; + CodedChannel *channel; + CodedVideoMedia::Frame data = {}; + uint32_t flags = 0; + size_t frameSize = 0, bufSize; + uint8_t *buf; + bool isIdr = false; + struct timespec ts = {0, 0}; + uint64_t curTime = 0; + uint64_t remainingPlayTime = 0; + unsigned int requiredMediaIndex; + CodedVideoMedia *requiredMedia; + struct vdef_coded_frame frameInfo; + struct mbuf_coded_video_frame *outputFrame = nullptr; + unsigned int sliceCount = 0; + int64_t clock_delta = 0; + uint32_t precision = UINT32_MAX; + + mDemuxer->CodedSource::lock(); + + /* Get an output memory */ + if (mCurrentFrame != nullptr) { + ret = mbuf_coded_video_frame_unref(mCurrentFrame); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_unref", -ret); + mCurrentFrame = nullptr; + } + if (mCurrentMem != nullptr) { + ret = mbuf_mem_unref(mCurrentMem); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_mem_unref", -ret); + mCurrentMem = nullptr; + } + ret = mDemuxer->getOutputMemory(mVideoMedias, + mNbVideoMedias, + &mCurrentMem, + &requiredMediaIndex); + if ((ret < 0) || (mCurrentMem == nullptr)) { + mDemuxer->CodedSource::unlock(); + PDRAW_LOGW("failed to get an output memory (%d)", ret); + flush(); + return ret; + } + requiredMedia = mVideoMedias[requiredMediaIndex]; + ret = mbuf_mem_get_data(mCurrentMem, (void **)&buf, &bufSize); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_mem_get_data", -ret); + goto out; + } + mCurrentFrameCaptureTs = 0; + mCurrentMemOffset = 0; + + /* Get the size of the frame */ + flags = (requiredMedia->format.data_format == + VDEF_CODED_DATA_FORMAT_BYTE_STREAM) + ? VSTRM_FRAME_COPY_FLAGS_INSERT_NALU_START_CODE + : VSTRM_FRAME_COPY_FLAGS_INSERT_NALU_SIZE; + flags |= VSTRM_FRAME_COPY_FLAGS_FILTER_SPS_PPS; + ret = vstrm_frame_get_size(frame, &frameSize, flags); + if (ret < 0) { + PDRAW_LOG_ERRNO("vstrm_frame_get_size", -ret); + goto out; + } + if (bufSize < frameSize) { + PDRAW_LOGW("input buffer too small (%zu vs. %zu", + bufSize, + frameSize); + ret = -EPROTO; + goto out; + } + + vdef_format_to_frame_info(&requiredMedia->info, &frameInfo.info); + frameInfo.info.timestamp = frame->timestamps.ntp_raw; + frameInfo.info.timescale = 1000000; + frameInfo.info.index = mFrameIndex++; + frameInfo.format = requiredMedia->format; + ret = mbuf_coded_video_frame_new(&frameInfo, &mCurrentFrame); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_new", -ret); + goto out; + } + + /* Copy the frame */ + /* TODO: avoid copy? */ + ret = vstrm_frame_copy(frame, buf, frameSize, flags); + if (ret < 0) { + PDRAW_LOG_ERRNO("vstrm_frame_copy", -ret); + goto out; + } + + frameInfo.type = VDEF_CODED_FRAME_TYPE_I; + for (uint32_t i = 0; i < frame->nalu_count; i++) { + enum h264_nalu_type naluType; + enum h264_slice_type sliceType = H264_SLICE_TYPE_UNKNOWN; + naluType = (enum h264_nalu_type)(*frame->nalus[i].cdata & 0x1F); + switch (naluType) { + /* Ignored NALUs */ + case H264_NALU_TYPE_SPS: + case H264_NALU_TYPE_PPS: + /* Ignore SPS/PPS */ + continue; + case H264_NALU_TYPE_SEI: + /* SEI NAL unit */ + ret = h264_reader_parse_nalu(mH264Reader, + 0, + frame->nalus[i].cdata, + frame->nalus[i].len); + if (ret < 0) + PDRAW_LOG_ERRNO("h264_reader_parse_nalu:sei", + -ret); + sliceType = H264_SLICE_TYPE_UNKNOWN; + break; + /* Slices */ + case H264_NALU_TYPE_SLICE_IDR: + /* IDR slice */ + isIdr = true; + frameInfo.type = VDEF_CODED_FRAME_TYPE_IDR; + /* fallthrough */ + case H264_NALU_TYPE_SLICE: + sliceType = /* TODO */ H264_SLICE_TYPE_UNKNOWN; + /* TODO (coverity complains that the code can + * never be reached) + * if (sliceType == H264_SLICE_TYPE_P) + * frameInfo.type = VDEF_CODED_FRAME_TYPE_P; */ + sliceCount++; + break; + /* Keep all other NALUs */ + default: + break; + } + struct vdef_nalu nalu = {}; + nalu.size = frame->nalus[i].len + 4; + nalu.h264.type = naluType; + nalu.h264.slice_type = sliceType; + /* TODO: h264.slice_mb_count */ + ret = mbuf_coded_video_frame_add_nalu( + mCurrentFrame, mCurrentMem, mCurrentMemOffset, &nalu); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_nalu", + -ret); + goto out; + } + mCurrentMemOffset += nalu.size; + } + + /* Ignore frames with 0 slices */ + if (sliceCount == 0) { + ret = 0; + PDRAW_LOGI("%s: empty frame, ignored", __func__); + goto out; + } + + /* If the frame is an IDR (excluding the generated gray IDR), + * sync is complete */ + if ((mWaitForSync) && (isIdr) && (!frame->info.gen_grey_idr)) { + mWaitForSync = false; + mRecoveryFrameCount = 0; + } + + /* If sync is in progress (intra refresh + recovery point received), + * decrement recoveryFrameCount for every ref frame until 0 */ + if ((!mWaitForSync) && (mRecoveryFrameCount > 0) && (frame->info.ref)) + mRecoveryFrameCount--; + data.isSync = isIdr; + data.isRef = frame->info.ref; + /* TODO: use unskewed timestamps */ + data.ntpTimestamp = frame->timestamps.ntp; + data.ntpUnskewedTimestamp = frame->timestamps.ntp_unskewed; + data.ntpRawTimestamp = frame->timestamps.ntp_raw; + data.ntpRawUnskewedTimestamp = frame->timestamps.ntp_raw_unskewed; + data.captureTimestamp = mCurrentFrameCaptureTs; + err = vstrm_receiver_get_clock_delta( + mReceiver, &clock_delta, &precision); + if (err < 0) { + if (err != -EAGAIN) + PDRAW_LOG_ERRNO("vstrm_receiver_get_clock_delta", -err); + } else { + data.localTimestamp = data.captureTimestamp - clock_delta; + data.localTimestampPrecision = precision; + } + data.recvStartTimestamp = frame->timestamps.recv_start; + data.recvEndTimestamp = frame->timestamps.recv_end; + + frameInfo.info.capture_timestamp = mCurrentFrameCaptureTs; + + if (frame->info.error || !frame->info.complete) + frameInfo.info.flags |= VDEF_FRAME_FLAG_VISUAL_ERROR; + if (mWaitForSync || mRecoveryFrameCount != 0) + frameInfo.info.flags |= VDEF_FRAME_FLAG_SILENT; + if (frame->info.uses_ltr) + frameInfo.info.flags |= VDEF_FRAME_FLAG_USES_LTR; + + /* Frame metadata */ + if (frame->metadata) { + ret = mbuf_coded_video_frame_set_metadata(mCurrentFrame, + frame->metadata); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_set_metadata", + -ret); + goto out; + } + } + + time_get_monotonic(&ts); + time_timespec_to_us(&ts, &curTime); + data.demuxOutputTimestamp = curTime; + mLastFrameReceiveTime = curTime; + + if (mDemuxer->mSessionProtocol == RTSP) { + mDemuxer->mCurrentTime = + frame->timestamps.ntp * mDemuxer->mSpeed - + mDemuxer->mNtpToNptOffset; + } else { + mDemuxer->mCurrentTime = frame->timestamps.ntp; + if (mDemuxer->mStartTime == 0) + mDemuxer->mStartTime = frame->timestamps.ntp; + } + data.playTimestamp = mDemuxer->mCurrentTime; + if (mDemuxer->mTrackDuration > 0) { + remainingPlayTime = + mDemuxer->mTrackDuration - mDemuxer->mCurrentTime; + if (remainingPlayTime < 1000000) { + /* Less than 1s of play time remaining */ + + /* Take the speed into account */ + remainingPlayTime = + (mDemuxer->mSpeed != 0.f) + ? remainingPlayTime / mDemuxer->mSpeed + : UINT64_MAX; + + if (remainingPlayTime < 1000000) { + /* Add 50ms margin */ + pomp_timer_set(mRangeTimer, + remainingPlayTime / 1000 + 50); + } + } + } + + /* update the frame info */ + ret = mbuf_coded_video_frame_set_frame_info(mCurrentFrame, &frameInfo); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_set_frame_info", -ret); + goto out; + } + + ret = mbuf_coded_video_frame_add_ancillary_buffer( + mCurrentFrame, + PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME, + &data, + sizeof(data)); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_ancillary_buffer", + -ret); + goto out; + } + + ret = mbuf_coded_video_frame_finalize(mCurrentFrame); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_finalize", -ret); + goto out; + } + + /* Queue the buffer in the output channels */ + for (unsigned int i = 0; i < mNbVideoMedias; i++) { + outputChannelCount = + mDemuxer->getOutputChannelCount(mVideoMedias[i]); + if (outputChannelCount == 0) + continue; + if (outputFrame != nullptr) + mbuf_coded_video_frame_unref(outputFrame); + outputFrame = mCurrentFrame; + if (!vdef_coded_format_cmp(&requiredMedia->format, + &mVideoMedias[i]->format)) { + /* The format is different, we need to pick another + * frame */ + int copy_ret = + mDemuxer->copyOutputFrame(requiredMedia, + mCurrentFrame, + mVideoMedias[i], + &outputFrame); + if (copy_ret < 0) { + PDRAW_LOG_ERRNO("copyOutputFrame", -copy_ret); + outputFrame = nullptr; + continue; + } + } else { + mbuf_coded_video_frame_ref(outputFrame); + } + ret = mbuf_coded_video_frame_get_frame_info(outputFrame, + &frameInfo); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_frame_info", + -ret); + goto out; + } + for (unsigned int j = 0; j < outputChannelCount; j++) { + const struct vdef_coded_format *caps; + int capsCount; + + channel = + mDemuxer->getOutputChannel(mVideoMedias[i], j); + if (channel == nullptr) { + PDRAW_LOGW("invalid channel"); + continue; + } + + capsCount = + channel->getCodedVideoMediaFormatCaps(&caps); + if (capsCount < 0) { + PDRAW_LOGW("invalid channel (no caps)"); + continue; + } + + if (!vdef_coded_format_intersect( + &frameInfo.format, caps, capsCount)) { + PDRAW_LOGW( + "incompatible coded video format " + "on channel"); + continue; + } + + int queue_ret = channel->queue(outputFrame); + if (queue_ret < 0) + PDRAW_LOG_ERRNO("channel->queue", -queue_ret); + } + } + if (outputFrame != nullptr) + mbuf_coded_video_frame_unref(outputFrame); + if ((mFirstFrame) && + (!(frameInfo.info.flags & VDEF_FRAME_FLAG_SILENT))) { + sendDownstreamEvent(CodedChannel::DownstreamEvent::SOS); + mFirstFrame = false; + } + +out: + mbuf_mem_unref(mCurrentMem); + mCurrentMem = nullptr; + mbuf_coded_video_frame_unref(mCurrentFrame); + mCurrentFrame = nullptr; + + mDemuxer->CodedSource::unlock(); + return ret; +} + + +void StreamDemuxer::VideoMedia::channelSendVideoPresStats(CodedChannel *channel, + VideoPresStats *stats) +{ + vstrm_video_stats vstrm_stats = {}; + + vstrm_stats.version = VSTRM_VIDEO_STATS_VERSION_2; + vstrm_stats.timestamp = stats->timestamp; + vstrm_stats.v2.presentation_frame_count = stats->presentationFrameCount; + vstrm_stats.v2.presentation_timestamp_delta_integral = + stats->presentationTimestampDeltaIntegral; + vstrm_stats.v2.presentation_timestamp_delta_integral_sq = + stats->presentationTimestampDeltaIntegralSq; + vstrm_stats.v2.presentation_timing_error_integral = + stats->presentationTimingErrorIntegral; + vstrm_stats.v2.presentation_timing_error_integral_sq = + stats->presentationTimingErrorIntegralSq; + vstrm_stats.v2.presentation_estimated_latency_integral = + stats->presentationEstimatedLatencyIntegral; + vstrm_stats.v2.presentation_estimated_latency_integral_sq = + stats->presentationEstimatedLatencyIntegralSq; + vstrm_stats.v2.player_latency_integral = stats->playerLatencyIntegral; + vstrm_stats.v2.player_latency_integral_sq = + stats->playerLatencyIntegralSq; + vstrm_stats.v2.estimated_latency_precision_integral = + stats->estimatedLatencyPrecisionIntegral; + + int err = vstrm_receiver_set_video_stats(mReceiver, &vstrm_stats); + if (err < 0) + PDRAW_LOG_ERRNO("vstrm_receiver_set_video_stats", -err); +} + + +void StreamDemuxer::VideoMedia::sessionMetadataFromSdp( + const struct sdp_media *media, + const struct vmeta_session *sessionMeta, + struct vmeta_session *meta) +{ + int err; + struct sdp_attr *attr = nullptr; + + *meta = *sessionMeta; + + if (media->media_title != nullptr) { + err = vmeta_session_streaming_sdp_read( + VMETA_STRM_SDP_TYPE_MEDIA_INFO, + media->media_title, + nullptr, + meta); + if (err < 0) + ULOG_ERRNO("vmeta_session_streaming_sdp_read", -err); + } + list_walk_entry_forward(&media->attrs, attr, node) + { + err = vmeta_session_streaming_sdp_read( + VMETA_STRM_SDP_TYPE_MEDIA_ATTR, + attr->value, + attr->key, + meta); + if (err < 0) + ULOG_ERRNO("vmeta_session_streaming_sdp_read", -err); + } +} + + +void StreamDemuxer::VideoMedia::h264UserDataSeiCb( + struct h264_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h264_sei_user_data_unregistered *sei, + void *userdata) +{ + VideoMedia *self = (VideoMedia *)userdata; + int ret = 0; + + if (self == nullptr) + return; + if ((buf == nullptr) || (len == 0)) + return; + if (sei == nullptr) + return; + if (self->mCurrentFrame == nullptr) + return; + + /* Ignore "Parrot Streaming" user data SEI */ + if (vstrm_h264_is_sei_streaming(sei->uuid)) + return; + + ret = mbuf_coded_video_frame_add_ancillary_buffer( + self->mCurrentFrame, MBUF_ANCILLARY_KEY_USERDATA_SEI, buf, len); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_ancillary_buffer", + -ret); + return; + } +} + + +void StreamDemuxer::VideoMedia::h264PicTimingSeiCb( + struct h264_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h264_sei_pic_timing *sei, + void *userdata) +{ + VideoMedia *self = (VideoMedia *)userdata; + + if (self == nullptr) + return; + if (ctx == nullptr) + return; + if (sei == nullptr) + return; + if (self->mCurrentFrame == nullptr) + return; + + self->mCurrentFrameCaptureTs = h264_ctx_sei_pic_timing_to_us(ctx, sei); +} + + +void StreamDemuxer::VideoMedia::h264RecoveryPointSeiCb( + struct h264_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h264_sei_recovery_point *sei, + void *userdata) +{ + VideoMedia *self = (VideoMedia *)userdata; + + if (self == nullptr) + return; + if (self->mCurrentFrame == nullptr) + return; + + if (self->mWaitForSync) { + self->mWaitForSync = false; + self->mRecoveryFrameCount = sei->recovery_frame_cnt + 1; + } +} + + +int StreamDemuxer::VideoMedia::sendCtrlCb(struct vstrm_receiver *stream, + struct tpkt_packet *pkt, + void *userdata) +{ + VideoMedia *self = (VideoMedia *)userdata; + + if (self == nullptr) + return -EINVAL; + + return self->sendCtrl(stream, pkt); +} + + +void StreamDemuxer::VideoMedia::codecInfoChangedCb( + struct vstrm_receiver *stream, + const struct vstrm_codec_info *info, + void *userdata) +{ + VideoMedia *self = (VideoMedia *)userdata; + int outputChannelCount = 0; + CodedChannel *channel; + int ret; + + if ((self == nullptr) || (info == nullptr)) + return; + if (info->codec != VSTRM_CODEC_VIDEO_H264) { + PDRAW_LOG_ERRNO("info->codec", EPROTO); + return; + } + + StreamDemuxer *demuxer = self->mDemuxer; + + if (demuxer->mState != STARTED) { + PDRAW_LOGE("demuxer is not started"); + return; + } + + PDRAW_LOGD("codec info changed"); + self->mWaitForCodecInfo = false; + + if ((!self->mCodecInfoChanging) && + (!memcmp(&self->mCodecInfo, info, sizeof(self->mCodecInfo)))) { + PDRAW_LOGI( + "codec info changed; no change in PS, " + "just flush and resync"); + self->flush(); + return; + } + self->mCodecInfo = *info; + + demuxer->CodedSource::lock(); + + if (self->mCurrentFrame != nullptr) { + ret = mbuf_coded_video_frame_unref(self->mCurrentFrame); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_unref", -ret); + self->mCurrentFrame = nullptr; + } + if (self->mCurrentMem != nullptr) { + ret = mbuf_mem_unref(self->mCurrentMem); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_mem_unref", -ret); + self->mCurrentMem = nullptr; + } + + if (self->mNbVideoMedias > 0) { + PDRAW_LOGI("change of output media"); + self->mCodecInfoChanging = true; + for (unsigned int i = 0; i < self->mNbVideoMedias; i++) { + outputChannelCount = demuxer->getOutputChannelCount( + self->mVideoMedias[i]); + + /* Teardown the output channels + * Note: loop downwards because calling teardown on a + * channel may or may not synchronously remove the + * channel from the output port */ + for (int j = outputChannelCount - 1; j >= 0; j--) { + channel = demuxer->getOutputChannel( + self->mVideoMedias[i], j); + if (channel == nullptr) { + PDRAW_LOGW( + "failed to get channel " + "at index %d", + j); + continue; + } + ret = channel->teardown(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->teardown", + -ret); + } + } + } else { + PDRAW_LOGI("new output media"); + self->mCodecInfoChanging = false; + ret = self->setupMedia(); + if (ret < 0) { + demuxer->CodedSource::unlock(); + PDRAW_LOG_ERRNO("setupMedia", -ret); + return; + } + } + + demuxer->CodedSource::unlock(); +} + + +void StreamDemuxer::VideoMedia::recvFrameCb(struct vstrm_receiver *stream, + struct vstrm_frame *frame, + void *userdata) +{ + VideoMedia *self = (VideoMedia *)userdata; + int ret; + + if ((self == nullptr) || (frame == nullptr)) + return; + + StreamDemuxer *demuxer = self->mDemuxer; + + if (demuxer->mState != STARTED) + return; + + if (demuxer->mRunning) { + pomp_timer_set(self->mFrameTimer, + DEMUXER_STREAM_TIMER_INTERVAL_MS); + } else if (demuxer->mSessionProtocol != RTSP) { + /* just ignore the frames */ + return; + } + + if (self->mCodecInfoChanging) { + /* Queue the frame to process later */ + vstrm_frame_ref(frame); + self->mTempQueue.push(frame); + return; + } + + if ((self->mWaitForCodecInfo) || (self->mFlushing)) + return; + + /* Process the incoming frame */ + ret = self->processFrame(frame); + if (ret < 0) { + if (ret != -EAGAIN) + PDRAW_LOG_ERRNO("processFrame", -ret); + return; + } +} + + +void StreamDemuxer::VideoMedia::sessionMetadataPeerChangedCb( + struct vstrm_receiver *stream, + const struct vmeta_session *meta, + void *userdata) +{ + VideoMedia *self = (VideoMedia *)userdata; + + if ((self == nullptr) || (meta == nullptr)) + return; + + PDRAW_LOGD("session metadata changed"); + + for (unsigned int i = 0; i < self->mNbVideoMedias; i++) + self->mVideoMedias[i]->sessionMeta = *meta; +} + + +void StreamDemuxer::VideoMedia::eventCb(struct vstrm_receiver *stream, + enum vstrm_event event, + void *userdata) +{ + VideoMedia *self = (VideoMedia *)userdata; + CodedChannel::DownstreamEvent evt; + bool sendEvent = false; + + if (self == nullptr) + return; + + PDRAW_LOGI("received custom RTCP event '%s'", + vstrm_event_to_str(event)); + + StreamDemuxer *demuxer = self->mDemuxer; + + if (demuxer->mState != STARTED) + return; + + switch (event) { + case VSTRM_EVENT_RECONFIGURE: + evt = CodedChannel::DownstreamEvent::RECONFIGURE; + sendEvent = true; + break; + case VSTRM_EVENT_RESOLUTION_CHANGE: + evt = CodedChannel::DownstreamEvent::TIMEOUT; + sendEvent = true; + break; + case VSTRM_EVENT_PHOTO_TRIGGER: + evt = CodedChannel::DownstreamEvent::PHOTO_TRIGGER; + sendEvent = true; + break; + default: + break; + } + + if (sendEvent) { + demuxer->CodedSource::lock(); + self->sendDownstreamEvent(evt); + demuxer->CodedSource::unlock(); + } +} + + +void StreamDemuxer::VideoMedia::goodbyeCb(struct vstrm_receiver *stream, + const char *reason, + void *userdata) +{ + VideoMedia *self = (VideoMedia *)userdata; + CodedChannel::DownstreamEvent event; + bool sendEvent = false; + + if (self == nullptr) + return; + + PDRAW_LOGI("received RTCP goodbye%s%s", + reason ? ", reason: " : "", + reason ? reason : ""); + + StreamDemuxer *demuxer = self->mDemuxer; + + if (demuxer->mState != STARTED) + return; + + pomp_timer_clear(self->mFrameTimer); + + /* Wait for new codec info */ + self->mWaitForCodecInfo = true; + + if (reason != nullptr) { + if (strcmp(reason, DEMUXER_STREAM_GOODBYE_REASON_RECONFIGURE) == + 0) { + event = CodedChannel::DownstreamEvent::RECONFIGURE; + sendEvent = true; + } else if ( + strcmp(reason, + DEMUXER_STREAM_GOODBYE_REASON_PHOTO_TRIGGER) == + 0) { + event = CodedChannel::DownstreamEvent::PHOTO_TRIGGER; + sendEvent = true; + } else { + event = CodedChannel::DownstreamEvent::EOS; + sendEvent = true; + self->mFirstFrame = true; + if (demuxer->mSessionProtocol == RTSP && + (strcmp(reason, + DEMUXER_STREAM_GOODBYE_REASON_USER) || + (demuxer->mState != STOPPING && + demuxer->mState != STOPPED))) { + /* We either received an unknown RTCP goodbye + * packet, or an unexpected (not initiated by a + * teardown) user_disconnection packet. Notify + * the application of an unrecoverable error */ + demuxer->onUnrecoverableError(); + } + } + + if (sendEvent) { + demuxer->CodedSource::lock(); + self->sendDownstreamEvent(event); + demuxer->CodedSource::unlock(); + } + } +} + + +void StreamDemuxer::VideoMedia::frameTimeoutCb(struct pomp_timer *timer, + void *userdata) +{ + VideoMedia *self = (VideoMedia *)userdata; + int res; + struct timespec ts = {0, 0}; + uint64_t curTime = 0; + + if (self == nullptr) + return; + + StreamDemuxer *demuxer = self->mDemuxer; + + if (demuxer->mState != STARTED) + return; + + res = time_get_monotonic(&ts); + if (res < 0) + PDRAW_LOG_ERRNO("time_get_monotonic", -res); + res = time_timespec_to_us(&ts, &curTime); + if (res < 0) + PDRAW_LOG_ERRNO("time_timespec_to_us", -res); + + demuxer->CodedSource::lock(); + if (curTime > + self->mLastFrameReceiveTime + DEMUXER_STREAM_FRAME_TIMEOUT_US) { + self->sendDownstreamEvent( + CodedChannel::DownstreamEvent::TIMEOUT); + } + demuxer->CodedSource::unlock(); +} + + +void StreamDemuxer::VideoMedia::rangeTimerCb(struct pomp_timer *timer, + void *userdata) +{ + VideoMedia *self = (VideoMedia *)userdata; + + if (self == nullptr) + return; + + StreamDemuxer *demuxer = self->mDemuxer; + + if (!demuxer->mEndOfRangeNotified) { + PDRAW_LOGI("end of range reached"); + self->sendDownstreamEvent(CodedChannel::DownstreamEvent::EOS); + demuxer->onEndOfRange(demuxer->mCurrentTime); + demuxer->mEndOfRangeNotified = true; + } +} + + +void StreamDemuxer::idleEndOfRangeNotification(void *userdata) +{ + StreamDemuxer *self = reinterpret_cast(userdata); + + if (self == nullptr) + return; + + if (!self->mEndOfRangeNotified) { + auto p = self->mVideoMedias.begin(); + while (p != self->mVideoMedias.end()) { + (*p)->sendDownstreamEvent( + CodedChannel::DownstreamEvent::EOS); + p++; + } + + PDRAW_LOGI("end of range reached"); + self->onEndOfRange(self->mCurrentTime); + self->mEndOfRangeNotified = true; + } +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_demuxer_stream.hpp b/libpdraw/src/pdraw_demuxer_stream.hpp index b98a056..1a1ac87 100644 --- a/libpdraw/src/pdraw_demuxer_stream.hpp +++ b/libpdraw/src/pdraw_demuxer_stream.hpp @@ -1,456 +1,456 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Streaming demuxer - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_DEMUXER_STREAM_HPP_ -#define _PDRAW_DEMUXER_STREAM_HPP_ - -#include "pdraw_demuxer.hpp" - -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace Pdraw { - - -#define DEMUXER_STREAM_DEFAULT_LOCAL_STREAM_PORT 55004 -#define DEMUXER_STREAM_DEFAULT_LOCAL_CONTROL_PORT 55005 - - -class StreamDemuxer : public Demuxer { -public: - StreamDemuxer(Session *session, - Element::Listener *elementListener, - CodedSource::Listener *sourceListener, - IPdraw::IDemuxer *demuxer, - IPdraw::IDemuxer::Listener *demuxerListener); - - virtual ~StreamDemuxer(void); - - int start(void); - - int stop(void); - - int play(float speed = 1.0f); - - bool isReadyToPlay(void); - - bool isPaused(void); - - int previous(void); - - int next(void); - - int seek(int64_t delta, bool exact = false); - - int seekTo(uint64_t timestamp, bool exact = false); - - uint64_t getDuration(void); - - uint64_t getCurrentTime(void); - -protected: - enum SessionProtocol { - NONE = 0, - RTSP, - }; - - class VideoMedia : public Loggable { - public: - VideoMedia(StreamDemuxer *demuxer); - - virtual ~VideoMedia(void); - - bool hasMedia(CodedVideoMedia *media); - - int setup(const struct sdp_media *media); - - int createReceiver(void); - - int destroyReceiver(void); - - virtual int startRtpAvp(void) = 0; - - virtual int stopRtpAvp(void) = 0; - - virtual int sendCtrl(struct vstrm_receiver *stream, - struct tpkt_packet *pkt) = 0; - - virtual int prepareSetup(void) = 0; - - virtual enum rtsp_lower_transport getLowerTransport(void) = 0; - - virtual uint16_t getLocalStreamPort(void) = 0; - - virtual uint16_t getLocalControlPort(void) = 0; - - virtual uint16_t getRemoteStreamPort(void) = 0; - - virtual uint16_t getRemoteControlPort(void) = 0; - - uint32_t getSsrc(void) - { - return mSsrc; - } - - const char *getControlUrl(void) - { - return mSdpMedia->control_url; - } - - virtual const struct rtsp_header_ext *getHeaderExt(void) - { - return NULL; - } - - virtual size_t getHeaderExtCount(void) - { - return 0; - } - - - virtual void setLocalStreamPort(uint16_t port) = 0; - - virtual void setLocalControlPort(uint16_t port) = 0; - - virtual void setRemoteStreamPort(uint16_t port) = 0; - - virtual void setRemoteControlPort(uint16_t port) = 0; - - void setSsrc(uint32_t ssrc) - { - mSsrc = ssrc; - } - - void play(void); - - void stop(void); - - void flush(void); - - void channelFlushed(CodedChannel *channel); - - void channelUnlink(CodedChannel *channel); - - void sendDownstreamEvent(CodedChannel::DownstreamEvent event); - - int processFrame(struct vstrm_frame *frame); - - void channelSendVideoPresStats(CodedChannel *channel, - VideoPresStats *stats); - - static void - sessionMetadataFromSdp(const struct sdp_media *media, - const struct vmeta_session *sessionMeta, - struct vmeta_session *meta); - - - protected: - StreamDemuxer *mDemuxer; - struct vstrm_receiver *mReceiver; - uint16_t mLocalStreamPort; - uint16_t mLocalControlPort; - uint16_t mRemoteStreamPort; - uint16_t mRemoteControlPort; - - void finishSetup(void); - - private: - int setupMedia(void); - - void teardownMedia(void); - - static void h264UserDataSeiCb( - struct h264_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h264_sei_user_data_unregistered *sei, - void *userdata); - - static void - h264PicTimingSeiCb(struct h264_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h264_sei_pic_timing *sei, - void *userdata); - - static void h264RecoveryPointSeiCb( - struct h264_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h264_sei_recovery_point *sei, - void *userdata); - - static int sendCtrlCb(struct vstrm_receiver *stream, - struct tpkt_packet *pkt, - void *userdata); - - static void - codecInfoChangedCb(struct vstrm_receiver *stream, - const struct vstrm_codec_info *info, - void *userdata); - - static void recvFrameCb(struct vstrm_receiver *stream, - struct vstrm_frame *frame, - void *userdata); - - static void - sessionMetadataPeerChangedCb(struct vstrm_receiver *stream, - const struct vmeta_session *meta, - void *userdata); - - static void eventCb(struct vstrm_receiver *stream, - enum vstrm_event event, - void *userdata); - - static void goodbyeCb(struct vstrm_receiver *stream, - const char *reason, - void *userdata); - - static void frameTimeoutCb(struct pomp_timer *timer, - void *userdata); - - static void rangeTimerCb(struct pomp_timer *timer, - void *userdata); - - CodedVideoMedia **mVideoMedias; - unsigned int mNbVideoMedias; - struct sdp_media *mSdpMedia; - struct h264_reader *mH264Reader; - struct pomp_timer *mFrameTimer; - struct pomp_timer *mRangeTimer; - uint32_t mSsrc; - bool mFlushing; - unsigned int mFlushChannelCount; - bool mFirstFrame; - uint64_t mLastFrameReceiveTime; - unsigned int mFrameIndex; - struct vstrm_codec_info mCodecInfo; - bool mWaitForCodecInfo; - bool mCodecInfoChanging; - bool mWaitForSync; - int mRecoveryFrameCount; - std::queue mTempQueue; - struct mbuf_coded_video_frame *mCurrentFrame; - struct mbuf_mem *mCurrentMem; - size_t mCurrentMemOffset; - uint64_t mCurrentFrameCaptureTs; - struct vmeta_session mSessionMetaFromSdp; - static const struct vstrm_receiver_cbs mReceiverCbs; - static const struct h264_ctx_cbs mH264Cbs; - }; - - struct SetupRequest { - VideoMedia *media; - char *controlUrl; - enum rtsp_lower_transport lowerTransport; - uint16_t localStreamPort; - uint16_t localControlPort; - const struct rtsp_header_ext *headerExt; - size_t headerExtCount; - }; - - int startRtsp(const std::string &url); - - int processSetupRequests(void); - - virtual VideoMedia *createVideoMedia(void) = 0; - void destroyAllVideoMedias(void); - - std::string mUrl; - std::string mServerAddr; - std::string mRtspAddr; - std::string mRtspPath; - const char *mContentBase; - std::string mLocalAddr; - std::string mRemoteAddr; - SessionProtocol mSessionProtocol; - std::vector mVideoMedias; - std::queue mSetupRequests; - unsigned int mSetupRequestsCount; - -private: - enum RtspState { - DISCONNECTED = 0, - CONNECTED, - OPTIONS_DONE, - DESCRIBE_DONE, - SETUP_DONE, - }; - - int internalPlay(float speed); - - int internalPause(void); - - int flush(void); - - int flush(bool destroyMedias); - - void completeTeardown(void); - - static void completeTeardownAsync(void *userdata); - - void onChannelFlushed(CodedChannel *channel); - - void onChannelUnlink(CodedChannel *channel); - - void onChannelResync(CodedChannel *channel); - - void onChannelVideoPresStats(CodedChannel *channel, - VideoPresStats *stats); - - void onNewSdp(const char *content_base, const char *sdp); - - static const char *getRtspStateStr(StreamDemuxer::RtspState val); - - static void sessionMetadataFromSdp(const struct sdp_session *session, - struct vmeta_session *meta); - - static void onRtspSocketCreated(int fd, void *userdata); - - static void onRtspConnectionState(struct rtsp_client *client, - enum rtsp_client_conn_state state, - void *userdata); - - static void onRtspSessionRemoved(struct rtsp_client *client, - const char *session_id, - int status, - void *userdata); - - static void onRtspOptionsResp(struct rtsp_client *client, - enum rtsp_client_req_status req_status, - int status, - uint32_t methods, - const struct rtsp_header_ext *ext, - size_t ext_count, - void *userdata, - void *req_userdata); - - static void onRtspDescribeResp(struct rtsp_client *client, - enum rtsp_client_req_status req_status, - int status, - const char *content_base, - const struct rtsp_header_ext *ext, - size_t ext_count, - const char *sdp, - void *userdata, - void *req_userdata); - - static void onRtspSetupResp(struct rtsp_client *client, - const char *session_id, - enum rtsp_client_req_status req_status, - int status, - uint16_t server_stream_port, - uint16_t server_control_port, - int ssrc_valid, - uint32_t ssrc, - const struct rtsp_header_ext *ext, - size_t ext_count, - void *userdata, - void *req_userdata); - - static void onRtspPlayResp(struct rtsp_client *client, - const char *session_id, - enum rtsp_client_req_status req_status, - int status, - const struct rtsp_range *range, - float scale, - int seq_valid, - uint16_t seq, - int rtptime_valid, - uint32_t rtptime, - const struct rtsp_header_ext *ext, - size_t ext_count, - void *userdata, - void *req_userdata); - - static void onRtspPauseResp(struct rtsp_client *client, - const char *session_id, - enum rtsp_client_req_status req_status, - int status, - const struct rtsp_range *range, - const struct rtsp_header_ext *ext, - size_t ext_count, - void *userdata, - void *req_userdata); - - static void onRtspTeardownResp(struct rtsp_client *client, - const char *session_id, - enum rtsp_client_req_status req_status, - int status, - const struct rtsp_header_ext *ext, - size_t ext_count, - void *userdata, - void *req_userdata); - - static void onRtspAnnounce(struct rtsp_client *client, - const char *content_base, - const struct rtsp_header_ext *ext, - size_t ext_count, - const char *sdp, - void *userdata); - - static void idleRtspDisconnect(void *userdata); - static void idleEndOfRangeNotification(void *userdata); - - struct vmeta_session mSessionMetaFromSdp; - bool mChannelsReadyForStop; - bool mNetworkReadyForStop; - RtspState mRtspState; - struct rtsp_client *mRtspClient; - const char *mRtspSessionId; - bool mRunning; - bool mFlushing; - bool mDestroyMediasAfterFlush; - unsigned int mFlushChannelCount; - uint64_t mStartTime; - uint64_t mDuration; - uint64_t mTrackDuration; - bool mUpdateTrackDuration; - uint64_t mCurrentTime; - uint64_t mPausePoint; - int64_t mNtpToNptOffset; - unsigned int mRtpClockRate; - float mSpeed; - bool mFrameByFrame; - bool mEndOfRangeNotified; - bool mSeeking; - static const struct rtsp_client_cbs mRtspClientCbs; -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_DEMUXER_STREAM_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Streaming demuxer + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_DEMUXER_STREAM_HPP_ +#define _PDRAW_DEMUXER_STREAM_HPP_ + +#include "pdraw_demuxer.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace Pdraw { + + +#define DEMUXER_STREAM_DEFAULT_LOCAL_STREAM_PORT 55004 +#define DEMUXER_STREAM_DEFAULT_LOCAL_CONTROL_PORT 55005 + + +class StreamDemuxer : public Demuxer { +public: + StreamDemuxer(Session *session, + Element::Listener *elementListener, + CodedSource::Listener *sourceListener, + IPdraw::IDemuxer *demuxer, + IPdraw::IDemuxer::Listener *demuxerListener); + + virtual ~StreamDemuxer(void); + + int start(void); + + int stop(void); + + int play(float speed = 1.0f); + + bool isReadyToPlay(void); + + bool isPaused(void); + + int previous(void); + + int next(void); + + int seek(int64_t delta, bool exact = false); + + int seekTo(uint64_t timestamp, bool exact = false); + + uint64_t getDuration(void); + + uint64_t getCurrentTime(void); + +protected: + enum SessionProtocol { + NONE = 0, + RTSP, + }; + + class VideoMedia : public Loggable { + public: + VideoMedia(StreamDemuxer *demuxer); + + virtual ~VideoMedia(void); + + bool hasMedia(CodedVideoMedia *media); + + int setup(const struct sdp_media *media); + + int createReceiver(void); + + int destroyReceiver(void); + + virtual int startRtpAvp(void) = 0; + + virtual int stopRtpAvp(void) = 0; + + virtual int sendCtrl(struct vstrm_receiver *stream, + struct tpkt_packet *pkt) = 0; + + virtual int prepareSetup(void) = 0; + + virtual enum rtsp_lower_transport getLowerTransport(void) = 0; + + virtual uint16_t getLocalStreamPort(void) = 0; + + virtual uint16_t getLocalControlPort(void) = 0; + + virtual uint16_t getRemoteStreamPort(void) = 0; + + virtual uint16_t getRemoteControlPort(void) = 0; + + uint32_t getSsrc(void) + { + return mSsrc; + } + + const char *getControlUrl(void) + { + return mSdpMedia->control_url; + } + + virtual const struct rtsp_header_ext *getHeaderExt(void) + { + return NULL; + } + + virtual size_t getHeaderExtCount(void) + { + return 0; + } + + + virtual void setLocalStreamPort(uint16_t port) = 0; + + virtual void setLocalControlPort(uint16_t port) = 0; + + virtual void setRemoteStreamPort(uint16_t port) = 0; + + virtual void setRemoteControlPort(uint16_t port) = 0; + + void setSsrc(uint32_t ssrc) + { + mSsrc = ssrc; + } + + void play(void); + + void stop(void); + + void flush(void); + + void channelFlushed(CodedChannel *channel); + + void channelUnlink(CodedChannel *channel); + + void sendDownstreamEvent(CodedChannel::DownstreamEvent event); + + int processFrame(struct vstrm_frame *frame); + + void channelSendVideoPresStats(CodedChannel *channel, + VideoPresStats *stats); + + static void + sessionMetadataFromSdp(const struct sdp_media *media, + const struct vmeta_session *sessionMeta, + struct vmeta_session *meta); + + + protected: + StreamDemuxer *mDemuxer; + struct vstrm_receiver *mReceiver; + uint16_t mLocalStreamPort; + uint16_t mLocalControlPort; + uint16_t mRemoteStreamPort; + uint16_t mRemoteControlPort; + + void finishSetup(void); + + private: + int setupMedia(void); + + void teardownMedia(void); + + static void h264UserDataSeiCb( + struct h264_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h264_sei_user_data_unregistered *sei, + void *userdata); + + static void + h264PicTimingSeiCb(struct h264_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h264_sei_pic_timing *sei, + void *userdata); + + static void h264RecoveryPointSeiCb( + struct h264_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h264_sei_recovery_point *sei, + void *userdata); + + static int sendCtrlCb(struct vstrm_receiver *stream, + struct tpkt_packet *pkt, + void *userdata); + + static void + codecInfoChangedCb(struct vstrm_receiver *stream, + const struct vstrm_codec_info *info, + void *userdata); + + static void recvFrameCb(struct vstrm_receiver *stream, + struct vstrm_frame *frame, + void *userdata); + + static void + sessionMetadataPeerChangedCb(struct vstrm_receiver *stream, + const struct vmeta_session *meta, + void *userdata); + + static void eventCb(struct vstrm_receiver *stream, + enum vstrm_event event, + void *userdata); + + static void goodbyeCb(struct vstrm_receiver *stream, + const char *reason, + void *userdata); + + static void frameTimeoutCb(struct pomp_timer *timer, + void *userdata); + + static void rangeTimerCb(struct pomp_timer *timer, + void *userdata); + + CodedVideoMedia **mVideoMedias; + unsigned int mNbVideoMedias; + struct sdp_media *mSdpMedia; + struct h264_reader *mH264Reader; + struct pomp_timer *mFrameTimer; + struct pomp_timer *mRangeTimer; + uint32_t mSsrc; + bool mFlushing; + unsigned int mFlushChannelCount; + bool mFirstFrame; + uint64_t mLastFrameReceiveTime; + unsigned int mFrameIndex; + struct vstrm_codec_info mCodecInfo; + bool mWaitForCodecInfo; + bool mCodecInfoChanging; + bool mWaitForSync; + int mRecoveryFrameCount; + std::queue mTempQueue; + struct mbuf_coded_video_frame *mCurrentFrame; + struct mbuf_mem *mCurrentMem; + size_t mCurrentMemOffset; + uint64_t mCurrentFrameCaptureTs; + struct vmeta_session mSessionMetaFromSdp; + static const struct vstrm_receiver_cbs mReceiverCbs; + static const struct h264_ctx_cbs mH264Cbs; + }; + + struct SetupRequest { + VideoMedia *media; + char *controlUrl; + enum rtsp_lower_transport lowerTransport; + uint16_t localStreamPort; + uint16_t localControlPort; + const struct rtsp_header_ext *headerExt; + size_t headerExtCount; + }; + + int startRtsp(const std::string &url); + + int processSetupRequests(void); + + virtual VideoMedia *createVideoMedia(void) = 0; + void destroyAllVideoMedias(void); + + std::string mUrl; + std::string mServerAddr; + std::string mRtspAddr; + std::string mRtspPath; + const char *mContentBase; + std::string mLocalAddr; + std::string mRemoteAddr; + SessionProtocol mSessionProtocol; + std::vector mVideoMedias; + std::queue mSetupRequests; + unsigned int mSetupRequestsCount; + +private: + enum RtspState { + DISCONNECTED = 0, + CONNECTED, + OPTIONS_DONE, + DESCRIBE_DONE, + SETUP_DONE, + }; + + int internalPlay(float speed); + + int internalPause(void); + + int flush(void); + + int flush(bool destroyMedias); + + void completeTeardown(void); + + static void completeTeardownAsync(void *userdata); + + void onChannelFlushed(CodedChannel *channel); + + void onChannelUnlink(CodedChannel *channel); + + void onChannelResync(CodedChannel *channel); + + void onChannelVideoPresStats(CodedChannel *channel, + VideoPresStats *stats); + + void onNewSdp(const char *content_base, const char *sdp); + + static const char *getRtspStateStr(StreamDemuxer::RtspState val); + + static void sessionMetadataFromSdp(const struct sdp_session *session, + struct vmeta_session *meta); + + static void onRtspSocketCreated(int fd, void *userdata); + + static void onRtspConnectionState(struct rtsp_client *client, + enum rtsp_client_conn_state state, + void *userdata); + + static void onRtspSessionRemoved(struct rtsp_client *client, + const char *session_id, + int status, + void *userdata); + + static void onRtspOptionsResp(struct rtsp_client *client, + enum rtsp_client_req_status req_status, + int status, + uint32_t methods, + const struct rtsp_header_ext *ext, + size_t ext_count, + void *userdata, + void *req_userdata); + + static void onRtspDescribeResp(struct rtsp_client *client, + enum rtsp_client_req_status req_status, + int status, + const char *content_base, + const struct rtsp_header_ext *ext, + size_t ext_count, + const char *sdp, + void *userdata, + void *req_userdata); + + static void onRtspSetupResp(struct rtsp_client *client, + const char *session_id, + enum rtsp_client_req_status req_status, + int status, + uint16_t server_stream_port, + uint16_t server_control_port, + int ssrc_valid, + uint32_t ssrc, + const struct rtsp_header_ext *ext, + size_t ext_count, + void *userdata, + void *req_userdata); + + static void onRtspPlayResp(struct rtsp_client *client, + const char *session_id, + enum rtsp_client_req_status req_status, + int status, + const struct rtsp_range *range, + float scale, + int seq_valid, + uint16_t seq, + int rtptime_valid, + uint32_t rtptime, + const struct rtsp_header_ext *ext, + size_t ext_count, + void *userdata, + void *req_userdata); + + static void onRtspPauseResp(struct rtsp_client *client, + const char *session_id, + enum rtsp_client_req_status req_status, + int status, + const struct rtsp_range *range, + const struct rtsp_header_ext *ext, + size_t ext_count, + void *userdata, + void *req_userdata); + + static void onRtspTeardownResp(struct rtsp_client *client, + const char *session_id, + enum rtsp_client_req_status req_status, + int status, + const struct rtsp_header_ext *ext, + size_t ext_count, + void *userdata, + void *req_userdata); + + static void onRtspAnnounce(struct rtsp_client *client, + const char *content_base, + const struct rtsp_header_ext *ext, + size_t ext_count, + const char *sdp, + void *userdata); + + static void idleRtspDisconnect(void *userdata); + static void idleEndOfRangeNotification(void *userdata); + + struct vmeta_session mSessionMetaFromSdp; + bool mChannelsReadyForStop; + bool mNetworkReadyForStop; + RtspState mRtspState; + struct rtsp_client *mRtspClient; + const char *mRtspSessionId; + bool mRunning; + bool mFlushing; + bool mDestroyMediasAfterFlush; + unsigned int mFlushChannelCount; + uint64_t mStartTime; + uint64_t mDuration; + uint64_t mTrackDuration; + bool mUpdateTrackDuration; + uint64_t mCurrentTime; + uint64_t mPausePoint; + int64_t mNtpToNptOffset; + unsigned int mRtpClockRate; + float mSpeed; + bool mFrameByFrame; + bool mEndOfRangeNotified; + bool mSeeking; + static const struct rtsp_client_cbs mRtspClientCbs; +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_DEMUXER_STREAM_HPP_ */ diff --git a/libpdraw/src/pdraw_demuxer_stream_mux.cpp b/libpdraw/src/pdraw_demuxer_stream_mux.cpp index 251707d..59785e8 100644 --- a/libpdraw/src/pdraw_demuxer_stream_mux.cpp +++ b/libpdraw/src/pdraw_demuxer_stream_mux.cpp @@ -1,678 +1,678 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Streaming demuxer - mux implementation - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_dmxstrmmux -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_demuxer_stream_mux.hpp" -#include "pdraw_session.hpp" - -#define DEFAULT_RX_BUFFER_SIZE 1500 - -#ifdef BUILD_LIBMUX - -# include -# include - -# include -# include - -namespace Pdraw { - - -const size_t StreamDemuxerMux::VideoMediaMux::mHeaderExtCount = 1; - - -const struct rtsp_header_ext StreamDemuxerMux::VideoMediaMux::mHeaderExt = { - .key = RTSP_HEADER_EXT_PARROT_LINK_TYPE, - .value = "mux", -}; - - -StreamDemuxerMux::StreamDemuxerMux(Session *session, - Element::Listener *elementListener, - CodedSource::Listener *sourceListener, - IPdraw::IDemuxer *demuxer, - IPdraw::IDemuxer::Listener *demuxerListener, - const std::string &url, - struct mux_ctx *mux) : - StreamDemuxer(session, - elementListener, - sourceListener, - demuxer, - demuxerListener), - mMux(nullptr) -{ - Element::setClassName(__func__); - - if (!setMux(mux)) - PDRAW_LOGE("invalid mux handle"); - - mUrl = url; - - setState(CREATED); -} - - -StreamDemuxerMux::~StreamDemuxerMux(void) -{ - destroyAllVideoMedias(); - setMux(nullptr); -} - - -StreamDemuxer::VideoMedia *StreamDemuxerMux::createVideoMedia(void) -{ - return (StreamDemuxer::VideoMedia *)new VideoMediaMux(this); -} - - -bool StreamDemuxerMux::setMux(struct mux_ctx *mux) -{ - if (mMux != nullptr) - mux_unref(mMux); - - mMux = mux; - if (mMux != nullptr) { - mux_ref(mMux); - return true; - } - return false; -} - - -StreamDemuxerMux::VideoMediaMux::VideoMediaMux(StreamDemuxerMux *demuxer) : - VideoMedia(demuxer), mDemuxerMux(demuxer), mStreamSock(nullptr), - mStreamProxy(nullptr), mStreamProxyOpened(false), - mControlSock(nullptr), mControlProxy(nullptr), - mControlProxyOpened(false), mRxPkt(nullptr), mRxBufLen(0) -{ -} - - -StreamDemuxerMux::VideoMediaMux::~VideoMediaMux(void) -{ - stopRtpAvp(); - struct pomp_loop *loop = mDemuxerMux->mSession->getLoop(); - pomp_loop_idle_remove(loop, callFinishSetup, this); -} - - -int StreamDemuxerMux::VideoMediaMux::startRtpAvp(void) -{ - int res; - - if (mDemuxerMux->mMux == nullptr) { - PDRAW_LOGE("invalid mux handle"); - return -EPROTO; - } - - if (mDemuxerMux->mSessionProtocol == RTSP) { - /* Everything should be done in prepareSetup() */ - } else { - res = mux_channel_open(mDemuxerMux->mMux, - MUX_ARSDK_CHANNEL_ID_STREAM_DATA, - &legacyDataCb, - this); - if (res < 0) { - PDRAW_LOG_ERRNO("mux_channel_open", -res); - goto error; - } - res = mux_channel_open(mDemuxerMux->mMux, - MUX_ARSDK_CHANNEL_ID_STREAM_CONTROL, - &legacyCtrlCb, - this); - if (res < 0) { - PDRAW_LOG_ERRNO("mux_channel_open", -res); - goto error; - } - } - - createReceiver(); - return 0; - -error: - stopRtpAvp(); - return res; -} - - -int StreamDemuxerMux::VideoMediaMux::stopRtpAvp(void) -{ - destroyReceiver(); - if (mDemuxerMux->mMux != nullptr) { - if (mDemuxerMux->mSessionProtocol == RTSP) { - closeSockets(); - if (mStreamProxy) { - mux_ip_proxy_destroy(mStreamProxy); - mStreamProxy = nullptr; - } - if (mControlProxy) { - mux_ip_proxy_destroy(mControlProxy); - mControlProxy = nullptr; - } - } else { - mux_channel_close(mDemuxerMux->mMux, - MUX_ARSDK_CHANNEL_ID_STREAM_DATA); - mux_channel_close(mDemuxerMux->mMux, - MUX_ARSDK_CHANNEL_ID_STREAM_CONTROL); - } - } - return 0; -} - - -int StreamDemuxerMux::VideoMediaMux::sendCtrl(struct vstrm_receiver *stream, - struct tpkt_packet *pkt) -{ - int res; - - PDRAW_LOG_ERRNO_RETURN_ERR_IF(pkt == nullptr, EINVAL); - - /* Write data */ - res = tskt_socket_write_pkt(mControlSock, pkt); - if (res < 0) - PDRAW_LOG_ERRNO("tskt_socket_write_pkt", -res); - - return res; -} - - -int StreamDemuxerMux::VideoMediaMux::prepareSetup(void) -{ - /* clang-format off */ - struct mux_ip_proxy_info info = { - .protocol = { - .transport = MUX_IP_PROXY_TRANSPORT_UDP, - .application = MUX_IP_PROXY_APPLICATION_NONE, - }, - .remote_host = "skycontroller", - /* remote port will be updated after setup */ - .remote_port = 0, - .udp_redirect_port = 0, - }; - /* clang-format on */ - struct mux_ip_proxy_cbs cbs = { - .open = proxyOpenCb, - .close = proxyCloseCb, - .remote_update = proxyUpdateCb, - .resolution_failed = proxyFailedCb, - .userdata = this, - }; - - int res = createSockets(); - if (res != 0) { - PDRAW_LOG_ERRNO("createSockets", -res); - return res; - } - - info.udp_redirect_port = tskt_socket_get_local_port(mStreamSock); - res = mux_ip_proxy_new( - mDemuxerMux->mMux, &info, &cbs, -1, &mStreamProxy); - if (res < 0) { - PDRAW_LOG_ERRNO("mux_ip_proxy_new(rtp)", -res); - goto error; - } - info.udp_redirect_port = tskt_socket_get_local_port(mControlSock); - res = mux_ip_proxy_new( - mDemuxerMux->mMux, &info, &cbs, -1, &mControlProxy); - if (res < 0) { - PDRAW_LOG_ERRNO("mux_ip_proxy_new(rtcp)", -res); - goto error; - } - - return -EINPROGRESS; - -error: - closeSockets(); - if (mStreamProxy) { - mux_ip_proxy_destroy(mStreamProxy); - mStreamProxy = nullptr; - } - if (mControlProxy) { - mux_ip_proxy_destroy(mControlProxy); - mControlProxy = nullptr; - } - return res; -} - - -enum rtsp_lower_transport -StreamDemuxerMux::VideoMediaMux::getLowerTransport(void) -{ - return RTSP_LOWER_TRANSPORT_UDP; -} - - -uint16_t StreamDemuxerMux::VideoMediaMux::getLocalStreamPort(void) -{ - return mux_ip_proxy_get_peerport(mStreamProxy); -} - - -uint16_t StreamDemuxerMux::VideoMediaMux::getLocalControlPort(void) -{ - return mux_ip_proxy_get_peerport(mControlProxy); -} - - -uint16_t StreamDemuxerMux::VideoMediaMux::getRemoteStreamPort(void) -{ - return mux_ip_proxy_get_remote_port(mStreamProxy); -} - - -uint16_t StreamDemuxerMux::VideoMediaMux::getRemoteControlPort(void) -{ - return mux_ip_proxy_get_remote_port(mControlProxy); -} - - -const struct rtsp_header_ext * -StreamDemuxerMux::VideoMediaMux::getHeaderExt(void) -{ - return &mHeaderExt; -} - - -size_t StreamDemuxerMux::VideoMediaMux::getHeaderExtCount(void) -{ - return mHeaderExtCount; -} - - -void StreamDemuxerMux::VideoMediaMux::setLocalStreamPort(uint16_t port) -{ - /* Nothing to do */ -} - - -void StreamDemuxerMux::VideoMediaMux::setLocalControlPort(uint16_t port) -{ - /* Nothing to do */ -} - - -void StreamDemuxerMux::VideoMediaMux::setRemoteStreamPort(uint16_t port) -{ - mux_ip_proxy_set_udp_remote(mStreamProxy, "skycontroller", port, -1); -} - - -void StreamDemuxerMux::VideoMediaMux::setRemoteControlPort(uint16_t port) -{ - mux_ip_proxy_set_udp_remote(mControlProxy, "skycontroller", port, -1); -} - - -void StreamDemuxerMux::VideoMediaMux::legacyDataCb(struct mux_ctx *ctx, - uint32_t chanid, - enum mux_channel_event event, - struct pomp_buffer *buf, - void *userdata) -{ - VideoMediaMux *self = (VideoMediaMux *)userdata; - int res; - struct tpkt_packet *pkt = nullptr; - struct timespec ts = {0, 0}; - uint64_t curTime = 0; - - if (self == nullptr) - return; - - res = tpkt_new_from_buffer(buf, &pkt); - if (res < 0) { - PDRAW_LOG_ERRNO("tpkt_new_from_buffer", -res); - return; - } - - res = time_get_monotonic(&ts); - if (res < 0) { - PDRAW_LOG_ERRNO("time_get_monotonic", -res); - goto out; - } - res = time_timespec_to_us(&ts, &curTime); - if (res < 0) { - PDRAW_LOG_ERRNO("time_timespec_to_us", -res); - goto out; - } - res = tpkt_set_timestamp(pkt, curTime); - if (res < 0) { - PDRAW_LOG_ERRNO("tpkt_set_timestamp", -res); - goto out; - } - - res = vstrm_receiver_recv_data(self->mReceiver, pkt); - if (res < 0) - PDRAW_LOG_ERRNO("vstrm_receiver_recv_data", -res); - -out: - tpkt_unref(pkt); -} - - -void StreamDemuxerMux::VideoMediaMux::legacyCtrlCb(struct mux_ctx *ctx, - uint32_t chanid, - enum mux_channel_event event, - struct pomp_buffer *buf, - void *userdata) -{ - VideoMediaMux *self = (VideoMediaMux *)userdata; - int res; - struct tpkt_packet *pkt = nullptr; - struct timespec ts = {0, 0}; - uint64_t curTime = 0; - - if (self == nullptr) - return; - - res = tpkt_new_from_buffer(buf, &pkt); - if (res < 0) { - PDRAW_LOG_ERRNO("tpkt_new_from_buffer", -res); - return; - } - - res = time_get_monotonic(&ts); - if (res < 0) { - PDRAW_LOG_ERRNO("time_get_monotonic", -res); - goto out; - } - res = time_timespec_to_us(&ts, &curTime); - if (res < 0) { - PDRAW_LOG_ERRNO("time_timespec_to_us", -res); - goto out; - } - res = tpkt_set_timestamp(pkt, curTime); - if (res < 0) { - PDRAW_LOG_ERRNO("tpkt_set_timestamp", -res); - goto out; - } - - res = vstrm_receiver_recv_ctrl(self->mReceiver, pkt); - if (res < 0) - PDRAW_LOG_ERRNO("vstrm_receiver_recv_ctrl", -res); - -out: - tpkt_unref(pkt); -} - - -int StreamDemuxerMux::VideoMediaMux::createSockets(void) -{ - int res; - if (mLocalStreamPort == 0) - mLocalStreamPort = DEMUXER_STREAM_DEFAULT_LOCAL_STREAM_PORT; - if (mLocalControlPort == 0) - mLocalControlPort = DEMUXER_STREAM_DEFAULT_LOCAL_CONTROL_PORT; - - /* Create the rx buffer */ - mRxBufLen = DEFAULT_RX_BUFFER_SIZE; - mRxPkt = newRxPkt(); - if (mRxPkt == nullptr) { - res = -ENOMEM; - PDRAW_LOG_ERRNO("newRxPkt", -res); - goto error; - } - - /* Create the sockets */ - res = tskt_socket_new("127.0.0.1", - &mLocalStreamPort, - "127.0.0.1", - mRemoteStreamPort, - nullptr, - mDemuxerMux->mSession->getLoop(), - dataCb, - this, - &mStreamSock); - if (res < 0) { - PDRAW_LOG_ERRNO("tskt_socket_new:stream", -res); - goto error; - } - res = tskt_socket_set_class_selector(mStreamSock, - IPTOS_PREC_FLASHOVERRIDE); - if (res < 0) - PDRAW_LOGW("failed to set class selector for stream socket"); - - res = tskt_socket_new("127.0.0.1", - &mLocalControlPort, - "127.0.0.1", - mRemoteControlPort, - nullptr, - mDemuxerMux->mSession->getLoop(), - ctrlCb, - this, - &mControlSock); - if (res < 0) { - PDRAW_LOG_ERRNO("tskt_socket_new:control", -res); - goto error; - } - res = tskt_socket_set_class_selector(mControlSock, - IPTOS_PREC_FLASHOVERRIDE); - if (res < 0) - PDRAW_LOGW("failed to set class selector for control socket"); - - return 0; - -error: - closeSockets(); - return res; -} - -void StreamDemuxerMux::VideoMediaMux::closeSockets(void) -{ - int err; - err = tskt_socket_destroy(mStreamSock); - if (err < 0) - PDRAW_LOG_ERRNO("tskt_socket_destroy", -err); - mStreamSock = nullptr; - err = tskt_socket_destroy(mControlSock); - if (err < 0) - PDRAW_LOG_ERRNO("tskt_socket_destroy", -err); - mControlSock = nullptr; - tpkt_unref(mRxPkt); - mRxPkt = nullptr; -} - - -struct tpkt_packet *StreamDemuxerMux::VideoMediaMux::newRxPkt(void) -{ - struct pomp_buffer *buf = pomp_buffer_new(mRxBufLen); - if (!buf) - return nullptr; - - struct tpkt_packet *pkt; - int res = tpkt_new_from_buffer(buf, &pkt); - pomp_buffer_unref(buf); - if (res < 0) - return nullptr; - - return pkt; -} - - -void StreamDemuxerMux::VideoMediaMux::dataCb(int fd, - uint32_t events, - void *userdata) -{ - VideoMediaMux *self = (VideoMediaMux *)userdata; - int res; - size_t readlen = 0; - - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - while (1) { - /* Read data */ - res = tskt_socket_read_pkt(self->mStreamSock, self->mRxPkt); - if (res < 0) - break; - - /* Discard any data received before starting a vstrm_receiver */ - if (!self->mReceiver) - continue; - - /* Something read? */ - res = tpkt_get_cdata(self->mRxPkt, nullptr, &readlen, nullptr); - if (res < 0) - break; - if (readlen != 0) { - /* Allocate new packet for replacement */ - struct tpkt_packet *newPkt = self->newRxPkt(); - if (!newPkt) { - PDRAW_LOG_ERRNO("newRxPkt", ENOMEM); - break; - } - /* Process received packet */ - res = vstrm_receiver_recv_data(self->mReceiver, - self->mRxPkt); - /* Replace processed packet with new one */ - tpkt_unref(self->mRxPkt); - self->mRxPkt = newPkt; - if (res < 0) - PDRAW_LOG_ERRNO("vstrm_receiver_recv_data", - -res); - } else { - /* TODO: EOF */ - break; - } - } -} - - -void StreamDemuxerMux::VideoMediaMux::ctrlCb(int fd, - uint32_t events, - void *userdata) -{ - VideoMediaMux *self = (VideoMediaMux *)userdata; - int res; - size_t readlen = 0; - - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - while (1) { - /* Read data */ - res = tskt_socket_read_pkt(self->mControlSock, self->mRxPkt); - if (res < 0) - break; - - /* Discard any data received before starting a vstrm_receiver */ - if (!self->mReceiver) - continue; - - /* Something read? */ - res = tpkt_get_cdata(self->mRxPkt, nullptr, &readlen, nullptr); - if (res < 0) - break; - if (readlen != 0) { - /* Allocate new packet for replacement */ - struct tpkt_packet *newPkt = self->newRxPkt(); - if (!newPkt) { - PDRAW_LOG_ERRNO("newRxPkt", ENOMEM); - break; - } - /* Process received packet */ - res = vstrm_receiver_recv_ctrl(self->mReceiver, - self->mRxPkt); - /* Replace processed packet with new one */ - tpkt_unref(self->mRxPkt); - self->mRxPkt = newPkt; - if (res < 0) - PDRAW_LOG_ERRNO("vstrm_receiver_recv_ctrl", - -res); - } else { - /* TODO: EOF */ - break; - } - } -} - -void StreamDemuxerMux::VideoMediaMux::callFinishSetup(void *userdata) -{ - VideoMediaMux *self = (VideoMediaMux *)userdata; - self->finishSetup(); -} - -void StreamDemuxerMux::VideoMediaMux::proxyOpenCb(struct mux_ip_proxy *proxy, - uint16_t localPort, - void *userdata) -{ - VideoMediaMux *self = (VideoMediaMux *)userdata; - if (proxy == self->mStreamProxy) { - self->mStreamProxyOpened = true; - } else if (proxy == self->mControlProxy) { - self->mControlProxyOpened = true; - } else { - PDRAW_LOGE("uknown proxy opened"); - return; - } - - if (self->mStreamProxyOpened && self->mControlProxyOpened) { - struct pomp_loop *loop = self->mDemuxerMux->mSession->getLoop(); - pomp_loop_idle_remove(loop, callFinishSetup, self); - pomp_loop_idle_add(loop, callFinishSetup, self); - } -} - -void StreamDemuxerMux::VideoMediaMux::proxyCloseCb(struct mux_ip_proxy *proxy, - void *userdata) -{ - VideoMediaMux *self = (VideoMediaMux *)userdata; - if (proxy == self->mStreamProxy) { - self->mStreamProxyOpened = false; - } else if (proxy == self->mControlProxy) { - self->mControlProxyOpened = false; - } else { - PDRAW_LOGE("uknown proxy closed"); - return; - } -} - -void StreamDemuxerMux::VideoMediaMux::proxyUpdateCb(struct mux_ip_proxy *proxy, - void *userdata) -{ - /* TODO ? */ -} - -void StreamDemuxerMux::VideoMediaMux::proxyFailedCb(struct mux_ip_proxy *proxy, - int err, - void *userdata) -{ - VideoMediaMux *self = (VideoMediaMux *)userdata; - const char *name = "unknown"; - if (proxy == self->mStreamProxy) - name = "stream"; - else if (proxy == self->mControlProxy) - name = "control"; - PDRAW_LOG_ERRNO("%s proxy failed to resolve", -err, name); -} - -} /* namespace Pdraw */ - -#endif /* BUILD_LIBMUX */ +/** + * Parrot Drones Awesome Video Viewer Library + * Streaming demuxer - mux implementation + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_dmxstrmmux +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_demuxer_stream_mux.hpp" +#include "pdraw_session.hpp" + +#define DEFAULT_RX_BUFFER_SIZE 1500 + +#ifdef BUILD_LIBMUX + +# include +# include + +# include +# include + +namespace Pdraw { + + +const size_t StreamDemuxerMux::VideoMediaMux::mHeaderExtCount = 1; + + +const struct rtsp_header_ext StreamDemuxerMux::VideoMediaMux::mHeaderExt = { + .key = RTSP_HEADER_EXT_PARROT_LINK_TYPE, + .value = "mux", +}; + + +StreamDemuxerMux::StreamDemuxerMux(Session *session, + Element::Listener *elementListener, + CodedSource::Listener *sourceListener, + IPdraw::IDemuxer *demuxer, + IPdraw::IDemuxer::Listener *demuxerListener, + const std::string &url, + struct mux_ctx *mux) : + StreamDemuxer(session, + elementListener, + sourceListener, + demuxer, + demuxerListener), + mMux(nullptr) +{ + Element::setClassName(__func__); + + if (!setMux(mux)) + PDRAW_LOGE("invalid mux handle"); + + mUrl = url; + + setState(CREATED); +} + + +StreamDemuxerMux::~StreamDemuxerMux(void) +{ + destroyAllVideoMedias(); + setMux(nullptr); +} + + +StreamDemuxer::VideoMedia *StreamDemuxerMux::createVideoMedia(void) +{ + return (StreamDemuxer::VideoMedia *)new VideoMediaMux(this); +} + + +bool StreamDemuxerMux::setMux(struct mux_ctx *mux) +{ + if (mMux != nullptr) + mux_unref(mMux); + + mMux = mux; + if (mMux != nullptr) { + mux_ref(mMux); + return true; + } + return false; +} + + +StreamDemuxerMux::VideoMediaMux::VideoMediaMux(StreamDemuxerMux *demuxer) : + VideoMedia(demuxer), mDemuxerMux(demuxer), mStreamSock(nullptr), + mStreamProxy(nullptr), mStreamProxyOpened(false), + mControlSock(nullptr), mControlProxy(nullptr), + mControlProxyOpened(false), mRxPkt(nullptr), mRxBufLen(0) +{ +} + + +StreamDemuxerMux::VideoMediaMux::~VideoMediaMux(void) +{ + stopRtpAvp(); + struct pomp_loop *loop = mDemuxerMux->mSession->getLoop(); + pomp_loop_idle_remove(loop, callFinishSetup, this); +} + + +int StreamDemuxerMux::VideoMediaMux::startRtpAvp(void) +{ + int res; + + if (mDemuxerMux->mMux == nullptr) { + PDRAW_LOGE("invalid mux handle"); + return -EPROTO; + } + + if (mDemuxerMux->mSessionProtocol == RTSP) { + /* Everything should be done in prepareSetup() */ + } else { + res = mux_channel_open(mDemuxerMux->mMux, + MUX_ARSDK_CHANNEL_ID_STREAM_DATA, + &legacyDataCb, + this); + if (res < 0) { + PDRAW_LOG_ERRNO("mux_channel_open", -res); + goto error; + } + res = mux_channel_open(mDemuxerMux->mMux, + MUX_ARSDK_CHANNEL_ID_STREAM_CONTROL, + &legacyCtrlCb, + this); + if (res < 0) { + PDRAW_LOG_ERRNO("mux_channel_open", -res); + goto error; + } + } + + createReceiver(); + return 0; + +error: + stopRtpAvp(); + return res; +} + + +int StreamDemuxerMux::VideoMediaMux::stopRtpAvp(void) +{ + destroyReceiver(); + if (mDemuxerMux->mMux != nullptr) { + if (mDemuxerMux->mSessionProtocol == RTSP) { + closeSockets(); + if (mStreamProxy) { + mux_ip_proxy_destroy(mStreamProxy); + mStreamProxy = nullptr; + } + if (mControlProxy) { + mux_ip_proxy_destroy(mControlProxy); + mControlProxy = nullptr; + } + } else { + mux_channel_close(mDemuxerMux->mMux, + MUX_ARSDK_CHANNEL_ID_STREAM_DATA); + mux_channel_close(mDemuxerMux->mMux, + MUX_ARSDK_CHANNEL_ID_STREAM_CONTROL); + } + } + return 0; +} + + +int StreamDemuxerMux::VideoMediaMux::sendCtrl(struct vstrm_receiver *stream, + struct tpkt_packet *pkt) +{ + int res; + + PDRAW_LOG_ERRNO_RETURN_ERR_IF(pkt == nullptr, EINVAL); + + /* Write data */ + res = tskt_socket_write_pkt(mControlSock, pkt); + if (res < 0) + PDRAW_LOG_ERRNO("tskt_socket_write_pkt", -res); + + return res; +} + + +int StreamDemuxerMux::VideoMediaMux::prepareSetup(void) +{ + /* clang-format off */ + struct mux_ip_proxy_info info = { + .protocol = { + .transport = MUX_IP_PROXY_TRANSPORT_UDP, + .application = MUX_IP_PROXY_APPLICATION_NONE, + }, + .remote_host = "skycontroller", + /* remote port will be updated after setup */ + .remote_port = 0, + .udp_redirect_port = 0, + }; + /* clang-format on */ + struct mux_ip_proxy_cbs cbs = { + .open = proxyOpenCb, + .close = proxyCloseCb, + .remote_update = proxyUpdateCb, + .resolution_failed = proxyFailedCb, + .userdata = this, + }; + + int res = createSockets(); + if (res != 0) { + PDRAW_LOG_ERRNO("createSockets", -res); + return res; + } + + info.udp_redirect_port = tskt_socket_get_local_port(mStreamSock); + res = mux_ip_proxy_new( + mDemuxerMux->mMux, &info, &cbs, -1, &mStreamProxy); + if (res < 0) { + PDRAW_LOG_ERRNO("mux_ip_proxy_new(rtp)", -res); + goto error; + } + info.udp_redirect_port = tskt_socket_get_local_port(mControlSock); + res = mux_ip_proxy_new( + mDemuxerMux->mMux, &info, &cbs, -1, &mControlProxy); + if (res < 0) { + PDRAW_LOG_ERRNO("mux_ip_proxy_new(rtcp)", -res); + goto error; + } + + return -EINPROGRESS; + +error: + closeSockets(); + if (mStreamProxy) { + mux_ip_proxy_destroy(mStreamProxy); + mStreamProxy = nullptr; + } + if (mControlProxy) { + mux_ip_proxy_destroy(mControlProxy); + mControlProxy = nullptr; + } + return res; +} + + +enum rtsp_lower_transport +StreamDemuxerMux::VideoMediaMux::getLowerTransport(void) +{ + return RTSP_LOWER_TRANSPORT_UDP; +} + + +uint16_t StreamDemuxerMux::VideoMediaMux::getLocalStreamPort(void) +{ + return mux_ip_proxy_get_peerport(mStreamProxy); +} + + +uint16_t StreamDemuxerMux::VideoMediaMux::getLocalControlPort(void) +{ + return mux_ip_proxy_get_peerport(mControlProxy); +} + + +uint16_t StreamDemuxerMux::VideoMediaMux::getRemoteStreamPort(void) +{ + return mux_ip_proxy_get_remote_port(mStreamProxy); +} + + +uint16_t StreamDemuxerMux::VideoMediaMux::getRemoteControlPort(void) +{ + return mux_ip_proxy_get_remote_port(mControlProxy); +} + + +const struct rtsp_header_ext * +StreamDemuxerMux::VideoMediaMux::getHeaderExt(void) +{ + return &mHeaderExt; +} + + +size_t StreamDemuxerMux::VideoMediaMux::getHeaderExtCount(void) +{ + return mHeaderExtCount; +} + + +void StreamDemuxerMux::VideoMediaMux::setLocalStreamPort(uint16_t port) +{ + /* Nothing to do */ +} + + +void StreamDemuxerMux::VideoMediaMux::setLocalControlPort(uint16_t port) +{ + /* Nothing to do */ +} + + +void StreamDemuxerMux::VideoMediaMux::setRemoteStreamPort(uint16_t port) +{ + mux_ip_proxy_set_udp_remote(mStreamProxy, "skycontroller", port, -1); +} + + +void StreamDemuxerMux::VideoMediaMux::setRemoteControlPort(uint16_t port) +{ + mux_ip_proxy_set_udp_remote(mControlProxy, "skycontroller", port, -1); +} + + +void StreamDemuxerMux::VideoMediaMux::legacyDataCb(struct mux_ctx *ctx, + uint32_t chanid, + enum mux_channel_event event, + struct pomp_buffer *buf, + void *userdata) +{ + VideoMediaMux *self = (VideoMediaMux *)userdata; + int res; + struct tpkt_packet *pkt = nullptr; + struct timespec ts = {0, 0}; + uint64_t curTime = 0; + + if (self == nullptr) + return; + + res = tpkt_new_from_buffer(buf, &pkt); + if (res < 0) { + PDRAW_LOG_ERRNO("tpkt_new_from_buffer", -res); + return; + } + + res = time_get_monotonic(&ts); + if (res < 0) { + PDRAW_LOG_ERRNO("time_get_monotonic", -res); + goto out; + } + res = time_timespec_to_us(&ts, &curTime); + if (res < 0) { + PDRAW_LOG_ERRNO("time_timespec_to_us", -res); + goto out; + } + res = tpkt_set_timestamp(pkt, curTime); + if (res < 0) { + PDRAW_LOG_ERRNO("tpkt_set_timestamp", -res); + goto out; + } + + res = vstrm_receiver_recv_data(self->mReceiver, pkt); + if (res < 0) + PDRAW_LOG_ERRNO("vstrm_receiver_recv_data", -res); + +out: + tpkt_unref(pkt); +} + + +void StreamDemuxerMux::VideoMediaMux::legacyCtrlCb(struct mux_ctx *ctx, + uint32_t chanid, + enum mux_channel_event event, + struct pomp_buffer *buf, + void *userdata) +{ + VideoMediaMux *self = (VideoMediaMux *)userdata; + int res; + struct tpkt_packet *pkt = nullptr; + struct timespec ts = {0, 0}; + uint64_t curTime = 0; + + if (self == nullptr) + return; + + res = tpkt_new_from_buffer(buf, &pkt); + if (res < 0) { + PDRAW_LOG_ERRNO("tpkt_new_from_buffer", -res); + return; + } + + res = time_get_monotonic(&ts); + if (res < 0) { + PDRAW_LOG_ERRNO("time_get_monotonic", -res); + goto out; + } + res = time_timespec_to_us(&ts, &curTime); + if (res < 0) { + PDRAW_LOG_ERRNO("time_timespec_to_us", -res); + goto out; + } + res = tpkt_set_timestamp(pkt, curTime); + if (res < 0) { + PDRAW_LOG_ERRNO("tpkt_set_timestamp", -res); + goto out; + } + + res = vstrm_receiver_recv_ctrl(self->mReceiver, pkt); + if (res < 0) + PDRAW_LOG_ERRNO("vstrm_receiver_recv_ctrl", -res); + +out: + tpkt_unref(pkt); +} + + +int StreamDemuxerMux::VideoMediaMux::createSockets(void) +{ + int res; + if (mLocalStreamPort == 0) + mLocalStreamPort = DEMUXER_STREAM_DEFAULT_LOCAL_STREAM_PORT; + if (mLocalControlPort == 0) + mLocalControlPort = DEMUXER_STREAM_DEFAULT_LOCAL_CONTROL_PORT; + + /* Create the rx buffer */ + mRxBufLen = DEFAULT_RX_BUFFER_SIZE; + mRxPkt = newRxPkt(); + if (mRxPkt == nullptr) { + res = -ENOMEM; + PDRAW_LOG_ERRNO("newRxPkt", -res); + goto error; + } + + /* Create the sockets */ + res = tskt_socket_new("127.0.0.1", + &mLocalStreamPort, + "127.0.0.1", + mRemoteStreamPort, + nullptr, + mDemuxerMux->mSession->getLoop(), + dataCb, + this, + &mStreamSock); + if (res < 0) { + PDRAW_LOG_ERRNO("tskt_socket_new:stream", -res); + goto error; + } + res = tskt_socket_set_class_selector(mStreamSock, + IPTOS_PREC_FLASHOVERRIDE); + if (res < 0) + PDRAW_LOGW("failed to set class selector for stream socket"); + + res = tskt_socket_new("127.0.0.1", + &mLocalControlPort, + "127.0.0.1", + mRemoteControlPort, + nullptr, + mDemuxerMux->mSession->getLoop(), + ctrlCb, + this, + &mControlSock); + if (res < 0) { + PDRAW_LOG_ERRNO("tskt_socket_new:control", -res); + goto error; + } + res = tskt_socket_set_class_selector(mControlSock, + IPTOS_PREC_FLASHOVERRIDE); + if (res < 0) + PDRAW_LOGW("failed to set class selector for control socket"); + + return 0; + +error: + closeSockets(); + return res; +} + +void StreamDemuxerMux::VideoMediaMux::closeSockets(void) +{ + int err; + err = tskt_socket_destroy(mStreamSock); + if (err < 0) + PDRAW_LOG_ERRNO("tskt_socket_destroy", -err); + mStreamSock = nullptr; + err = tskt_socket_destroy(mControlSock); + if (err < 0) + PDRAW_LOG_ERRNO("tskt_socket_destroy", -err); + mControlSock = nullptr; + tpkt_unref(mRxPkt); + mRxPkt = nullptr; +} + + +struct tpkt_packet *StreamDemuxerMux::VideoMediaMux::newRxPkt(void) +{ + struct pomp_buffer *buf = pomp_buffer_new(mRxBufLen); + if (!buf) + return nullptr; + + struct tpkt_packet *pkt; + int res = tpkt_new_from_buffer(buf, &pkt); + pomp_buffer_unref(buf); + if (res < 0) + return nullptr; + + return pkt; +} + + +void StreamDemuxerMux::VideoMediaMux::dataCb(int fd, + uint32_t events, + void *userdata) +{ + VideoMediaMux *self = (VideoMediaMux *)userdata; + int res; + size_t readlen = 0; + + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + while (1) { + /* Read data */ + res = tskt_socket_read_pkt(self->mStreamSock, self->mRxPkt); + if (res < 0) + break; + + /* Discard any data received before starting a vstrm_receiver */ + if (!self->mReceiver) + continue; + + /* Something read? */ + res = tpkt_get_cdata(self->mRxPkt, nullptr, &readlen, nullptr); + if (res < 0) + break; + if (readlen != 0) { + /* Allocate new packet for replacement */ + struct tpkt_packet *newPkt = self->newRxPkt(); + if (!newPkt) { + PDRAW_LOG_ERRNO("newRxPkt", ENOMEM); + break; + } + /* Process received packet */ + res = vstrm_receiver_recv_data(self->mReceiver, + self->mRxPkt); + /* Replace processed packet with new one */ + tpkt_unref(self->mRxPkt); + self->mRxPkt = newPkt; + if (res < 0) + PDRAW_LOG_ERRNO("vstrm_receiver_recv_data", + -res); + } else { + /* TODO: EOF */ + break; + } + } +} + + +void StreamDemuxerMux::VideoMediaMux::ctrlCb(int fd, + uint32_t events, + void *userdata) +{ + VideoMediaMux *self = (VideoMediaMux *)userdata; + int res; + size_t readlen = 0; + + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + while (1) { + /* Read data */ + res = tskt_socket_read_pkt(self->mControlSock, self->mRxPkt); + if (res < 0) + break; + + /* Discard any data received before starting a vstrm_receiver */ + if (!self->mReceiver) + continue; + + /* Something read? */ + res = tpkt_get_cdata(self->mRxPkt, nullptr, &readlen, nullptr); + if (res < 0) + break; + if (readlen != 0) { + /* Allocate new packet for replacement */ + struct tpkt_packet *newPkt = self->newRxPkt(); + if (!newPkt) { + PDRAW_LOG_ERRNO("newRxPkt", ENOMEM); + break; + } + /* Process received packet */ + res = vstrm_receiver_recv_ctrl(self->mReceiver, + self->mRxPkt); + /* Replace processed packet with new one */ + tpkt_unref(self->mRxPkt); + self->mRxPkt = newPkt; + if (res < 0) + PDRAW_LOG_ERRNO("vstrm_receiver_recv_ctrl", + -res); + } else { + /* TODO: EOF */ + break; + } + } +} + +void StreamDemuxerMux::VideoMediaMux::callFinishSetup(void *userdata) +{ + VideoMediaMux *self = (VideoMediaMux *)userdata; + self->finishSetup(); +} + +void StreamDemuxerMux::VideoMediaMux::proxyOpenCb(struct mux_ip_proxy *proxy, + uint16_t localPort, + void *userdata) +{ + VideoMediaMux *self = (VideoMediaMux *)userdata; + if (proxy == self->mStreamProxy) { + self->mStreamProxyOpened = true; + } else if (proxy == self->mControlProxy) { + self->mControlProxyOpened = true; + } else { + PDRAW_LOGE("uknown proxy opened"); + return; + } + + if (self->mStreamProxyOpened && self->mControlProxyOpened) { + struct pomp_loop *loop = self->mDemuxerMux->mSession->getLoop(); + pomp_loop_idle_remove(loop, callFinishSetup, self); + pomp_loop_idle_add(loop, callFinishSetup, self); + } +} + +void StreamDemuxerMux::VideoMediaMux::proxyCloseCb(struct mux_ip_proxy *proxy, + void *userdata) +{ + VideoMediaMux *self = (VideoMediaMux *)userdata; + if (proxy == self->mStreamProxy) { + self->mStreamProxyOpened = false; + } else if (proxy == self->mControlProxy) { + self->mControlProxyOpened = false; + } else { + PDRAW_LOGE("uknown proxy closed"); + return; + } +} + +void StreamDemuxerMux::VideoMediaMux::proxyUpdateCb(struct mux_ip_proxy *proxy, + void *userdata) +{ + /* TODO ? */ +} + +void StreamDemuxerMux::VideoMediaMux::proxyFailedCb(struct mux_ip_proxy *proxy, + int err, + void *userdata) +{ + VideoMediaMux *self = (VideoMediaMux *)userdata; + const char *name = "unknown"; + if (proxy == self->mStreamProxy) + name = "stream"; + else if (proxy == self->mControlProxy) + name = "control"; + PDRAW_LOG_ERRNO("%s proxy failed to resolve", -err, name); +} + +} /* namespace Pdraw */ + +#endif /* BUILD_LIBMUX */ diff --git a/libpdraw/src/pdraw_demuxer_stream_mux.hpp b/libpdraw/src/pdraw_demuxer_stream_mux.hpp index d2aea6c..d855354 100644 --- a/libpdraw/src/pdraw_demuxer_stream_mux.hpp +++ b/libpdraw/src/pdraw_demuxer_stream_mux.hpp @@ -1,162 +1,162 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Streaming demuxer - mux implementation - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_DEMUXER_STREAM_MUX_HPP_ -#define _PDRAW_DEMUXER_STREAM_MUX_HPP_ - -#ifdef BUILD_LIBMUX - -# include "pdraw_demuxer_stream.hpp" - -# include -# include - -# include - -# include -# include - -namespace Pdraw { - -class StreamDemuxerMux : public StreamDemuxer { -public: - StreamDemuxerMux(Session *session, - Element::Listener *elementListener, - CodedSource::Listener *sourceListener, - IPdraw::IDemuxer *demuxer, - IPdraw::IDemuxer::Listener *demuxerListener, - const std::string &url, - struct mux_ctx *mux); - - ~StreamDemuxerMux(void); - -protected: - VideoMedia *createVideoMedia(void); - -private: - class VideoMediaMux : StreamDemuxer::VideoMedia { - public: - VideoMediaMux(StreamDemuxerMux *demuxer); - - ~VideoMediaMux(void); - - int startRtpAvp(void); - - int stopRtpAvp(void); - - int sendCtrl(struct vstrm_receiver *stream, - struct tpkt_packet *pkt); - - int prepareSetup(void); - - enum rtsp_lower_transport getLowerTransport(void); - - uint16_t getLocalStreamPort(void); - - uint16_t getLocalControlPort(void); - - uint16_t getRemoteStreamPort(void); - - uint16_t getRemoteControlPort(void); - - const struct rtsp_header_ext *getHeaderExt(void); - - size_t getHeaderExtCount(void); - - void setLocalStreamPort(uint16_t port); - - void setLocalControlPort(uint16_t port); - - void setRemoteStreamPort(uint16_t port); - - void setRemoteControlPort(uint16_t port); - - private: - static void legacyDataCb(struct mux_ctx *ctx, - uint32_t chanid, - enum mux_channel_event event, - struct pomp_buffer *buf, - void *userdata); - - static void legacyCtrlCb(struct mux_ctx *ctx, - uint32_t chanid, - enum mux_channel_event event, - struct pomp_buffer *buf, - void *userdata); - - int createSockets(void); - - void closeSockets(void); - - struct tpkt_packet *newRxPkt(void); - - static void dataCb(int fd, uint32_t events, void *userdata); - - static void ctrlCb(int fd, uint32_t events, void *userdata); - - static void callFinishSetup(void *userdata); - - static void proxyOpenCb(struct mux_ip_proxy *proxy, - uint16_t localPort, - void *userdata); - - static void proxyCloseCb(struct mux_ip_proxy *proxy, - void *userdata); - - static void proxyUpdateCb(struct mux_ip_proxy *proxy, - void *userdata); - - static void proxyFailedCb(struct mux_ip_proxy *proxy, - int err, - void *userdata); - - StreamDemuxerMux *mDemuxerMux; - struct tskt_socket *mStreamSock; - struct mux_ip_proxy *mStreamProxy; - bool mStreamProxyOpened; - struct tskt_socket *mControlSock; - struct mux_ip_proxy *mControlProxy; - bool mControlProxyOpened; - struct tpkt_packet *mRxPkt; - size_t mRxBufLen; - static const struct rtsp_header_ext mHeaderExt; - static const size_t mHeaderExtCount; - }; - - bool setMux(struct mux_ctx *mux); - - struct mux_ctx *mMux; -}; - -} /* namespace Pdraw */ - -#endif /* BUILD_LIBMUX */ - -#endif /* !_PDRAW_DEMUXER_STREAM_MUX_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Streaming demuxer - mux implementation + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_DEMUXER_STREAM_MUX_HPP_ +#define _PDRAW_DEMUXER_STREAM_MUX_HPP_ + +#ifdef BUILD_LIBMUX + +# include "pdraw_demuxer_stream.hpp" + +# include +# include + +# include + +# include +# include + +namespace Pdraw { + +class StreamDemuxerMux : public StreamDemuxer { +public: + StreamDemuxerMux(Session *session, + Element::Listener *elementListener, + CodedSource::Listener *sourceListener, + IPdraw::IDemuxer *demuxer, + IPdraw::IDemuxer::Listener *demuxerListener, + const std::string &url, + struct mux_ctx *mux); + + ~StreamDemuxerMux(void); + +protected: + VideoMedia *createVideoMedia(void); + +private: + class VideoMediaMux : StreamDemuxer::VideoMedia { + public: + VideoMediaMux(StreamDemuxerMux *demuxer); + + ~VideoMediaMux(void); + + int startRtpAvp(void); + + int stopRtpAvp(void); + + int sendCtrl(struct vstrm_receiver *stream, + struct tpkt_packet *pkt); + + int prepareSetup(void); + + enum rtsp_lower_transport getLowerTransport(void); + + uint16_t getLocalStreamPort(void); + + uint16_t getLocalControlPort(void); + + uint16_t getRemoteStreamPort(void); + + uint16_t getRemoteControlPort(void); + + const struct rtsp_header_ext *getHeaderExt(void); + + size_t getHeaderExtCount(void); + + void setLocalStreamPort(uint16_t port); + + void setLocalControlPort(uint16_t port); + + void setRemoteStreamPort(uint16_t port); + + void setRemoteControlPort(uint16_t port); + + private: + static void legacyDataCb(struct mux_ctx *ctx, + uint32_t chanid, + enum mux_channel_event event, + struct pomp_buffer *buf, + void *userdata); + + static void legacyCtrlCb(struct mux_ctx *ctx, + uint32_t chanid, + enum mux_channel_event event, + struct pomp_buffer *buf, + void *userdata); + + int createSockets(void); + + void closeSockets(void); + + struct tpkt_packet *newRxPkt(void); + + static void dataCb(int fd, uint32_t events, void *userdata); + + static void ctrlCb(int fd, uint32_t events, void *userdata); + + static void callFinishSetup(void *userdata); + + static void proxyOpenCb(struct mux_ip_proxy *proxy, + uint16_t localPort, + void *userdata); + + static void proxyCloseCb(struct mux_ip_proxy *proxy, + void *userdata); + + static void proxyUpdateCb(struct mux_ip_proxy *proxy, + void *userdata); + + static void proxyFailedCb(struct mux_ip_proxy *proxy, + int err, + void *userdata); + + StreamDemuxerMux *mDemuxerMux; + struct tskt_socket *mStreamSock; + struct mux_ip_proxy *mStreamProxy; + bool mStreamProxyOpened; + struct tskt_socket *mControlSock; + struct mux_ip_proxy *mControlProxy; + bool mControlProxyOpened; + struct tpkt_packet *mRxPkt; + size_t mRxBufLen; + static const struct rtsp_header_ext mHeaderExt; + static const size_t mHeaderExtCount; + }; + + bool setMux(struct mux_ctx *mux); + + struct mux_ctx *mMux; +}; + +} /* namespace Pdraw */ + +#endif /* BUILD_LIBMUX */ + +#endif /* !_PDRAW_DEMUXER_STREAM_MUX_HPP_ */ diff --git a/libpdraw/src/pdraw_demuxer_stream_net.cpp b/libpdraw/src/pdraw_demuxer_stream_net.cpp index 2b7530b..d1ae7c2 100644 --- a/libpdraw/src/pdraw_demuxer_stream_net.cpp +++ b/libpdraw/src/pdraw_demuxer_stream_net.cpp @@ -1,533 +1,533 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Streaming demuxer - net implementation - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_dmxstrmnet -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_demuxer_stream_net.hpp" -#include "pdraw_session.hpp" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#define DEFAULT_RX_BUFFER_SIZE 1500 - - -namespace Pdraw { - - -StreamDemuxerNet::StreamDemuxerNet(Session *session, - Element::Listener *elementListener, - CodedSource::Listener *sourceListener, - IPdraw::IDemuxer *demuxer, - IPdraw::IDemuxer::Listener *demuxerListener, - const std::string &url) : - StreamDemuxer(session, - elementListener, - sourceListener, - demuxer, - demuxerListener), - mSingleLocalStreamPort(0), mSingleLocalControlPort(0), - mSingleRemoteStreamPort(0), mSingleRemoteControlPort(0) -{ - Element::setClassName(__func__); - - mUrl = url; - - setState(CREATED); -} - - -StreamDemuxerNet::StreamDemuxerNet(Session *session, - Element::Listener *elementListener, - CodedSource::Listener *sourceListener, - IPdraw::IDemuxer *demuxer, - IPdraw::IDemuxer::Listener *demuxerListener, - const std::string &localAddr, - uint16_t localStreamPort, - uint16_t localControlPort, - const std::string &remoteAddr, - uint16_t remoteStreamPort, - uint16_t remoteControlPort) : - StreamDemuxer(session, - elementListener, - sourceListener, - demuxer, - demuxerListener), - mSingleLocalStreamPort(localStreamPort), - mSingleLocalControlPort(localControlPort), - mSingleRemoteStreamPort(remoteStreamPort), - mSingleRemoteControlPort(remoteControlPort) -{ - Element::setClassName(__func__); - - mLocalAddr = (localAddr.length() > 0) ? localAddr : "0.0.0.0"; - mRemoteAddr = (remoteAddr.length() > 0) ? remoteAddr : "0.0.0.0"; - - setState(CREATED); -} - - -StreamDemuxerNet::~StreamDemuxerNet(void) -{ - return; -} - - -uint16_t StreamDemuxerNet::getSingleStreamLocalStreamPort(void) -{ - if (mState != STARTED) { - PDRAW_LOG_ERRNO("demuxer is not started", EPROTO); - return 0; - } - if (mVideoMedias.size() != 1) { - PDRAW_LOG_ERRNO("invalid media count", EPROTO); - return 0; - } - - VideoMediaNet *media = - dynamic_cast(mVideoMedias.front()); - if (media == nullptr) { - PDRAW_LOG_ERRNO("invalid media", EPROTO); - return 0; - } - - return media->getLocalStreamPort(); -} - - -uint16_t StreamDemuxerNet::getSingleStreamLocalControlPort(void) -{ - if (mState != STARTED) { - PDRAW_LOG_ERRNO("demuxer is not started", EPROTO); - return 0; - } - if (mVideoMedias.size() != 1) { - PDRAW_LOG_ERRNO("invalid media count", EPROTO); - return 0; - } - - VideoMediaNet *media = - dynamic_cast(mVideoMedias.front()); - if (media == nullptr) { - PDRAW_LOG_ERRNO("invalid media", EPROTO); - return 0; - } - - return media->getLocalControlPort(); -} - - -StreamDemuxer::VideoMedia *StreamDemuxerNet::createVideoMedia(void) -{ - return (StreamDemuxer::VideoMedia *)new VideoMediaNet(this); -} - - -StreamDemuxerNet::VideoMediaNet::VideoMediaNet(StreamDemuxerNet *demuxer) : - VideoMedia(demuxer), mDemuxerNet(demuxer), mStreamSock(nullptr), - mControlSock(nullptr), mRxPkt(nullptr), mRxBufLen(0) -{ -} - - -StreamDemuxerNet::VideoMediaNet::~VideoMediaNet(void) -{ - stopRtpAvp(); - tpkt_unref(mRxPkt); -} - - -int StreamDemuxerNet::VideoMediaNet::startRtpAvp(void) -{ - int res; - - /* Create sockets only if not created during prepareSetup */ - if (mStreamSock == nullptr && mControlSock == nullptr) { - res = createSockets(); - if (res != 0) { - PDRAW_LOG_ERRNO("createSockets", -res); - goto error; - } - } else if ((mStreamSock != nullptr && mControlSock == nullptr) || - (mStreamSock == nullptr && mControlSock != nullptr)) { - PDRAW_LOGE("bad state, only one socket created !"); - res = -EPROTO; - goto error; - } - - PDRAW_LOGD("startRtpAvp localStreamPort=%d localControlPort=%d", - mLocalStreamPort, - mLocalControlPort); - - /* Create the stream receiver */ - res = createReceiver(); - if (res < 0) { - PDRAW_LOG_ERRNO("createReceiver", -res); - goto error; - } - - return 0; - -error: - stopRtpAvp(); - return res; -} - - -int StreamDemuxerNet::VideoMediaNet::stopRtpAvp(void) -{ - int err; - PDRAW_LOGD("stopRtpAvp"); - destroyReceiver(); - err = tskt_socket_destroy(mStreamSock); - if (err < 0) - PDRAW_LOG_ERRNO("tskt_socket_destroy", -err); - mStreamSock = nullptr; - err = tskt_socket_destroy(mControlSock); - if (err < 0) - PDRAW_LOG_ERRNO("tskt_socket_destroy", -err); - mControlSock = nullptr; - tpkt_unref(mRxPkt); - mRxPkt = nullptr; - return 0; -} - - -int StreamDemuxerNet::VideoMediaNet::sendCtrl(struct vstrm_receiver *stream, - struct tpkt_packet *pkt) -{ - int res; - - PDRAW_LOG_ERRNO_RETURN_ERR_IF(pkt == nullptr, EINVAL); - - /* Write data */ - res = tskt_socket_write_pkt(mControlSock, pkt); - if (res < 0) - PDRAW_LOG_ERRNO("tskt_socket_write_pkt", -res); - - return res; -} - - -int StreamDemuxerNet::VideoMediaNet::prepareSetup(void) -{ - int res = createSockets(); - if (res != 0) { - PDRAW_LOG_ERRNO("createSockets", -res); - return res; - } - - return 0; -} - - -enum rtsp_lower_transport -StreamDemuxerNet::VideoMediaNet::getLowerTransport(void) -{ - return RTSP_LOWER_TRANSPORT_UDP; -} - - -uint16_t StreamDemuxerNet::VideoMediaNet::getLocalStreamPort(void) -{ - if (mStreamSock == nullptr) { - PDRAW_LOG_ERRNO("invalid stream socket", EPROTO); - return 0; - } - - return tskt_socket_get_local_port(mStreamSock); -} - - -uint16_t StreamDemuxerNet::VideoMediaNet::getLocalControlPort(void) -{ - if (mControlSock == nullptr) { - PDRAW_LOG_ERRNO("invalid control socket", EPROTO); - return 0; - } - - return tskt_socket_get_local_port(mControlSock); -} - - -uint16_t StreamDemuxerNet::VideoMediaNet::getRemoteStreamPort(void) -{ - if (mStreamSock == nullptr) { - PDRAW_LOG_ERRNO("invalid stream socket", EPROTO); - return 0; - } - - return tskt_socket_get_remote_port(mStreamSock); -} - - -uint16_t StreamDemuxerNet::VideoMediaNet::getRemoteControlPort(void) -{ - if (mControlSock == nullptr) { - PDRAW_LOG_ERRNO("invalid control socket", EPROTO); - return 0; - } - - return tskt_socket_get_remote_port(mControlSock); -} - - -void StreamDemuxerNet::VideoMediaNet::setLocalStreamPort(uint16_t port) -{ - mLocalStreamPort = port; -} - - -void StreamDemuxerNet::VideoMediaNet::setLocalControlPort(uint16_t port) -{ - mLocalControlPort = port; -} - - -void StreamDemuxerNet::VideoMediaNet::setRemoteStreamPort(uint16_t port) -{ - mRemoteStreamPort = port; - if (mStreamSock != nullptr) { - int res = - tskt_socket_set_remote(mStreamSock, - mDemuxerNet->mRemoteAddr.c_str(), - mRemoteStreamPort); - if (res < 0) - PDRAW_LOG_ERRNO("tskt_socket_set_remote", -res); - } -} - - -void StreamDemuxerNet::VideoMediaNet::setRemoteControlPort(uint16_t port) -{ - mRemoteControlPort = port; - if (mControlSock != nullptr) { - int res = - tskt_socket_set_remote(mControlSock, - mDemuxerNet->mRemoteAddr.c_str(), - mRemoteControlPort); - if (res < 0) - PDRAW_LOG_ERRNO("tskt_socket_set_remote", -res); - } -} - - -int StreamDemuxerNet::VideoMediaNet::createSockets(void) -{ - int res, err; - if (mLocalStreamPort == 0) - mLocalStreamPort = DEMUXER_STREAM_DEFAULT_LOCAL_STREAM_PORT; - if (mLocalControlPort == 0) - mLocalControlPort = DEMUXER_STREAM_DEFAULT_LOCAL_CONTROL_PORT; - - /* Create the rx buffer */ - mRxBufLen = DEFAULT_RX_BUFFER_SIZE; - mRxPkt = newRxPkt(); - if (mRxPkt == nullptr) { - res = -ENOMEM; - PDRAW_LOG_ERRNO("newRxPkt", -res); - goto error; - } - - /* Create the sockets */ - res = tskt_socket_new(mDemuxerNet->mLocalAddr.c_str(), - &mLocalStreamPort, - mDemuxerNet->mRemoteAddr.c_str(), - mRemoteStreamPort, - nullptr, - mDemuxerNet->mSession->getLoop(), - dataCb, - this, - &mStreamSock); - if (res < 0) { - PDRAW_LOG_ERRNO("tskt_socket_new:stream", -res); - goto error; - } - res = tskt_socket_set_class_selector(mStreamSock, - IPTOS_PREC_FLASHOVERRIDE); - if (res < 0) - PDRAW_LOGW("failed to set class selector for stream socket"); - - res = tskt_socket_new(mDemuxerNet->mLocalAddr.c_str(), - &mLocalControlPort, - mDemuxerNet->mRemoteAddr.c_str(), - mRemoteControlPort, - nullptr, - mDemuxerNet->mSession->getLoop(), - ctrlCb, - this, - &mControlSock); - if (res < 0) { - PDRAW_LOG_ERRNO("tskt_socket_new:control", -res); - goto error; - } - res = tskt_socket_set_class_selector(mControlSock, - IPTOS_PREC_FLASHOVERRIDE); - if (res < 0) - PDRAW_LOGW("failed to set class selector for control socket"); - - return 0; - -error: - err = tskt_socket_destroy(mStreamSock); - if (err < 0) - PDRAW_LOG_ERRNO("tskt_socket_destroy", -err); - mStreamSock = nullptr; - err = tskt_socket_destroy(mControlSock); - if (err < 0) - PDRAW_LOG_ERRNO("tskt_socket_destroy", -err); - mControlSock = nullptr; - tpkt_unref(mRxPkt); - mRxPkt = nullptr; - return res; -} - - -struct tpkt_packet *StreamDemuxerNet::VideoMediaNet::newRxPkt(void) -{ - struct pomp_buffer *buf = pomp_buffer_new(mRxBufLen); - if (!buf) - return nullptr; - - struct tpkt_packet *pkt; - int res = tpkt_new_from_buffer(buf, &pkt); - pomp_buffer_unref(buf); - if (res < 0) - return nullptr; - - return pkt; -} - - -void StreamDemuxerNet::VideoMediaNet::dataCb(int fd, - uint32_t events, - void *userdata) -{ - VideoMediaNet *self = (VideoMediaNet *)userdata; - int res; - size_t readlen = 0; - - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - while (1) { - /* Read data */ - res = tskt_socket_read_pkt(self->mStreamSock, self->mRxPkt); - if (res < 0) - break; - - /* Discard any data received before starting a vstrm_receiver */ - if (!self->mReceiver) - continue; - - /* Something read? */ - res = tpkt_get_cdata(self->mRxPkt, nullptr, &readlen, nullptr); - if (res < 0) - break; - if (readlen != 0) { - /* Allocate new packet for replacement */ - struct tpkt_packet *newPkt = self->newRxPkt(); - if (!newPkt) { - PDRAW_LOG_ERRNO("newRxPkt", ENOMEM); - break; - } - /* Process received packet */ - res = vstrm_receiver_recv_data(self->mReceiver, - self->mRxPkt); - /* Replace processed packet with new one */ - tpkt_unref(self->mRxPkt); - self->mRxPkt = newPkt; - if (res < 0) - PDRAW_LOG_ERRNO("vstrm_receiver_recv_data", - -res); - } else { - /* TODO: EOF */ - break; - } - } -} - - -void StreamDemuxerNet::VideoMediaNet::ctrlCb(int fd, - uint32_t events, - void *userdata) -{ - VideoMediaNet *self = (VideoMediaNet *)userdata; - int res; - size_t readlen = 0; - - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - while (1) { - /* Read data */ - res = tskt_socket_read_pkt(self->mControlSock, self->mRxPkt); - if (res < 0) - break; - - /* Discard any data received before starting a vstrm_receiver */ - if (!self->mReceiver) - continue; - - /* Something read? */ - res = tpkt_get_cdata(self->mRxPkt, nullptr, &readlen, nullptr); - if (res < 0) - break; - if (readlen != 0) { - /* Allocate new packet for replacement */ - struct tpkt_packet *newPkt = self->newRxPkt(); - if (!newPkt) { - PDRAW_LOG_ERRNO("newRxPkt", ENOMEM); - break; - } - /* Process received packet */ - res = vstrm_receiver_recv_ctrl(self->mReceiver, - self->mRxPkt); - /* Replace processed packet with new one */ - tpkt_unref(self->mRxPkt); - self->mRxPkt = newPkt; - if (res < 0) - PDRAW_LOG_ERRNO("vstrm_receiver_recv_ctrl", - -res); - } else { - /* TODO: EOF */ - break; - } - } -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Streaming demuxer - net implementation + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_dmxstrmnet +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_demuxer_stream_net.hpp" +#include "pdraw_session.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DEFAULT_RX_BUFFER_SIZE 1500 + + +namespace Pdraw { + + +StreamDemuxerNet::StreamDemuxerNet(Session *session, + Element::Listener *elementListener, + CodedSource::Listener *sourceListener, + IPdraw::IDemuxer *demuxer, + IPdraw::IDemuxer::Listener *demuxerListener, + const std::string &url) : + StreamDemuxer(session, + elementListener, + sourceListener, + demuxer, + demuxerListener), + mSingleLocalStreamPort(0), mSingleLocalControlPort(0), + mSingleRemoteStreamPort(0), mSingleRemoteControlPort(0) +{ + Element::setClassName(__func__); + + mUrl = url; + + setState(CREATED); +} + + +StreamDemuxerNet::StreamDemuxerNet(Session *session, + Element::Listener *elementListener, + CodedSource::Listener *sourceListener, + IPdraw::IDemuxer *demuxer, + IPdraw::IDemuxer::Listener *demuxerListener, + const std::string &localAddr, + uint16_t localStreamPort, + uint16_t localControlPort, + const std::string &remoteAddr, + uint16_t remoteStreamPort, + uint16_t remoteControlPort) : + StreamDemuxer(session, + elementListener, + sourceListener, + demuxer, + demuxerListener), + mSingleLocalStreamPort(localStreamPort), + mSingleLocalControlPort(localControlPort), + mSingleRemoteStreamPort(remoteStreamPort), + mSingleRemoteControlPort(remoteControlPort) +{ + Element::setClassName(__func__); + + mLocalAddr = (localAddr.length() > 0) ? localAddr : "0.0.0.0"; + mRemoteAddr = (remoteAddr.length() > 0) ? remoteAddr : "0.0.0.0"; + + setState(CREATED); +} + + +StreamDemuxerNet::~StreamDemuxerNet(void) +{ + return; +} + + +uint16_t StreamDemuxerNet::getSingleStreamLocalStreamPort(void) +{ + if (mState != STARTED) { + PDRAW_LOG_ERRNO("demuxer is not started", EPROTO); + return 0; + } + if (mVideoMedias.size() != 1) { + PDRAW_LOG_ERRNO("invalid media count", EPROTO); + return 0; + } + + VideoMediaNet *media = + dynamic_cast(mVideoMedias.front()); + if (media == nullptr) { + PDRAW_LOG_ERRNO("invalid media", EPROTO); + return 0; + } + + return media->getLocalStreamPort(); +} + + +uint16_t StreamDemuxerNet::getSingleStreamLocalControlPort(void) +{ + if (mState != STARTED) { + PDRAW_LOG_ERRNO("demuxer is not started", EPROTO); + return 0; + } + if (mVideoMedias.size() != 1) { + PDRAW_LOG_ERRNO("invalid media count", EPROTO); + return 0; + } + + VideoMediaNet *media = + dynamic_cast(mVideoMedias.front()); + if (media == nullptr) { + PDRAW_LOG_ERRNO("invalid media", EPROTO); + return 0; + } + + return media->getLocalControlPort(); +} + + +StreamDemuxer::VideoMedia *StreamDemuxerNet::createVideoMedia(void) +{ + return (StreamDemuxer::VideoMedia *)new VideoMediaNet(this); +} + + +StreamDemuxerNet::VideoMediaNet::VideoMediaNet(StreamDemuxerNet *demuxer) : + VideoMedia(demuxer), mDemuxerNet(demuxer), mStreamSock(nullptr), + mControlSock(nullptr), mRxPkt(nullptr), mRxBufLen(0) +{ +} + + +StreamDemuxerNet::VideoMediaNet::~VideoMediaNet(void) +{ + stopRtpAvp(); + tpkt_unref(mRxPkt); +} + + +int StreamDemuxerNet::VideoMediaNet::startRtpAvp(void) +{ + int res; + + /* Create sockets only if not created during prepareSetup */ + if (mStreamSock == nullptr && mControlSock == nullptr) { + res = createSockets(); + if (res != 0) { + PDRAW_LOG_ERRNO("createSockets", -res); + goto error; + } + } else if ((mStreamSock != nullptr && mControlSock == nullptr) || + (mStreamSock == nullptr && mControlSock != nullptr)) { + PDRAW_LOGE("bad state, only one socket created !"); + res = -EPROTO; + goto error; + } + + PDRAW_LOGD("startRtpAvp localStreamPort=%d localControlPort=%d", + mLocalStreamPort, + mLocalControlPort); + + /* Create the stream receiver */ + res = createReceiver(); + if (res < 0) { + PDRAW_LOG_ERRNO("createReceiver", -res); + goto error; + } + + return 0; + +error: + stopRtpAvp(); + return res; +} + + +int StreamDemuxerNet::VideoMediaNet::stopRtpAvp(void) +{ + int err; + PDRAW_LOGD("stopRtpAvp"); + destroyReceiver(); + err = tskt_socket_destroy(mStreamSock); + if (err < 0) + PDRAW_LOG_ERRNO("tskt_socket_destroy", -err); + mStreamSock = nullptr; + err = tskt_socket_destroy(mControlSock); + if (err < 0) + PDRAW_LOG_ERRNO("tskt_socket_destroy", -err); + mControlSock = nullptr; + tpkt_unref(mRxPkt); + mRxPkt = nullptr; + return 0; +} + + +int StreamDemuxerNet::VideoMediaNet::sendCtrl(struct vstrm_receiver *stream, + struct tpkt_packet *pkt) +{ + int res; + + PDRAW_LOG_ERRNO_RETURN_ERR_IF(pkt == nullptr, EINVAL); + + /* Write data */ + res = tskt_socket_write_pkt(mControlSock, pkt); + if (res < 0) + PDRAW_LOG_ERRNO("tskt_socket_write_pkt", -res); + + return res; +} + + +int StreamDemuxerNet::VideoMediaNet::prepareSetup(void) +{ + int res = createSockets(); + if (res != 0) { + PDRAW_LOG_ERRNO("createSockets", -res); + return res; + } + + return 0; +} + + +enum rtsp_lower_transport +StreamDemuxerNet::VideoMediaNet::getLowerTransport(void) +{ + return RTSP_LOWER_TRANSPORT_UDP; +} + + +uint16_t StreamDemuxerNet::VideoMediaNet::getLocalStreamPort(void) +{ + if (mStreamSock == nullptr) { + PDRAW_LOG_ERRNO("invalid stream socket", EPROTO); + return 0; + } + + return tskt_socket_get_local_port(mStreamSock); +} + + +uint16_t StreamDemuxerNet::VideoMediaNet::getLocalControlPort(void) +{ + if (mControlSock == nullptr) { + PDRAW_LOG_ERRNO("invalid control socket", EPROTO); + return 0; + } + + return tskt_socket_get_local_port(mControlSock); +} + + +uint16_t StreamDemuxerNet::VideoMediaNet::getRemoteStreamPort(void) +{ + if (mStreamSock == nullptr) { + PDRAW_LOG_ERRNO("invalid stream socket", EPROTO); + return 0; + } + + return tskt_socket_get_remote_port(mStreamSock); +} + + +uint16_t StreamDemuxerNet::VideoMediaNet::getRemoteControlPort(void) +{ + if (mControlSock == nullptr) { + PDRAW_LOG_ERRNO("invalid control socket", EPROTO); + return 0; + } + + return tskt_socket_get_remote_port(mControlSock); +} + + +void StreamDemuxerNet::VideoMediaNet::setLocalStreamPort(uint16_t port) +{ + mLocalStreamPort = port; +} + + +void StreamDemuxerNet::VideoMediaNet::setLocalControlPort(uint16_t port) +{ + mLocalControlPort = port; +} + + +void StreamDemuxerNet::VideoMediaNet::setRemoteStreamPort(uint16_t port) +{ + mRemoteStreamPort = port; + if (mStreamSock != nullptr) { + int res = + tskt_socket_set_remote(mStreamSock, + mDemuxerNet->mRemoteAddr.c_str(), + mRemoteStreamPort); + if (res < 0) + PDRAW_LOG_ERRNO("tskt_socket_set_remote", -res); + } +} + + +void StreamDemuxerNet::VideoMediaNet::setRemoteControlPort(uint16_t port) +{ + mRemoteControlPort = port; + if (mControlSock != nullptr) { + int res = + tskt_socket_set_remote(mControlSock, + mDemuxerNet->mRemoteAddr.c_str(), + mRemoteControlPort); + if (res < 0) + PDRAW_LOG_ERRNO("tskt_socket_set_remote", -res); + } +} + + +int StreamDemuxerNet::VideoMediaNet::createSockets(void) +{ + int res, err; + if (mLocalStreamPort == 0) + mLocalStreamPort = DEMUXER_STREAM_DEFAULT_LOCAL_STREAM_PORT; + if (mLocalControlPort == 0) + mLocalControlPort = DEMUXER_STREAM_DEFAULT_LOCAL_CONTROL_PORT; + + /* Create the rx buffer */ + mRxBufLen = DEFAULT_RX_BUFFER_SIZE; + mRxPkt = newRxPkt(); + if (mRxPkt == nullptr) { + res = -ENOMEM; + PDRAW_LOG_ERRNO("newRxPkt", -res); + goto error; + } + + /* Create the sockets */ + res = tskt_socket_new(mDemuxerNet->mLocalAddr.c_str(), + &mLocalStreamPort, + mDemuxerNet->mRemoteAddr.c_str(), + mRemoteStreamPort, + nullptr, + mDemuxerNet->mSession->getLoop(), + dataCb, + this, + &mStreamSock); + if (res < 0) { + PDRAW_LOG_ERRNO("tskt_socket_new:stream", -res); + goto error; + } + res = tskt_socket_set_class_selector(mStreamSock, + IPTOS_PREC_FLASHOVERRIDE); + if (res < 0) + PDRAW_LOGW("failed to set class selector for stream socket"); + + res = tskt_socket_new(mDemuxerNet->mLocalAddr.c_str(), + &mLocalControlPort, + mDemuxerNet->mRemoteAddr.c_str(), + mRemoteControlPort, + nullptr, + mDemuxerNet->mSession->getLoop(), + ctrlCb, + this, + &mControlSock); + if (res < 0) { + PDRAW_LOG_ERRNO("tskt_socket_new:control", -res); + goto error; + } + res = tskt_socket_set_class_selector(mControlSock, + IPTOS_PREC_FLASHOVERRIDE); + if (res < 0) + PDRAW_LOGW("failed to set class selector for control socket"); + + return 0; + +error: + err = tskt_socket_destroy(mStreamSock); + if (err < 0) + PDRAW_LOG_ERRNO("tskt_socket_destroy", -err); + mStreamSock = nullptr; + err = tskt_socket_destroy(mControlSock); + if (err < 0) + PDRAW_LOG_ERRNO("tskt_socket_destroy", -err); + mControlSock = nullptr; + tpkt_unref(mRxPkt); + mRxPkt = nullptr; + return res; +} + + +struct tpkt_packet *StreamDemuxerNet::VideoMediaNet::newRxPkt(void) +{ + struct pomp_buffer *buf = pomp_buffer_new(mRxBufLen); + if (!buf) + return nullptr; + + struct tpkt_packet *pkt; + int res = tpkt_new_from_buffer(buf, &pkt); + pomp_buffer_unref(buf); + if (res < 0) + return nullptr; + + return pkt; +} + + +void StreamDemuxerNet::VideoMediaNet::dataCb(int fd, + uint32_t events, + void *userdata) +{ + VideoMediaNet *self = (VideoMediaNet *)userdata; + int res; + size_t readlen = 0; + + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + while (1) { + /* Read data */ + res = tskt_socket_read_pkt(self->mStreamSock, self->mRxPkt); + if (res < 0) + break; + + /* Discard any data received before starting a vstrm_receiver */ + if (!self->mReceiver) + continue; + + /* Something read? */ + res = tpkt_get_cdata(self->mRxPkt, nullptr, &readlen, nullptr); + if (res < 0) + break; + if (readlen != 0) { + /* Allocate new packet for replacement */ + struct tpkt_packet *newPkt = self->newRxPkt(); + if (!newPkt) { + PDRAW_LOG_ERRNO("newRxPkt", ENOMEM); + break; + } + /* Process received packet */ + res = vstrm_receiver_recv_data(self->mReceiver, + self->mRxPkt); + /* Replace processed packet with new one */ + tpkt_unref(self->mRxPkt); + self->mRxPkt = newPkt; + if (res < 0) + PDRAW_LOG_ERRNO("vstrm_receiver_recv_data", + -res); + } else { + /* TODO: EOF */ + break; + } + } +} + + +void StreamDemuxerNet::VideoMediaNet::ctrlCb(int fd, + uint32_t events, + void *userdata) +{ + VideoMediaNet *self = (VideoMediaNet *)userdata; + int res; + size_t readlen = 0; + + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + while (1) { + /* Read data */ + res = tskt_socket_read_pkt(self->mControlSock, self->mRxPkt); + if (res < 0) + break; + + /* Discard any data received before starting a vstrm_receiver */ + if (!self->mReceiver) + continue; + + /* Something read? */ + res = tpkt_get_cdata(self->mRxPkt, nullptr, &readlen, nullptr); + if (res < 0) + break; + if (readlen != 0) { + /* Allocate new packet for replacement */ + struct tpkt_packet *newPkt = self->newRxPkt(); + if (!newPkt) { + PDRAW_LOG_ERRNO("newRxPkt", ENOMEM); + break; + } + /* Process received packet */ + res = vstrm_receiver_recv_ctrl(self->mReceiver, + self->mRxPkt); + /* Replace processed packet with new one */ + tpkt_unref(self->mRxPkt); + self->mRxPkt = newPkt; + if (res < 0) + PDRAW_LOG_ERRNO("vstrm_receiver_recv_ctrl", + -res); + } else { + /* TODO: EOF */ + break; + } + } +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_demuxer_stream_net.hpp b/libpdraw/src/pdraw_demuxer_stream_net.hpp index 5e9f401..e0967c7 100644 --- a/libpdraw/src/pdraw_demuxer_stream_net.hpp +++ b/libpdraw/src/pdraw_demuxer_stream_net.hpp @@ -1,131 +1,131 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Streaming demuxer - net implementation - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_DEMUXER_STREAM_NET_HPP_ -#define _PDRAW_DEMUXER_STREAM_NET_HPP_ - -#include "pdraw_demuxer_stream.hpp" - -#include -#include - -#include - -namespace Pdraw { - -class StreamDemuxerNet : public StreamDemuxer { -public: - StreamDemuxerNet(Session *session, - Element::Listener *elementListener, - CodedSource::Listener *sourceListener, - IPdraw::IDemuxer *demuxer, - IPdraw::IDemuxer::Listener *demuxerListener, - const std::string &url); - - StreamDemuxerNet(Session *session, - Element::Listener *elementListener, - CodedSource::Listener *sourceListener, - IPdraw::IDemuxer *demuxer, - IPdraw::IDemuxer::Listener *demuxerListener, - const std::string &localAddr, - uint16_t localStreamPort, - uint16_t localControlPort, - const std::string &remoteAddr, - uint16_t remoteStreamPort, - uint16_t remoteControlPort); - - ~StreamDemuxerNet(void); - - uint16_t getSingleStreamLocalStreamPort(void); - - uint16_t getSingleStreamLocalControlPort(void); - -protected: - VideoMedia *createVideoMedia(void); - -private: - class VideoMediaNet : StreamDemuxer::VideoMedia { - public: - VideoMediaNet(StreamDemuxerNet *demuxer); - - ~VideoMediaNet(void); - - int startRtpAvp(void); - - int stopRtpAvp(void); - - int sendCtrl(struct vstrm_receiver *stream, - struct tpkt_packet *pkt); - - int prepareSetup(void); - - enum rtsp_lower_transport getLowerTransport(void); - - uint16_t getLocalStreamPort(void); - - uint16_t getLocalControlPort(void); - - uint16_t getRemoteStreamPort(void); - - uint16_t getRemoteControlPort(void); - - void setLocalStreamPort(uint16_t port); - - void setLocalControlPort(uint16_t port); - - void setRemoteStreamPort(uint16_t port); - - void setRemoteControlPort(uint16_t port); - - private: - int createSockets(void); - - struct tpkt_packet *newRxPkt(void); - - static void dataCb(int fd, uint32_t events, void *userdata); - - static void ctrlCb(int fd, uint32_t events, void *userdata); - - StreamDemuxerNet *mDemuxerNet; - struct tskt_socket *mStreamSock; - struct tskt_socket *mControlSock; - struct tpkt_packet *mRxPkt; - size_t mRxBufLen; - }; - - uint16_t mSingleLocalStreamPort; - uint16_t mSingleLocalControlPort; - uint16_t mSingleRemoteStreamPort; - uint16_t mSingleRemoteControlPort; -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_DEMUXER_STREAM_NET_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Streaming demuxer - net implementation + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_DEMUXER_STREAM_NET_HPP_ +#define _PDRAW_DEMUXER_STREAM_NET_HPP_ + +#include "pdraw_demuxer_stream.hpp" + +#include +#include + +#include + +namespace Pdraw { + +class StreamDemuxerNet : public StreamDemuxer { +public: + StreamDemuxerNet(Session *session, + Element::Listener *elementListener, + CodedSource::Listener *sourceListener, + IPdraw::IDemuxer *demuxer, + IPdraw::IDemuxer::Listener *demuxerListener, + const std::string &url); + + StreamDemuxerNet(Session *session, + Element::Listener *elementListener, + CodedSource::Listener *sourceListener, + IPdraw::IDemuxer *demuxer, + IPdraw::IDemuxer::Listener *demuxerListener, + const std::string &localAddr, + uint16_t localStreamPort, + uint16_t localControlPort, + const std::string &remoteAddr, + uint16_t remoteStreamPort, + uint16_t remoteControlPort); + + ~StreamDemuxerNet(void); + + uint16_t getSingleStreamLocalStreamPort(void); + + uint16_t getSingleStreamLocalControlPort(void); + +protected: + VideoMedia *createVideoMedia(void); + +private: + class VideoMediaNet : StreamDemuxer::VideoMedia { + public: + VideoMediaNet(StreamDemuxerNet *demuxer); + + ~VideoMediaNet(void); + + int startRtpAvp(void); + + int stopRtpAvp(void); + + int sendCtrl(struct vstrm_receiver *stream, + struct tpkt_packet *pkt); + + int prepareSetup(void); + + enum rtsp_lower_transport getLowerTransport(void); + + uint16_t getLocalStreamPort(void); + + uint16_t getLocalControlPort(void); + + uint16_t getRemoteStreamPort(void); + + uint16_t getRemoteControlPort(void); + + void setLocalStreamPort(uint16_t port); + + void setLocalControlPort(uint16_t port); + + void setRemoteStreamPort(uint16_t port); + + void setRemoteControlPort(uint16_t port); + + private: + int createSockets(void); + + struct tpkt_packet *newRxPkt(void); + + static void dataCb(int fd, uint32_t events, void *userdata); + + static void ctrlCb(int fd, uint32_t events, void *userdata); + + StreamDemuxerNet *mDemuxerNet; + struct tskt_socket *mStreamSock; + struct tskt_socket *mControlSock; + struct tpkt_packet *mRxPkt; + size_t mRxBufLen; + }; + + uint16_t mSingleLocalStreamPort; + uint16_t mSingleLocalControlPort; + uint16_t mSingleRemoteStreamPort; + uint16_t mSingleRemoteControlPort; +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_DEMUXER_STREAM_NET_HPP_ */ diff --git a/libpdraw/src/pdraw_element.cpp b/libpdraw/src/pdraw_element.cpp index b0f7500..2267c71 100644 --- a/libpdraw/src/pdraw_element.cpp +++ b/libpdraw/src/pdraw_element.cpp @@ -1,632 +1,632 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Pipeline element - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_elmt -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_element.hpp" - -#include - -namespace Pdraw { - - -std::atomic Element::mIdCounter(0); - - -Element::Element(Session *session, Listener *listener) : - mSession(session), mListener(listener), mState(INVALID) -{ - int res; - pthread_mutexattr_t attr; - bool attr_created = false; - - mId = ++mIdCounter; - std::string name = std::string(__func__) + "#" + std::to_string(mId); - Loggable::setName(name); - - res = pthread_mutexattr_init(&attr); - if (res != 0) { - PDRAW_LOG_ERRNO("pthread_mutexattr_init", res); - goto out; - } - attr_created = true; - - res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - if (res != 0) { - PDRAW_LOG_ERRNO("pthread_mutexattr_settype", res); - goto out; - } - - res = pthread_mutex_init(&mMutex, &attr); - if (res != 0) { - PDRAW_LOG_ERRNO("pthread_mutex_init", res); - goto out; - } - -out: - if (attr_created) - pthread_mutexattr_destroy(&attr); -} - - -Element::~Element(void) -{ - mState = INVALID; - pthread_mutex_destroy(&mMutex); - PDRAW_LOGI("element DESTROYED"); -} - - -void Element::lock(void) -{ - pthread_mutex_lock(&mMutex); -} - - -void Element::unlock(void) -{ - pthread_mutex_unlock(&mMutex); -} - - -unsigned int Element::getId(void) -{ - return mId; -} - - -void Element::setClassName(std::string &name) -{ - std::string new_name = name + "#" + std::to_string(mId); - Loggable::setName(new_name); -} - - -void Element::setClassName(const char *name) -{ - std::string new_name = std::string(name) + "#" + std::to_string(mId); - Loggable::setName(new_name); -} - - -Element::State Element::getState(void) -{ - pthread_mutex_lock(&mMutex); - Element::State ret = mState; - pthread_mutex_unlock(&mMutex); - return ret; -} - - -void Element::setState(Element::State state) -{ - pthread_mutex_lock(&mMutex); - if (state == mState) { - pthread_mutex_unlock(&mMutex); - return; - } - - mState = state; - pthread_mutex_unlock(&mMutex); - PDRAW_LOGI("element state change to %s", getElementStateStr(state)); - - if (mListener) - mListener->onElementStateChanged(this, state); -} - - -void Element::setStateAsyncNotify(Element::State state) -{ - pthread_mutex_lock(&mMutex); - if (state == mState) { - pthread_mutex_unlock(&mMutex); - return; - } - - mState = state; - pthread_mutex_unlock(&mMutex); - PDRAW_LOGI("element state change to %s (async notify)", - getElementStateStr(state)); - - if (mListener) - mListener->asyncElementStateChange(this, state); -} - - -const char *Element::getElementStateStr(Element::State val) -{ - switch (val) { - case Element::State::INVALID: - return "INVALID"; - case Element::State::CREATED: - return "CREATED"; - case Element::State::STARTING: - return "STARTING"; - case Element::State::STARTED: - return "STARTED"; - case Element::State::STOPPING: - return "STOPPING"; - case Element::State::STOPPED: - return "STOPPED"; - default: - return nullptr; - } -} - - -void CodedToRawFilterElement::onChannelSos(CodedChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - CodedSink::onChannelSos(channel); - - RawSource::lock(); - unsigned int count = RawSource::getOutputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - RawVideoMedia *media = getOutputMedia(i); - if (media == nullptr) - continue; - int ret = RawSource::sendDownstreamEvent( - media, RawChannel::DownstreamEvent::SOS); - if (ret < 0) - PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); - } - RawSource::unlock(); -} - - -void CodedToRawFilterElement::onChannelEos(CodedChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - CodedSink::onChannelEos(channel); - - RawSource::lock(); - unsigned int count = RawSource::getOutputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - RawVideoMedia *media = getOutputMedia(i); - if (media == nullptr) - continue; - int ret = RawSource::sendDownstreamEvent( - media, RawChannel::DownstreamEvent::EOS); - if (ret < 0) - PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); - } - RawSource::unlock(); -} - - -void CodedToRawFilterElement::onChannelReconfigure(CodedChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - CodedSink::onChannelReconfigure(channel); - - RawSource::lock(); - unsigned int count = RawSource::getOutputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - RawVideoMedia *media = getOutputMedia(i); - if (media == nullptr) - continue; - int ret = RawSource::sendDownstreamEvent( - media, RawChannel::DownstreamEvent::RECONFIGURE); - if (ret < 0) - PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); - } - RawSource::unlock(); -} - - -void CodedToRawFilterElement::onChannelTimeout(CodedChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - CodedSink::onChannelTimeout(channel); - - RawSource::lock(); - unsigned int count = RawSource::getOutputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - RawVideoMedia *media = getOutputMedia(i); - if (media == nullptr) - continue; - int ret = RawSource::sendDownstreamEvent( - media, RawChannel::DownstreamEvent::TIMEOUT); - if (ret < 0) - PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); - } - RawSource::unlock(); -} - - -void CodedToRawFilterElement::onChannelPhotoTrigger(CodedChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - CodedSink::onChannelPhotoTrigger(channel); - - RawSource::lock(); - unsigned int count = RawSource::getOutputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - RawVideoMedia *media = getOutputMedia(i); - if (media == nullptr) - continue; - int ret = RawSource::sendDownstreamEvent( - media, RawChannel::DownstreamEvent::PHOTO_TRIGGER); - if (ret < 0) - PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); - } - RawSource::unlock(); -} - - -void CodedToRawFilterElement::onChannelVideoPresStats(RawChannel *channel, - VideoPresStats *stats) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSource::onChannelVideoPresStats(channel, stats); - - /* Propagate the video prenstation statistics to all - * input media channels */ - CodedSink::lock(); - unsigned int count = CodedSink::getInputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - CodedVideoMedia *media = getInputMedia(i); - if (media == nullptr) - continue; - CodedChannel *inChannel = getInputChannel(media); - if (inChannel == nullptr) - continue; - int ret = inChannel->sendVideoPresStats(stats); - if (ret < 0) - PDRAW_LOG_ERRNO("sendVideoPresStats", -ret); - } - CodedSink::unlock(); -} - - -void RawToCodedFilterElement::onChannelSos(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSink::onChannelSos(channel); - - CodedSource::lock(); - unsigned int count = CodedSource::getOutputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - CodedVideoMedia *media = getOutputMedia(i); - if (media == nullptr) - continue; - int ret = CodedSource::sendDownstreamEvent( - media, CodedChannel::DownstreamEvent::SOS); - if (ret < 0) - PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); - } - CodedSource::unlock(); -} - - -void RawToCodedFilterElement::onChannelEos(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSink::onChannelEos(channel); - - CodedSource::lock(); - unsigned int count = CodedSource::getOutputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - CodedVideoMedia *media = getOutputMedia(i); - if (media == nullptr) - continue; - int ret = CodedSource::sendDownstreamEvent( - media, CodedChannel::DownstreamEvent::EOS); - if (ret < 0) - PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); - } - CodedSource::unlock(); -} - - -void RawToCodedFilterElement::onChannelReconfigure(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSink::onChannelReconfigure(channel); - - CodedSource::lock(); - unsigned int count = CodedSource::getOutputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - CodedVideoMedia *media = getOutputMedia(i); - if (media == nullptr) - continue; - int ret = CodedSource::sendDownstreamEvent( - media, CodedChannel::DownstreamEvent::RECONFIGURE); - if (ret < 0) - PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); - } - CodedSource::unlock(); -} - - -void RawToCodedFilterElement::onChannelTimeout(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSink::onChannelTimeout(channel); - - CodedSource::lock(); - unsigned int count = CodedSource::getOutputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - CodedVideoMedia *media = getOutputMedia(i); - if (media == nullptr) - continue; - int ret = CodedSource::sendDownstreamEvent( - media, CodedChannel::DownstreamEvent::TIMEOUT); - if (ret < 0) - PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); - } - CodedSource::unlock(); -} - - -void RawToCodedFilterElement::onChannelPhotoTrigger(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSink::onChannelPhotoTrigger(channel); - - CodedSource::lock(); - unsigned int count = CodedSource::getOutputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - CodedVideoMedia *media = getOutputMedia(i); - if (media == nullptr) - continue; - int ret = CodedSource::sendDownstreamEvent( - media, CodedChannel::DownstreamEvent::PHOTO_TRIGGER); - if (ret < 0) - PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); - } - CodedSource::unlock(); -} - - -void RawToCodedFilterElement::onChannelVideoPresStats(CodedChannel *channel, - VideoPresStats *stats) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - CodedSource::onChannelVideoPresStats(channel, stats); - - /* Propagate the video prenstation statistics to all - * input media channels */ - RawSink::lock(); - unsigned int count = RawSink::getInputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - RawVideoMedia *media = getInputMedia(i); - if (media == nullptr) - continue; - RawChannel *inChannel = getInputChannel(media); - if (inChannel == nullptr) - continue; - int ret = inChannel->sendVideoPresStats(stats); - if (ret < 0) - PDRAW_LOG_ERRNO("sendVideoPresStats", -ret); - } - RawSink::unlock(); -} - - -void RawToRawFilterElement::onChannelSos(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSink::onChannelSos(channel); - - RawSource::lock(); - unsigned int count = RawSource::getOutputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - RawVideoMedia *media = getOutputMedia(i); - if (media == nullptr) - continue; - int ret = RawSource::sendDownstreamEvent( - media, RawChannel::DownstreamEvent::SOS); - if (ret < 0) - PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); - } - RawSource::unlock(); -} - - -void RawToRawFilterElement::onChannelEos(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSink::onChannelEos(channel); - - RawSource::lock(); - unsigned int count = RawSource::getOutputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - RawVideoMedia *media = getOutputMedia(i); - if (media == nullptr) - continue; - int ret = RawSource::sendDownstreamEvent( - media, RawChannel::DownstreamEvent::EOS); - if (ret < 0) - PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); - } - RawSource::unlock(); -} - - -void RawToRawFilterElement::onChannelReconfigure(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSink::onChannelReconfigure(channel); - - RawSource::lock(); - unsigned int count = RawSource::getOutputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - RawVideoMedia *media = getOutputMedia(i); - if (media == nullptr) - continue; - int ret = RawSource::sendDownstreamEvent( - media, RawChannel::DownstreamEvent::RECONFIGURE); - if (ret < 0) - PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); - } - RawSource::unlock(); -} - - -void RawToRawFilterElement::onChannelTimeout(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSink::onChannelTimeout(channel); - - RawSource::lock(); - unsigned int count = RawSource::getOutputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - RawVideoMedia *media = getOutputMedia(i); - if (media == nullptr) - continue; - int ret = RawSource::sendDownstreamEvent( - media, RawChannel::DownstreamEvent::TIMEOUT); - if (ret < 0) - PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); - } - RawSource::unlock(); -} - - -void RawToRawFilterElement::onChannelPhotoTrigger(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSink::onChannelPhotoTrigger(channel); - - RawSource::lock(); - unsigned int count = RawSource::getOutputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - RawVideoMedia *media = getOutputMedia(i); - if (media == nullptr) - continue; - int ret = RawSource::sendDownstreamEvent( - media, RawChannel::DownstreamEvent::PHOTO_TRIGGER); - if (ret < 0) - PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); - } - RawSource::unlock(); -} - - -void RawToRawFilterElement::onChannelVideoPresStats(RawChannel *channel, - VideoPresStats *stats) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSource::onChannelVideoPresStats(channel, stats); - - /* Propagate the video prenstation statistics to all - * input media channels */ - RawSink::lock(); - unsigned int count = RawSink::getInputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - RawVideoMedia *media = getInputMedia(i); - if (media == nullptr) - continue; - RawChannel *inChannel = getInputChannel(media); - if (inChannel == nullptr) - continue; - int ret = inChannel->sendVideoPresStats(stats); - if (ret < 0) - PDRAW_LOG_ERRNO("sendVideoPresStats", -ret); - } - RawSink::unlock(); -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Pipeline element + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_elmt +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_element.hpp" + +#include + +namespace Pdraw { + + +std::atomic Element::mIdCounter(0); + + +Element::Element(Session *session, Listener *listener) : + mSession(session), mListener(listener), mState(INVALID) +{ + int res; + pthread_mutexattr_t attr; + bool attr_created = false; + + mId = ++mIdCounter; + std::string name = std::string(__func__) + "#" + std::to_string(mId); + Loggable::setName(name); + + res = pthread_mutexattr_init(&attr); + if (res != 0) { + PDRAW_LOG_ERRNO("pthread_mutexattr_init", res); + goto out; + } + attr_created = true; + + res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + if (res != 0) { + PDRAW_LOG_ERRNO("pthread_mutexattr_settype", res); + goto out; + } + + res = pthread_mutex_init(&mMutex, &attr); + if (res != 0) { + PDRAW_LOG_ERRNO("pthread_mutex_init", res); + goto out; + } + +out: + if (attr_created) + pthread_mutexattr_destroy(&attr); +} + + +Element::~Element(void) +{ + mState = INVALID; + pthread_mutex_destroy(&mMutex); + PDRAW_LOGI("element DESTROYED"); +} + + +void Element::lock(void) +{ + pthread_mutex_lock(&mMutex); +} + + +void Element::unlock(void) +{ + pthread_mutex_unlock(&mMutex); +} + + +unsigned int Element::getId(void) +{ + return mId; +} + + +void Element::setClassName(std::string &name) +{ + std::string new_name = name + "#" + std::to_string(mId); + Loggable::setName(new_name); +} + + +void Element::setClassName(const char *name) +{ + std::string new_name = std::string(name) + "#" + std::to_string(mId); + Loggable::setName(new_name); +} + + +Element::State Element::getState(void) +{ + pthread_mutex_lock(&mMutex); + Element::State ret = mState; + pthread_mutex_unlock(&mMutex); + return ret; +} + + +void Element::setState(Element::State state) +{ + pthread_mutex_lock(&mMutex); + if (state == mState) { + pthread_mutex_unlock(&mMutex); + return; + } + + mState = state; + pthread_mutex_unlock(&mMutex); + PDRAW_LOGI("element state change to %s", getElementStateStr(state)); + + if (mListener) + mListener->onElementStateChanged(this, state); +} + + +void Element::setStateAsyncNotify(Element::State state) +{ + pthread_mutex_lock(&mMutex); + if (state == mState) { + pthread_mutex_unlock(&mMutex); + return; + } + + mState = state; + pthread_mutex_unlock(&mMutex); + PDRAW_LOGI("element state change to %s (async notify)", + getElementStateStr(state)); + + if (mListener) + mListener->asyncElementStateChange(this, state); +} + + +const char *Element::getElementStateStr(Element::State val) +{ + switch (val) { + case Element::State::INVALID: + return "INVALID"; + case Element::State::CREATED: + return "CREATED"; + case Element::State::STARTING: + return "STARTING"; + case Element::State::STARTED: + return "STARTED"; + case Element::State::STOPPING: + return "STOPPING"; + case Element::State::STOPPED: + return "STOPPED"; + default: + return nullptr; + } +} + + +void CodedToRawFilterElement::onChannelSos(CodedChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + CodedSink::onChannelSos(channel); + + RawSource::lock(); + unsigned int count = RawSource::getOutputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + RawVideoMedia *media = getOutputMedia(i); + if (media == nullptr) + continue; + int ret = RawSource::sendDownstreamEvent( + media, RawChannel::DownstreamEvent::SOS); + if (ret < 0) + PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); + } + RawSource::unlock(); +} + + +void CodedToRawFilterElement::onChannelEos(CodedChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + CodedSink::onChannelEos(channel); + + RawSource::lock(); + unsigned int count = RawSource::getOutputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + RawVideoMedia *media = getOutputMedia(i); + if (media == nullptr) + continue; + int ret = RawSource::sendDownstreamEvent( + media, RawChannel::DownstreamEvent::EOS); + if (ret < 0) + PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); + } + RawSource::unlock(); +} + + +void CodedToRawFilterElement::onChannelReconfigure(CodedChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + CodedSink::onChannelReconfigure(channel); + + RawSource::lock(); + unsigned int count = RawSource::getOutputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + RawVideoMedia *media = getOutputMedia(i); + if (media == nullptr) + continue; + int ret = RawSource::sendDownstreamEvent( + media, RawChannel::DownstreamEvent::RECONFIGURE); + if (ret < 0) + PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); + } + RawSource::unlock(); +} + + +void CodedToRawFilterElement::onChannelTimeout(CodedChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + CodedSink::onChannelTimeout(channel); + + RawSource::lock(); + unsigned int count = RawSource::getOutputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + RawVideoMedia *media = getOutputMedia(i); + if (media == nullptr) + continue; + int ret = RawSource::sendDownstreamEvent( + media, RawChannel::DownstreamEvent::TIMEOUT); + if (ret < 0) + PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); + } + RawSource::unlock(); +} + + +void CodedToRawFilterElement::onChannelPhotoTrigger(CodedChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + CodedSink::onChannelPhotoTrigger(channel); + + RawSource::lock(); + unsigned int count = RawSource::getOutputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + RawVideoMedia *media = getOutputMedia(i); + if (media == nullptr) + continue; + int ret = RawSource::sendDownstreamEvent( + media, RawChannel::DownstreamEvent::PHOTO_TRIGGER); + if (ret < 0) + PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); + } + RawSource::unlock(); +} + + +void CodedToRawFilterElement::onChannelVideoPresStats(RawChannel *channel, + VideoPresStats *stats) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSource::onChannelVideoPresStats(channel, stats); + + /* Propagate the video prenstation statistics to all + * input media channels */ + CodedSink::lock(); + unsigned int count = CodedSink::getInputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + CodedVideoMedia *media = getInputMedia(i); + if (media == nullptr) + continue; + CodedChannel *inChannel = getInputChannel(media); + if (inChannel == nullptr) + continue; + int ret = inChannel->sendVideoPresStats(stats); + if (ret < 0) + PDRAW_LOG_ERRNO("sendVideoPresStats", -ret); + } + CodedSink::unlock(); +} + + +void RawToCodedFilterElement::onChannelSos(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSink::onChannelSos(channel); + + CodedSource::lock(); + unsigned int count = CodedSource::getOutputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + CodedVideoMedia *media = getOutputMedia(i); + if (media == nullptr) + continue; + int ret = CodedSource::sendDownstreamEvent( + media, CodedChannel::DownstreamEvent::SOS); + if (ret < 0) + PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); + } + CodedSource::unlock(); +} + + +void RawToCodedFilterElement::onChannelEos(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSink::onChannelEos(channel); + + CodedSource::lock(); + unsigned int count = CodedSource::getOutputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + CodedVideoMedia *media = getOutputMedia(i); + if (media == nullptr) + continue; + int ret = CodedSource::sendDownstreamEvent( + media, CodedChannel::DownstreamEvent::EOS); + if (ret < 0) + PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); + } + CodedSource::unlock(); +} + + +void RawToCodedFilterElement::onChannelReconfigure(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSink::onChannelReconfigure(channel); + + CodedSource::lock(); + unsigned int count = CodedSource::getOutputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + CodedVideoMedia *media = getOutputMedia(i); + if (media == nullptr) + continue; + int ret = CodedSource::sendDownstreamEvent( + media, CodedChannel::DownstreamEvent::RECONFIGURE); + if (ret < 0) + PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); + } + CodedSource::unlock(); +} + + +void RawToCodedFilterElement::onChannelTimeout(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSink::onChannelTimeout(channel); + + CodedSource::lock(); + unsigned int count = CodedSource::getOutputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + CodedVideoMedia *media = getOutputMedia(i); + if (media == nullptr) + continue; + int ret = CodedSource::sendDownstreamEvent( + media, CodedChannel::DownstreamEvent::TIMEOUT); + if (ret < 0) + PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); + } + CodedSource::unlock(); +} + + +void RawToCodedFilterElement::onChannelPhotoTrigger(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSink::onChannelPhotoTrigger(channel); + + CodedSource::lock(); + unsigned int count = CodedSource::getOutputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + CodedVideoMedia *media = getOutputMedia(i); + if (media == nullptr) + continue; + int ret = CodedSource::sendDownstreamEvent( + media, CodedChannel::DownstreamEvent::PHOTO_TRIGGER); + if (ret < 0) + PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); + } + CodedSource::unlock(); +} + + +void RawToCodedFilterElement::onChannelVideoPresStats(CodedChannel *channel, + VideoPresStats *stats) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + CodedSource::onChannelVideoPresStats(channel, stats); + + /* Propagate the video prenstation statistics to all + * input media channels */ + RawSink::lock(); + unsigned int count = RawSink::getInputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + RawVideoMedia *media = getInputMedia(i); + if (media == nullptr) + continue; + RawChannel *inChannel = getInputChannel(media); + if (inChannel == nullptr) + continue; + int ret = inChannel->sendVideoPresStats(stats); + if (ret < 0) + PDRAW_LOG_ERRNO("sendVideoPresStats", -ret); + } + RawSink::unlock(); +} + + +void RawToRawFilterElement::onChannelSos(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSink::onChannelSos(channel); + + RawSource::lock(); + unsigned int count = RawSource::getOutputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + RawVideoMedia *media = getOutputMedia(i); + if (media == nullptr) + continue; + int ret = RawSource::sendDownstreamEvent( + media, RawChannel::DownstreamEvent::SOS); + if (ret < 0) + PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); + } + RawSource::unlock(); +} + + +void RawToRawFilterElement::onChannelEos(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSink::onChannelEos(channel); + + RawSource::lock(); + unsigned int count = RawSource::getOutputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + RawVideoMedia *media = getOutputMedia(i); + if (media == nullptr) + continue; + int ret = RawSource::sendDownstreamEvent( + media, RawChannel::DownstreamEvent::EOS); + if (ret < 0) + PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); + } + RawSource::unlock(); +} + + +void RawToRawFilterElement::onChannelReconfigure(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSink::onChannelReconfigure(channel); + + RawSource::lock(); + unsigned int count = RawSource::getOutputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + RawVideoMedia *media = getOutputMedia(i); + if (media == nullptr) + continue; + int ret = RawSource::sendDownstreamEvent( + media, RawChannel::DownstreamEvent::RECONFIGURE); + if (ret < 0) + PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); + } + RawSource::unlock(); +} + + +void RawToRawFilterElement::onChannelTimeout(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSink::onChannelTimeout(channel); + + RawSource::lock(); + unsigned int count = RawSource::getOutputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + RawVideoMedia *media = getOutputMedia(i); + if (media == nullptr) + continue; + int ret = RawSource::sendDownstreamEvent( + media, RawChannel::DownstreamEvent::TIMEOUT); + if (ret < 0) + PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); + } + RawSource::unlock(); +} + + +void RawToRawFilterElement::onChannelPhotoTrigger(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSink::onChannelPhotoTrigger(channel); + + RawSource::lock(); + unsigned int count = RawSource::getOutputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + RawVideoMedia *media = getOutputMedia(i); + if (media == nullptr) + continue; + int ret = RawSource::sendDownstreamEvent( + media, RawChannel::DownstreamEvent::PHOTO_TRIGGER); + if (ret < 0) + PDRAW_LOG_ERRNO("sendDownstreamEvent", -ret); + } + RawSource::unlock(); +} + + +void RawToRawFilterElement::onChannelVideoPresStats(RawChannel *channel, + VideoPresStats *stats) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSource::onChannelVideoPresStats(channel, stats); + + /* Propagate the video prenstation statistics to all + * input media channels */ + RawSink::lock(); + unsigned int count = RawSink::getInputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + RawVideoMedia *media = getInputMedia(i); + if (media == nullptr) + continue; + RawChannel *inChannel = getInputChannel(media); + if (inChannel == nullptr) + continue; + int ret = inChannel->sendVideoPresStats(stats); + if (ret < 0) + PDRAW_LOG_ERRNO("sendVideoPresStats", -ret); + } + RawSink::unlock(); +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_element.hpp b/libpdraw/src/pdraw_element.hpp index 1bc0fb6..ab30290 100644 --- a/libpdraw/src/pdraw_element.hpp +++ b/libpdraw/src/pdraw_element.hpp @@ -1,347 +1,347 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Pipeline element - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_ELEMENT_HPP_ -#define _PDRAW_ELEMENT_HPP_ - -#include "pdraw_channel_coded_video.hpp" -#include "pdraw_channel_raw_video.hpp" -#include "pdraw_media.hpp" -#include "pdraw_sink_coded_video.hpp" -#include "pdraw_sink_raw_video.hpp" -#include "pdraw_source_coded_video.hpp" -#include "pdraw_source_raw_video.hpp" -#include "pdraw_utils.hpp" - -#include -#include - -#include -#include -#include - -namespace Pdraw { - -class Session; - -class Element : public Loggable { -public: - friend class Session; - - enum State { - INVALID, - CREATED, - STARTING, - STARTED, - STOPPING, - STOPPED, - }; - - class Listener { - public: - virtual ~Listener(void) {} - - virtual void onElementStateChanged(Element *element, - Element::State state) = 0; - - virtual void asyncElementStateChange(Element *element, - Element::State state) = 0; - }; - - virtual ~Element(void); - - virtual int start(void) = 0; - - virtual int stop(void) = 0; - - void lock(void); - - void unlock(void); - - unsigned int getId(void); - - Element::State getState(void); - - static const char *getElementStateStr(Element::State val); - -protected: - Element(Session *session, Listener *listener); - - void setClassName(std::string &name); - - void setClassName(const char *name); - - void setState(Element::State state); - - void setStateAsyncNotify(Element::State state); - - Session *mSession; - Listener *mListener; - Element::State mState; - unsigned int mId; - pthread_mutex_t mMutex; - static std::atomic mIdCounter; -}; - - -class CodedSourceElement : public Element, public CodedSource { -public: - friend class Session; - - CodedSourceElement(Session *session, - Element::Listener *listener, - unsigned int maxOutputMedias, - CodedSource::Listener *sourceListener) : - Element(session, listener), - CodedSource(maxOutputMedias, sourceListener) - { - } - - virtual ~CodedSourceElement(void) {} - -protected: - std::string &getName(void) - { - return Element::getName(); - } -}; - - -class RawSourceElement : public Element, public RawSource { -public: - friend class Session; - - RawSourceElement(Session *session, - Element::Listener *listener, - unsigned int maxOutputMedias, - RawSource::Listener *sourceListener) : - Element(session, listener), - RawSource(maxOutputMedias, sourceListener) - { - } - - virtual ~RawSourceElement(void) {} - -protected: - std::string &getName(void) - { - return Element::getName(); - } -}; - - -class CodedSinkElement : public Element, public CodedSink { -public: - friend class Session; - - CodedSinkElement( - Session *session, - Element::Listener *listener, - unsigned int maxInputMedias, - const struct vdef_coded_format *codedVideoMediaFormatCaps, - int codedVideoMediaFormatCapsCount) : - Element(session, listener), - CodedSink(maxInputMedias, - codedVideoMediaFormatCaps, - codedVideoMediaFormatCapsCount) - { - } - - virtual ~CodedSinkElement(void) {} - -protected: - std::string &getName(void) - { - return Element::getName(); - } -}; - - -class RawSinkElement : public Element, public RawSink { -public: - friend class Session; - - RawSinkElement(Session *session, - Element::Listener *listener, - unsigned int maxInputMedias, - const struct vdef_raw_format *rawVideoMediaFormatCaps, - int rawVideoMediaFormatCapsCount) : - Element(session, listener), - RawSink(maxInputMedias, - rawVideoMediaFormatCaps, - rawVideoMediaFormatCapsCount) - { - } - - virtual ~RawSinkElement(void) {} - -protected: - std::string &getName(void) - { - return Element::getName(); - } -}; - - -class CodedToRawFilterElement : public Element, - public CodedSink, - public RawSource { -public: - friend class Session; - - CodedToRawFilterElement( - Session *session, - Element::Listener *listener, - unsigned int maxInputMedias, - const struct vdef_coded_format *codedVideoMediaFormatCaps, - int codedVideoMediaFormatCapsCount, - unsigned int maxOutputMedias, - RawSource::Listener *sourceListener) : - Element(session, listener), - CodedSink(maxInputMedias, - codedVideoMediaFormatCaps, - codedVideoMediaFormatCapsCount), - RawSource(maxOutputMedias, sourceListener) - { - } - - virtual ~CodedToRawFilterElement(void) {} - -protected: - std::string &getName(void) - { - return Element::getName(); - } - - virtual void onChannelSos(CodedChannel *channel); - - virtual void onChannelEos(CodedChannel *channel); - - virtual void onChannelReconfigure(CodedChannel *channel); - - virtual void onChannelTimeout(CodedChannel *channel); - - virtual void onChannelPhotoTrigger(CodedChannel *channel); - - virtual void onChannelVideoPresStats(RawChannel *channel, - VideoPresStats *stats); -}; - - -class RawToCodedFilterElement : public Element, - public RawSink, - public CodedSource { -public: - friend class Session; - - RawToCodedFilterElement( - Session *session, - Element::Listener *listener, - unsigned int maxInputMedias, - const struct vdef_raw_format *rawVideoMediaFormatCaps, - int rawVideoMediaFormatCapsCount, - unsigned int maxOutputMedias, - CodedSource::Listener *sourceListener) : - Element(session, listener), - RawSink(maxInputMedias, - rawVideoMediaFormatCaps, - rawVideoMediaFormatCapsCount), - CodedSource(maxOutputMedias, sourceListener) - { - } - - virtual ~RawToCodedFilterElement(void) {} - -protected: - std::string &getName(void) - { - return Element::getName(); - } - - virtual void onChannelSos(RawChannel *channel); - - virtual void onChannelEos(RawChannel *channel); - - virtual void onChannelReconfigure(RawChannel *channel); - - virtual void onChannelTimeout(RawChannel *channel); - - virtual void onChannelPhotoTrigger(RawChannel *channel); - - virtual void onChannelVideoPresStats(CodedChannel *channel, - VideoPresStats *stats); -}; - - -class RawToRawFilterElement : public Element, public RawSink, public RawSource { -public: - friend class Session; - - RawToRawFilterElement( - Session *session, - Element::Listener *listener, - unsigned int maxInputMedias, - const struct vdef_raw_format *rawVideoMediaFormatCaps, - int rawVideoMediaFormatCapsCount, - unsigned int maxOutputMedias, - RawSource::Listener *sourceListener) : - Element(session, listener), - RawSink(maxInputMedias, - rawVideoMediaFormatCaps, - rawVideoMediaFormatCapsCount), - RawSource(maxOutputMedias, sourceListener) - { - } - - virtual ~RawToRawFilterElement(void) {} - -protected: - std::string &getName(void) - { - return Element::getName(); - } - - virtual void onChannelSos(RawChannel *channel); - - virtual void onChannelEos(RawChannel *channel); - - virtual void onChannelReconfigure(RawChannel *channel); - - virtual void onChannelTimeout(RawChannel *channel); - - virtual void onChannelPhotoTrigger(RawChannel *channel); - - virtual void onChannelVideoPresStats(RawChannel *channel, - VideoPresStats *stats); -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_ELEMENT_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Pipeline element + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_ELEMENT_HPP_ +#define _PDRAW_ELEMENT_HPP_ + +#include "pdraw_channel_coded_video.hpp" +#include "pdraw_channel_raw_video.hpp" +#include "pdraw_media.hpp" +#include "pdraw_sink_coded_video.hpp" +#include "pdraw_sink_raw_video.hpp" +#include "pdraw_source_coded_video.hpp" +#include "pdraw_source_raw_video.hpp" +#include "pdraw_utils.hpp" + +#include +#include + +#include +#include +#include + +namespace Pdraw { + +class Session; + +class Element : public Loggable { +public: + friend class Session; + + enum State { + INVALID, + CREATED, + STARTING, + STARTED, + STOPPING, + STOPPED, + }; + + class Listener { + public: + virtual ~Listener(void) {} + + virtual void onElementStateChanged(Element *element, + Element::State state) = 0; + + virtual void asyncElementStateChange(Element *element, + Element::State state) = 0; + }; + + virtual ~Element(void); + + virtual int start(void) = 0; + + virtual int stop(void) = 0; + + void lock(void); + + void unlock(void); + + unsigned int getId(void); + + Element::State getState(void); + + static const char *getElementStateStr(Element::State val); + +protected: + Element(Session *session, Listener *listener); + + void setClassName(std::string &name); + + void setClassName(const char *name); + + void setState(Element::State state); + + void setStateAsyncNotify(Element::State state); + + Session *mSession; + Listener *mListener; + Element::State mState; + unsigned int mId; + pthread_mutex_t mMutex; + static std::atomic mIdCounter; +}; + + +class CodedSourceElement : public Element, public CodedSource { +public: + friend class Session; + + CodedSourceElement(Session *session, + Element::Listener *listener, + unsigned int maxOutputMedias, + CodedSource::Listener *sourceListener) : + Element(session, listener), + CodedSource(maxOutputMedias, sourceListener) + { + } + + virtual ~CodedSourceElement(void) {} + +protected: + std::string &getName(void) + { + return Element::getName(); + } +}; + + +class RawSourceElement : public Element, public RawSource { +public: + friend class Session; + + RawSourceElement(Session *session, + Element::Listener *listener, + unsigned int maxOutputMedias, + RawSource::Listener *sourceListener) : + Element(session, listener), + RawSource(maxOutputMedias, sourceListener) + { + } + + virtual ~RawSourceElement(void) {} + +protected: + std::string &getName(void) + { + return Element::getName(); + } +}; + + +class CodedSinkElement : public Element, public CodedSink { +public: + friend class Session; + + CodedSinkElement( + Session *session, + Element::Listener *listener, + unsigned int maxInputMedias, + const struct vdef_coded_format *codedVideoMediaFormatCaps, + int codedVideoMediaFormatCapsCount) : + Element(session, listener), + CodedSink(maxInputMedias, + codedVideoMediaFormatCaps, + codedVideoMediaFormatCapsCount) + { + } + + virtual ~CodedSinkElement(void) {} + +protected: + std::string &getName(void) + { + return Element::getName(); + } +}; + + +class RawSinkElement : public Element, public RawSink { +public: + friend class Session; + + RawSinkElement(Session *session, + Element::Listener *listener, + unsigned int maxInputMedias, + const struct vdef_raw_format *rawVideoMediaFormatCaps, + int rawVideoMediaFormatCapsCount) : + Element(session, listener), + RawSink(maxInputMedias, + rawVideoMediaFormatCaps, + rawVideoMediaFormatCapsCount) + { + } + + virtual ~RawSinkElement(void) {} + +protected: + std::string &getName(void) + { + return Element::getName(); + } +}; + + +class CodedToRawFilterElement : public Element, + public CodedSink, + public RawSource { +public: + friend class Session; + + CodedToRawFilterElement( + Session *session, + Element::Listener *listener, + unsigned int maxInputMedias, + const struct vdef_coded_format *codedVideoMediaFormatCaps, + int codedVideoMediaFormatCapsCount, + unsigned int maxOutputMedias, + RawSource::Listener *sourceListener) : + Element(session, listener), + CodedSink(maxInputMedias, + codedVideoMediaFormatCaps, + codedVideoMediaFormatCapsCount), + RawSource(maxOutputMedias, sourceListener) + { + } + + virtual ~CodedToRawFilterElement(void) {} + +protected: + std::string &getName(void) + { + return Element::getName(); + } + + virtual void onChannelSos(CodedChannel *channel); + + virtual void onChannelEos(CodedChannel *channel); + + virtual void onChannelReconfigure(CodedChannel *channel); + + virtual void onChannelTimeout(CodedChannel *channel); + + virtual void onChannelPhotoTrigger(CodedChannel *channel); + + virtual void onChannelVideoPresStats(RawChannel *channel, + VideoPresStats *stats); +}; + + +class RawToCodedFilterElement : public Element, + public RawSink, + public CodedSource { +public: + friend class Session; + + RawToCodedFilterElement( + Session *session, + Element::Listener *listener, + unsigned int maxInputMedias, + const struct vdef_raw_format *rawVideoMediaFormatCaps, + int rawVideoMediaFormatCapsCount, + unsigned int maxOutputMedias, + CodedSource::Listener *sourceListener) : + Element(session, listener), + RawSink(maxInputMedias, + rawVideoMediaFormatCaps, + rawVideoMediaFormatCapsCount), + CodedSource(maxOutputMedias, sourceListener) + { + } + + virtual ~RawToCodedFilterElement(void) {} + +protected: + std::string &getName(void) + { + return Element::getName(); + } + + virtual void onChannelSos(RawChannel *channel); + + virtual void onChannelEos(RawChannel *channel); + + virtual void onChannelReconfigure(RawChannel *channel); + + virtual void onChannelTimeout(RawChannel *channel); + + virtual void onChannelPhotoTrigger(RawChannel *channel); + + virtual void onChannelVideoPresStats(CodedChannel *channel, + VideoPresStats *stats); +}; + + +class RawToRawFilterElement : public Element, public RawSink, public RawSource { +public: + friend class Session; + + RawToRawFilterElement( + Session *session, + Element::Listener *listener, + unsigned int maxInputMedias, + const struct vdef_raw_format *rawVideoMediaFormatCaps, + int rawVideoMediaFormatCapsCount, + unsigned int maxOutputMedias, + RawSource::Listener *sourceListener) : + Element(session, listener), + RawSink(maxInputMedias, + rawVideoMediaFormatCaps, + rawVideoMediaFormatCapsCount), + RawSource(maxOutputMedias, sourceListener) + { + } + + virtual ~RawToRawFilterElement(void) {} + +protected: + std::string &getName(void) + { + return Element::getName(); + } + + virtual void onChannelSos(RawChannel *channel); + + virtual void onChannelEos(RawChannel *channel); + + virtual void onChannelReconfigure(RawChannel *channel); + + virtual void onChannelTimeout(RawChannel *channel); + + virtual void onChannelPhotoTrigger(RawChannel *channel); + + virtual void onChannelVideoPresStats(RawChannel *channel, + VideoPresStats *stats); +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_ELEMENT_HPP_ */ diff --git a/libpdraw/src/pdraw_encoder_video.cpp b/libpdraw/src/pdraw_encoder_video.cpp index 42bc337..751ccc2 100644 --- a/libpdraw/src/pdraw_encoder_video.cpp +++ b/libpdraw/src/pdraw_encoder_video.cpp @@ -1,763 +1,763 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Video encoder element - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_venc -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_encoder_video.hpp" -#include "pdraw_session.hpp" -#include "pdraw_utils.hpp" - -#include -#include - -#include - -namespace Pdraw { - - -const struct venc_cbs VideoEncoder::mEncoderCbs = { - .frame_output = &VideoEncoder::frameOutputCb, - .flush = &VideoEncoder::flushCb, - .stop = &VideoEncoder::stopCb, - .pre_release = nullptr, -}; - - -VideoEncoder::VideoEncoder(Session *session, - Element::Listener *elementListener, - CodedSource::Listener *sourceListener) : - RawToCodedFilterElement(session, - elementListener, - 1, - nullptr, - 0, - 1, - sourceListener), - mInputMedia(nullptr), mOutputMedia(nullptr), - mInputBufferPool(nullptr), mInputBufferQueue(nullptr), - mVenc(nullptr), mIsFlushed(true), - mInputChannelFlushPending(false), mVencFlushPending(false), - mVencStopPending(false), mCompleteStopPendingCount(0) -{ - const struct vdef_raw_format *supportedInputFormats; - int supportedInputFormatsCount; - - Element::setClassName(__func__); - - /* Supported input formats */ - supportedInputFormatsCount = venc_get_supported_input_formats( - VENC_ENCODER_IMPLEM_AUTO, &supportedInputFormats); - if (supportedInputFormatsCount < 0) - PDRAW_LOG_ERRNO("venc_get_supported_input_formats", - -supportedInputFormatsCount); - else - setRawVideoMediaFormatCaps(supportedInputFormats, - supportedInputFormatsCount); - - setState(CREATED); -} - - -VideoEncoder::~VideoEncoder(void) -{ - int ret; - - if (mState != STOPPED) - PDRAW_LOGW("encoder is still running"); - - if (mVenc) { - ret = venc_destroy(mVenc); - if (ret < 0) - PDRAW_LOG_ERRNO("venc_destroy", -ret); - } - - if (mOutputMedia != nullptr) - PDRAW_LOGW("output media was not properly removed"); -} - - -int VideoEncoder::start(void) -{ - int ret = 0; - - if ((mState == STARTED) || (mState == STARTING)) { - return 0; - } - if (mState != CREATED) { - PDRAW_LOGE("encoder is not created"); - return -EPROTO; - } - setState(STARTING); - - /* Get the input media and port */ - RawSink::lock(); - unsigned int inputMediaCount = getInputMediaCount(); - if (inputMediaCount != 1) { - RawSink::unlock(); - PDRAW_LOGE("invalid input media count"); - return -EPROTO; - } - Media *media = getInputMedia(0); - if (media == nullptr) { - RawSink::unlock(); - PDRAW_LOGE("invalid input media"); - return -EPROTO; - } - mInputMedia = dynamic_cast(media); - if (mInputMedia == nullptr) { - RawSink::unlock(); - PDRAW_LOGE("invalid input media"); - return -EPROTO; - } - InputPort *port = getInputPort(mInputMedia); - if (port == nullptr) { - RawSink::unlock(); - PDRAW_LOGE("invalid input port"); - return -EPROTO; - } - - /* Initialize the encoder */ - struct venc_config cfg = {}; - cfg.implem = VENC_ENCODER_IMPLEM_AUTO; - cfg.encoding = VDEF_ENCODING_H264; /* TODO */ - cfg.input.format = mInputMedia->format; - cfg.input.info = mInputMedia->info; - if ((cfg.input.info.framerate.num == 0) || - (cfg.input.info.framerate.den == 0)) { - /* TODO: this is an ugly workaround, - * mInputMedia should have a valid framerate */ - cfg.input.info.framerate.num = 30; - cfg.input.info.framerate.den = 1; - } - cfg.h264.max_bitrate = 10000000; /* TODO */ - /* TODO: Default to AVCC, see how to handle the choice later */ - cfg.output.preferred_format = VDEF_CODED_DATA_FORMAT_AVCC; - ret = venc_new(mSession->getLoop(), &cfg, &mEncoderCbs, this, &mVenc); - if (ret < 0) { - RawSink::unlock(); - PDRAW_LOG_ERRNO("venc_new", -ret); - return ret; - } - - /* Setup the input port */ - port->channel->setKey(this); - mInputBufferQueue = venc_get_input_buffer_queue(mVenc); - port->channel->setQueue(mInputBufferQueue); - mInputBufferPool = venc_get_input_buffer_pool(mVenc); - port->channel->setPool(mInputBufferPool); - - RawSink::unlock(); - - setState(STARTED); - - return 0; -} - - -int VideoEncoder::stop(void) -{ - int ret; - - if ((mState == STOPPED) || (mState == STOPPING)) - return 0; - if (mState != STARTED) { - PDRAW_LOGE("encoder is not started"); - return -EPROTO; - } - setState(STOPPING); - mVencStopPending = true; - - /* Flush everything */ - ret = flush(); - if (ret < 0) - PDRAW_LOG_ERRNO("flush", -ret); - - /* When the flush is complete, stopping will be triggered */ - return ret; -} - - -int VideoEncoder::flush(void) -{ - int ret; - unsigned int outputChannelCount, i; - CodedChannel *outputChannel; - - if (mIsFlushed) { - PDRAW_LOGD("encoder is already flushed, nothing to do"); - completeFlush(); - return 0; - } - - mVencFlushPending = true; - - /* Flush the output channels (async) */ - CodedSource::lock(); - if (mOutputMedia != nullptr) { - outputChannelCount = getOutputChannelCount(mOutputMedia); - for (i = 0; i < outputChannelCount; i++) { - outputChannel = getOutputChannel(mOutputMedia, i); - if (outputChannel == nullptr) { - PDRAW_LOGW( - "failed to get output channel " - "at index %d", - i); - continue; - } - ret = outputChannel->flush(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->flush", -ret); - } - } - CodedSource::unlock(); - - /* Flush the encoder (async) - * (the input channel queue is flushed by venc) */ - ret = venc_flush(mVenc, 1); - if (ret < 0) - PDRAW_LOG_ERRNO("venc_flush", -ret); - - return ret; -} - - -void VideoEncoder::completeFlush(void) -{ - int ret; - unsigned int outputChannelCount, i; - CodedChannel *outputChannel; - bool pending = false; - - if (mVencFlushPending) - return; - - CodedSource::lock(); - if (mOutputMedia != nullptr) { - outputChannelCount = getOutputChannelCount(mOutputMedia); - for (i = 0; i < outputChannelCount; i++) { - outputChannel = getOutputChannel(mOutputMedia, i); - if (outputChannel == nullptr) { - PDRAW_LOGW( - "failed to get output channel " - "at index %d", - i); - continue; - } - if (outputChannel->isFlushPending()) { - pending = true; - break; - } - } - } - CodedSource::unlock(); - - if (pending) - return; - - RawSink::lock(); - if (mInputMedia != nullptr) { - mIsFlushed = true; - if (mInputChannelFlushPending) { - mInputChannelFlushPending = false; - RawChannel *inputChannel = getInputChannel(mInputMedia); - if (inputChannel == nullptr) { - PDRAW_LOGE("failed to get input channel"); - } else { - ret = inputChannel->flushDone(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->flushDone", - -ret); - } - } - } - RawSink::unlock(); - - tryStop(); -} - - -int VideoEncoder::tryStop(void) -{ - int ret; - int outputChannelCount = 0, i; - - if (mState != STOPPING) - return 0; - - /* Remove the input port */ - RawSink::lock(); - if (mInputMedia != nullptr) { - RawChannel *channel = getInputChannel(mInputMedia); - if (channel == nullptr) { - PDRAW_LOGE("failed to get channel"); - } else { - channel->setQueue(nullptr); - channel->setPool(nullptr); - } - - ret = removeInputMedia(mInputMedia); - if (ret < 0) - PDRAW_LOG_ERRNO("removeInputMedia", -ret); - else - mInputMedia = nullptr; - } - RawSink::unlock(); - - /* Teardown the output channels - * Note: loop downwards because calling teardown on a channel may or - * may not synchronously remove the channel from the output port */ - CodedSource::lock(); - if (mOutputMedia != nullptr) { - outputChannelCount = getOutputChannelCount(mOutputMedia); - - for (i = outputChannelCount - 1; i >= 0; i--) { - CodedChannel *channel = - getOutputChannel(mOutputMedia, i); - if (channel == nullptr) { - PDRAW_LOGW("failed to get channel at index %d", - i); - continue; - } - ret = channel->teardown(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->teardown", -ret); - } - } - CodedSource::unlock(); - - /* Stop the encoder */ - ret = venc_stop(mVenc); - if (ret < 0) { - PDRAW_LOG_ERRNO("venc_stop", -ret); - return ret; - } - - return 0; -} - - -void VideoEncoder::completeStop(void) -{ - int ret; - unsigned int outputChannelCount; - - mCompleteStopPendingCount--; - - CodedSource::lock(); - if (mOutputMedia == nullptr) { - CodedSource::unlock(); - goto exit; - } - outputChannelCount = getOutputChannelCount(mOutputMedia); - if (outputChannelCount > 0) { - CodedSource::unlock(); - return; - } - - /* Remove the output port */ - if (CodedSource::mListener) { - CodedSource::mListener->onOutputMediaRemoved(this, - mOutputMedia); - } - ret = removeOutputPort(mOutputMedia); - if (ret < 0) { - PDRAW_LOG_ERRNO("removeOutputPort", -ret); - } else { - delete mOutputMedia; - mOutputMedia = nullptr; - } - - CodedSource::unlock(); - -exit: - if ((!mVencStopPending) && (mCompleteStopPendingCount == 0)) - setState(STOPPED); -} - - -int VideoEncoder::createOutputMedia(struct vdef_coded_frame *frame_info, - CodedVideoMedia::Frame &frame) -{ - int ret; - - CodedSource::lock(); - - mOutputMedia = new CodedVideoMedia(mSession); - if (mOutputMedia == nullptr) { - CodedSource::unlock(); - PDRAW_LOGE("output media allocation failed"); - return -ENOMEM; - } - std::string path = mInputMedia->getPath() + ">" + Element::getName() + - "$" + mOutputMedia->getName(); - mOutputMedia->setPath(path); - - ret = addOutputPort(mOutputMedia); - if (ret < 0) { - CodedSource::unlock(); - PDRAW_LOG_ERRNO("addOutputPort", -ret); - return ret; - } - - mOutputMedia->format = frame_info->format; - vdef_frame_to_format_info(&frame_info->info, &mOutputMedia->info); - mOutputMedia->info.framerate = mInputMedia->info.framerate; - mOutputMedia->sessionMeta = mInputMedia->sessionMeta; - mOutputMedia->playbackType = mInputMedia->playbackType; - mOutputMedia->duration = mInputMedia->duration; - - uint8_t *sps, *pps; - size_t spsSize = 0, ppsSize = 0; - ret = venc_get_h264_ps(mVenc, nullptr, &spsSize, nullptr, &ppsSize); - if (ret < 0) { - CodedSource::unlock(); - PDRAW_LOG_ERRNO("venc_get_h264_ps", -ret); - return ret; - } - sps = (uint8_t *)malloc(spsSize); - if (sps == nullptr) { - CodedSource::unlock(); - PDRAW_LOG_ERRNO("malloc:sps", ENOMEM); - return ret; - } - pps = (uint8_t *)malloc(ppsSize); - if (pps == nullptr) { - CodedSource::unlock(); - PDRAW_LOG_ERRNO("malloc:pps", ENOMEM); - free(sps); - return ret; - } - ret = venc_get_h264_ps(mVenc, sps, &spsSize, pps, &ppsSize); - if (ret < 0) { - CodedSource::unlock(); - PDRAW_LOG_ERRNO("venc_get_h264_ps", -ret); - free(sps); - free(pps); - return ret; - } - ret = mOutputMedia->setPs(nullptr, 0, sps, spsSize, pps, ppsSize); - if (ret < 0) { - CodedSource::unlock(); - PDRAW_LOG_ERRNO("media->setPs", -ret); - free(sps); - free(pps); - return ret; - } - free(sps); - free(pps); - - CodedSource::unlock(); - - if (CodedSource::mListener) - CodedSource::mListener->onOutputMediaAdded(this, mOutputMedia); - - return 0; -} - - -void VideoEncoder::onChannelQueue(RawChannel *channel, - struct mbuf_raw_video_frame *frame) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - if (frame == nullptr) { - PDRAW_LOG_ERRNO("frame", EINVAL); - return; - } - if (mState != STARTED) { - PDRAW_LOGE("encoder is not started"); - return; - } - if ((mVencFlushPending) || (mInputChannelFlushPending)) { - PDRAW_LOGI("frame input: flush pending, discard frame"); - return; - } - RawSink::lock(); - struct mbuf_raw_video_frame_queue *queue = channel->getQueue(); - if (queue == nullptr) { - RawSink::unlock(); - PDRAW_LOGE("invalid queue"); - return; - } - if (queue != mInputBufferQueue) { - RawSink::unlock(); - PDRAW_LOGE("invalid input buffer queue"); - return; - } - - RawSink::onChannelQueue(channel, frame); - mIsFlushed = false; - RawSink::unlock(); -} - - -void VideoEncoder::onChannelFlush(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - PDRAW_LOGD("flushing input channel"); - mInputChannelFlushPending = true; - - int ret = flush(); - if (ret < 0) - PDRAW_LOG_ERRNO("flush", -ret); -} - - -void VideoEncoder::onChannelFlushed(CodedChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); - if (media == nullptr) { - PDRAW_LOGE("media not found"); - return; - } - PDRAW_LOGD("'%s': channel flushed media name=%s (channel key=%p)", - Element::getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - completeFlush(); -} - - -void VideoEncoder::onChannelTeardown(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - PDRAW_LOGD("tearing down input channel"); - - int ret = stop(); - if (ret < 0) - PDRAW_LOG_ERRNO("stop", -ret); -} - - -void VideoEncoder::onChannelUnlink(CodedChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - CodedSource::onChannelUnlink(channel); - - if (mState == STOPPING) { - mCompleteStopPendingCount++; - completeStop(); - } -} - - -void VideoEncoder::frameOutputCb(struct venc_encoder *enc, - int status, - struct mbuf_coded_video_frame *out_frame, - void *userdata) -{ - int ret; - VideoEncoder *self = (VideoEncoder *)userdata; - struct vdef_coded_frame info; - struct mbuf_ancillary_data *ancillaryData = nullptr; - RawVideoMedia::Frame *in_meta; - CodedVideoMedia::Frame out_meta; - unsigned int outputChannelCount, i; - CodedChannel *channel; - - if (status != 0) { - PDRAW_LOGE("encoder error: %d(%s)", -status, strerror(-status)); - return; - } - - if (userdata == nullptr) { - PDRAW_LOG_ERRNO("userdata", EINVAL); - return; - } - if (out_frame == nullptr) { - PDRAW_LOG_ERRNO("out_frame", EINVAL); - return; - } - if (self->mState != STARTED) { - PDRAW_LOGE("encoder is not started"); - return; - } - if ((self->mVencFlushPending) || (self->mInputChannelFlushPending)) { - PDRAW_LOGI("frame output: flush pending, discard frame"); - return; - } - - self->RawSink::lock(); - if (self->mInputMedia == nullptr) { - self->RawSink::unlock(); - PDRAW_LOG_ERRNO("invalid input media", EPROTO); - return; - } - - ret = mbuf_coded_video_frame_get_frame_info(out_frame, &info); - if (ret < 0) { - self->RawSink::unlock(); - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_frame_info", -ret); - return; - } - ret = mbuf_coded_video_frame_get_ancillary_data( - out_frame, - PDRAW_ANCILLARY_DATA_KEY_RAWVIDEOFRAME, - &ancillaryData); - if (ret < 0) { - self->RawSink::unlock(); - PDRAW_LOG_ERRNO( - "mbuf_coded_video_frame_get_ancillary_data:pdraw_in", - -ret); - return; - } - - in_meta = (RawVideoMedia::Frame *)mbuf_ancillary_data_get_buffer( - ancillaryData, NULL); - memset(&out_meta, 0, sizeof(out_meta)); - out_meta.ntpTimestamp = in_meta->ntpTimestamp; - out_meta.ntpUnskewedTimestamp = in_meta->ntpUnskewedTimestamp; - out_meta.ntpRawTimestamp = in_meta->ntpRawTimestamp; - out_meta.ntpRawUnskewedTimestamp = in_meta->ntpRawUnskewedTimestamp; - out_meta.playTimestamp = in_meta->playTimestamp; - out_meta.captureTimestamp = in_meta->captureTimestamp; - out_meta.localTimestamp = in_meta->localTimestamp; - out_meta.localTimestampPrecision = in_meta->localTimestampPrecision; - out_meta.recvStartTimestamp = in_meta->recvStartTimestamp; - out_meta.recvEndTimestamp = in_meta->recvEndTimestamp; - out_meta.demuxOutputTimestamp = in_meta->demuxOutputTimestamp; - out_meta.encoderOutputTimestamp = pdraw_getTimestampFromMbufFrame( - out_frame, VENC_ANCILLARY_KEY_OUTPUT_TIME); - out_meta.isRef = (info.type != VDEF_CODED_FRAME_TYPE_P_NON_REF); - out_meta.isSync = ((info.type == VDEF_CODED_FRAME_TYPE_IDR) || - (info.type == VDEF_CODED_FRAME_TYPE_P_IR_START)); - ret = mbuf_ancillary_data_unref(ancillaryData); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_ancillary_data_unref", -ret); - - /* Remove the PDrAW input ancillary data */ - ret = mbuf_coded_video_frame_remove_ancillary_data( - out_frame, PDRAW_ANCILLARY_DATA_KEY_RAWVIDEOFRAME); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_remove_ancillary_data", - -ret); - - ret = mbuf_coded_video_frame_add_ancillary_buffer( - out_frame, - PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME, - &out_meta, - sizeof(out_meta)); - if (ret < 0) { - self->RawSink::unlock(); - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_ancillary_buffer", - -ret); - return; - } - - self->RawSink::unlock(); - self->CodedSource::lock(); - - if (self->mOutputMedia == nullptr) { - ret = self->createOutputMedia(&info, out_meta); - if (ret < 0) { - self->CodedSource::unlock(); - PDRAW_LOG_ERRNO("createOutputMedia", -ret); - return; - } - } - - /* Push the frame (unless it is silent) */ - if (!(info.info.flags & VDEF_FRAME_FLAG_SILENT)) { - outputChannelCount = - self->getOutputChannelCount(self->mOutputMedia); - for (i = 0; i < outputChannelCount; i++) { - channel = self->getOutputChannel(self->mOutputMedia, i); - if (channel == nullptr) { - PDRAW_LOGE("failed to get channel at index %d", - i); - continue; - } - ret = channel->queue(out_frame); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->queue", -ret); - } - } else { - PDRAW_LOGD("silent frame (ignored)"); - } - - self->CodedSource::unlock(); -} - - -void VideoEncoder::flushCb(struct venc_encoder *enc, void *userdata) -{ - VideoEncoder *self = (VideoEncoder *)userdata; - - if (userdata == nullptr) { - PDRAW_LOG_ERRNO("userdata", EINVAL); - return; - } - - PDRAW_LOGD("encoder is flushed"); - self->mVencFlushPending = false; - - self->completeFlush(); -} - - -void VideoEncoder::stopCb(struct venc_encoder *enc, void *userdata) -{ - VideoEncoder *self = (VideoEncoder *)userdata; - - if (userdata == nullptr) { - PDRAW_LOG_ERRNO("userdata", EINVAL); - return; - } - - PDRAW_LOGD("encoder is stopped"); - self->mVencStopPending = false; - - self->mCompleteStopPendingCount++; - self->completeStop(); -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Video encoder element + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_venc +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_encoder_video.hpp" +#include "pdraw_session.hpp" +#include "pdraw_utils.hpp" + +#include +#include + +#include + +namespace Pdraw { + + +const struct venc_cbs VideoEncoder::mEncoderCbs = { + .frame_output = &VideoEncoder::frameOutputCb, + .flush = &VideoEncoder::flushCb, + .stop = &VideoEncoder::stopCb, + .pre_release = nullptr, +}; + + +VideoEncoder::VideoEncoder(Session *session, + Element::Listener *elementListener, + CodedSource::Listener *sourceListener) : + RawToCodedFilterElement(session, + elementListener, + 1, + nullptr, + 0, + 1, + sourceListener), + mInputMedia(nullptr), mOutputMedia(nullptr), + mInputBufferPool(nullptr), mInputBufferQueue(nullptr), + mVenc(nullptr), mIsFlushed(true), + mInputChannelFlushPending(false), mVencFlushPending(false), + mVencStopPending(false), mCompleteStopPendingCount(0) +{ + const struct vdef_raw_format *supportedInputFormats; + int supportedInputFormatsCount; + + Element::setClassName(__func__); + + /* Supported input formats */ + supportedInputFormatsCount = venc_get_supported_input_formats( + VENC_ENCODER_IMPLEM_AUTO, &supportedInputFormats); + if (supportedInputFormatsCount < 0) + PDRAW_LOG_ERRNO("venc_get_supported_input_formats", + -supportedInputFormatsCount); + else + setRawVideoMediaFormatCaps(supportedInputFormats, + supportedInputFormatsCount); + + setState(CREATED); +} + + +VideoEncoder::~VideoEncoder(void) +{ + int ret; + + if (mState != STOPPED) + PDRAW_LOGW("encoder is still running"); + + if (mVenc) { + ret = venc_destroy(mVenc); + if (ret < 0) + PDRAW_LOG_ERRNO("venc_destroy", -ret); + } + + if (mOutputMedia != nullptr) + PDRAW_LOGW("output media was not properly removed"); +} + + +int VideoEncoder::start(void) +{ + int ret = 0; + + if ((mState == STARTED) || (mState == STARTING)) { + return 0; + } + if (mState != CREATED) { + PDRAW_LOGE("encoder is not created"); + return -EPROTO; + } + setState(STARTING); + + /* Get the input media and port */ + RawSink::lock(); + unsigned int inputMediaCount = getInputMediaCount(); + if (inputMediaCount != 1) { + RawSink::unlock(); + PDRAW_LOGE("invalid input media count"); + return -EPROTO; + } + Media *media = getInputMedia(0); + if (media == nullptr) { + RawSink::unlock(); + PDRAW_LOGE("invalid input media"); + return -EPROTO; + } + mInputMedia = dynamic_cast(media); + if (mInputMedia == nullptr) { + RawSink::unlock(); + PDRAW_LOGE("invalid input media"); + return -EPROTO; + } + InputPort *port = getInputPort(mInputMedia); + if (port == nullptr) { + RawSink::unlock(); + PDRAW_LOGE("invalid input port"); + return -EPROTO; + } + + /* Initialize the encoder */ + struct venc_config cfg = {}; + cfg.implem = VENC_ENCODER_IMPLEM_AUTO; + cfg.encoding = VDEF_ENCODING_H264; /* TODO */ + cfg.input.format = mInputMedia->format; + cfg.input.info = mInputMedia->info; + if ((cfg.input.info.framerate.num == 0) || + (cfg.input.info.framerate.den == 0)) { + /* TODO: this is an ugly workaround, + * mInputMedia should have a valid framerate */ + cfg.input.info.framerate.num = 30; + cfg.input.info.framerate.den = 1; + } + cfg.h264.max_bitrate = 10000000; /* TODO */ + /* TODO: Default to AVCC, see how to handle the choice later */ + cfg.output.preferred_format = VDEF_CODED_DATA_FORMAT_AVCC; + ret = venc_new(mSession->getLoop(), &cfg, &mEncoderCbs, this, &mVenc); + if (ret < 0) { + RawSink::unlock(); + PDRAW_LOG_ERRNO("venc_new", -ret); + return ret; + } + + /* Setup the input port */ + port->channel->setKey(this); + mInputBufferQueue = venc_get_input_buffer_queue(mVenc); + port->channel->setQueue(mInputBufferQueue); + mInputBufferPool = venc_get_input_buffer_pool(mVenc); + port->channel->setPool(mInputBufferPool); + + RawSink::unlock(); + + setState(STARTED); + + return 0; +} + + +int VideoEncoder::stop(void) +{ + int ret; + + if ((mState == STOPPED) || (mState == STOPPING)) + return 0; + if (mState != STARTED) { + PDRAW_LOGE("encoder is not started"); + return -EPROTO; + } + setState(STOPPING); + mVencStopPending = true; + + /* Flush everything */ + ret = flush(); + if (ret < 0) + PDRAW_LOG_ERRNO("flush", -ret); + + /* When the flush is complete, stopping will be triggered */ + return ret; +} + + +int VideoEncoder::flush(void) +{ + int ret; + unsigned int outputChannelCount, i; + CodedChannel *outputChannel; + + if (mIsFlushed) { + PDRAW_LOGD("encoder is already flushed, nothing to do"); + completeFlush(); + return 0; + } + + mVencFlushPending = true; + + /* Flush the output channels (async) */ + CodedSource::lock(); + if (mOutputMedia != nullptr) { + outputChannelCount = getOutputChannelCount(mOutputMedia); + for (i = 0; i < outputChannelCount; i++) { + outputChannel = getOutputChannel(mOutputMedia, i); + if (outputChannel == nullptr) { + PDRAW_LOGW( + "failed to get output channel " + "at index %d", + i); + continue; + } + ret = outputChannel->flush(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->flush", -ret); + } + } + CodedSource::unlock(); + + /* Flush the encoder (async) + * (the input channel queue is flushed by venc) */ + ret = venc_flush(mVenc, 1); + if (ret < 0) + PDRAW_LOG_ERRNO("venc_flush", -ret); + + return ret; +} + + +void VideoEncoder::completeFlush(void) +{ + int ret; + unsigned int outputChannelCount, i; + CodedChannel *outputChannel; + bool pending = false; + + if (mVencFlushPending) + return; + + CodedSource::lock(); + if (mOutputMedia != nullptr) { + outputChannelCount = getOutputChannelCount(mOutputMedia); + for (i = 0; i < outputChannelCount; i++) { + outputChannel = getOutputChannel(mOutputMedia, i); + if (outputChannel == nullptr) { + PDRAW_LOGW( + "failed to get output channel " + "at index %d", + i); + continue; + } + if (outputChannel->isFlushPending()) { + pending = true; + break; + } + } + } + CodedSource::unlock(); + + if (pending) + return; + + RawSink::lock(); + if (mInputMedia != nullptr) { + mIsFlushed = true; + if (mInputChannelFlushPending) { + mInputChannelFlushPending = false; + RawChannel *inputChannel = getInputChannel(mInputMedia); + if (inputChannel == nullptr) { + PDRAW_LOGE("failed to get input channel"); + } else { + ret = inputChannel->flushDone(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->flushDone", + -ret); + } + } + } + RawSink::unlock(); + + tryStop(); +} + + +int VideoEncoder::tryStop(void) +{ + int ret; + int outputChannelCount = 0, i; + + if (mState != STOPPING) + return 0; + + /* Remove the input port */ + RawSink::lock(); + if (mInputMedia != nullptr) { + RawChannel *channel = getInputChannel(mInputMedia); + if (channel == nullptr) { + PDRAW_LOGE("failed to get channel"); + } else { + channel->setQueue(nullptr); + channel->setPool(nullptr); + } + + ret = removeInputMedia(mInputMedia); + if (ret < 0) + PDRAW_LOG_ERRNO("removeInputMedia", -ret); + else + mInputMedia = nullptr; + } + RawSink::unlock(); + + /* Teardown the output channels + * Note: loop downwards because calling teardown on a channel may or + * may not synchronously remove the channel from the output port */ + CodedSource::lock(); + if (mOutputMedia != nullptr) { + outputChannelCount = getOutputChannelCount(mOutputMedia); + + for (i = outputChannelCount - 1; i >= 0; i--) { + CodedChannel *channel = + getOutputChannel(mOutputMedia, i); + if (channel == nullptr) { + PDRAW_LOGW("failed to get channel at index %d", + i); + continue; + } + ret = channel->teardown(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->teardown", -ret); + } + } + CodedSource::unlock(); + + /* Stop the encoder */ + ret = venc_stop(mVenc); + if (ret < 0) { + PDRAW_LOG_ERRNO("venc_stop", -ret); + return ret; + } + + return 0; +} + + +void VideoEncoder::completeStop(void) +{ + int ret; + unsigned int outputChannelCount; + + mCompleteStopPendingCount--; + + CodedSource::lock(); + if (mOutputMedia == nullptr) { + CodedSource::unlock(); + goto exit; + } + outputChannelCount = getOutputChannelCount(mOutputMedia); + if (outputChannelCount > 0) { + CodedSource::unlock(); + return; + } + + /* Remove the output port */ + if (CodedSource::mListener) { + CodedSource::mListener->onOutputMediaRemoved(this, + mOutputMedia); + } + ret = removeOutputPort(mOutputMedia); + if (ret < 0) { + PDRAW_LOG_ERRNO("removeOutputPort", -ret); + } else { + delete mOutputMedia; + mOutputMedia = nullptr; + } + + CodedSource::unlock(); + +exit: + if ((!mVencStopPending) && (mCompleteStopPendingCount == 0)) + setState(STOPPED); +} + + +int VideoEncoder::createOutputMedia(struct vdef_coded_frame *frame_info, + CodedVideoMedia::Frame &frame) +{ + int ret; + + CodedSource::lock(); + + mOutputMedia = new CodedVideoMedia(mSession); + if (mOutputMedia == nullptr) { + CodedSource::unlock(); + PDRAW_LOGE("output media allocation failed"); + return -ENOMEM; + } + std::string path = mInputMedia->getPath() + ">" + Element::getName() + + "$" + mOutputMedia->getName(); + mOutputMedia->setPath(path); + + ret = addOutputPort(mOutputMedia); + if (ret < 0) { + CodedSource::unlock(); + PDRAW_LOG_ERRNO("addOutputPort", -ret); + return ret; + } + + mOutputMedia->format = frame_info->format; + vdef_frame_to_format_info(&frame_info->info, &mOutputMedia->info); + mOutputMedia->info.framerate = mInputMedia->info.framerate; + mOutputMedia->sessionMeta = mInputMedia->sessionMeta; + mOutputMedia->playbackType = mInputMedia->playbackType; + mOutputMedia->duration = mInputMedia->duration; + + uint8_t *sps, *pps; + size_t spsSize = 0, ppsSize = 0; + ret = venc_get_h264_ps(mVenc, nullptr, &spsSize, nullptr, &ppsSize); + if (ret < 0) { + CodedSource::unlock(); + PDRAW_LOG_ERRNO("venc_get_h264_ps", -ret); + return ret; + } + sps = (uint8_t *)malloc(spsSize); + if (sps == nullptr) { + CodedSource::unlock(); + PDRAW_LOG_ERRNO("malloc:sps", ENOMEM); + return ret; + } + pps = (uint8_t *)malloc(ppsSize); + if (pps == nullptr) { + CodedSource::unlock(); + PDRAW_LOG_ERRNO("malloc:pps", ENOMEM); + free(sps); + return ret; + } + ret = venc_get_h264_ps(mVenc, sps, &spsSize, pps, &ppsSize); + if (ret < 0) { + CodedSource::unlock(); + PDRAW_LOG_ERRNO("venc_get_h264_ps", -ret); + free(sps); + free(pps); + return ret; + } + ret = mOutputMedia->setPs(nullptr, 0, sps, spsSize, pps, ppsSize); + if (ret < 0) { + CodedSource::unlock(); + PDRAW_LOG_ERRNO("media->setPs", -ret); + free(sps); + free(pps); + return ret; + } + free(sps); + free(pps); + + CodedSource::unlock(); + + if (CodedSource::mListener) + CodedSource::mListener->onOutputMediaAdded(this, mOutputMedia); + + return 0; +} + + +void VideoEncoder::onChannelQueue(RawChannel *channel, + struct mbuf_raw_video_frame *frame) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + if (frame == nullptr) { + PDRAW_LOG_ERRNO("frame", EINVAL); + return; + } + if (mState != STARTED) { + PDRAW_LOGE("encoder is not started"); + return; + } + if ((mVencFlushPending) || (mInputChannelFlushPending)) { + PDRAW_LOGI("frame input: flush pending, discard frame"); + return; + } + RawSink::lock(); + struct mbuf_raw_video_frame_queue *queue = channel->getQueue(); + if (queue == nullptr) { + RawSink::unlock(); + PDRAW_LOGE("invalid queue"); + return; + } + if (queue != mInputBufferQueue) { + RawSink::unlock(); + PDRAW_LOGE("invalid input buffer queue"); + return; + } + + RawSink::onChannelQueue(channel, frame); + mIsFlushed = false; + RawSink::unlock(); +} + + +void VideoEncoder::onChannelFlush(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + PDRAW_LOGD("flushing input channel"); + mInputChannelFlushPending = true; + + int ret = flush(); + if (ret < 0) + PDRAW_LOG_ERRNO("flush", -ret); +} + + +void VideoEncoder::onChannelFlushed(CodedChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); + if (media == nullptr) { + PDRAW_LOGE("media not found"); + return; + } + PDRAW_LOGD("'%s': channel flushed media name=%s (channel key=%p)", + Element::getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + completeFlush(); +} + + +void VideoEncoder::onChannelTeardown(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + PDRAW_LOGD("tearing down input channel"); + + int ret = stop(); + if (ret < 0) + PDRAW_LOG_ERRNO("stop", -ret); +} + + +void VideoEncoder::onChannelUnlink(CodedChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + CodedSource::onChannelUnlink(channel); + + if (mState == STOPPING) { + mCompleteStopPendingCount++; + completeStop(); + } +} + + +void VideoEncoder::frameOutputCb(struct venc_encoder *enc, + int status, + struct mbuf_coded_video_frame *out_frame, + void *userdata) +{ + int ret; + VideoEncoder *self = (VideoEncoder *)userdata; + struct vdef_coded_frame info; + struct mbuf_ancillary_data *ancillaryData = nullptr; + RawVideoMedia::Frame *in_meta; + CodedVideoMedia::Frame out_meta; + unsigned int outputChannelCount, i; + CodedChannel *channel; + + if (status != 0) { + PDRAW_LOGE("encoder error: %d(%s)", -status, strerror(-status)); + return; + } + + if (userdata == nullptr) { + PDRAW_LOG_ERRNO("userdata", EINVAL); + return; + } + if (out_frame == nullptr) { + PDRAW_LOG_ERRNO("out_frame", EINVAL); + return; + } + if (self->mState != STARTED) { + PDRAW_LOGE("encoder is not started"); + return; + } + if ((self->mVencFlushPending) || (self->mInputChannelFlushPending)) { + PDRAW_LOGI("frame output: flush pending, discard frame"); + return; + } + + self->RawSink::lock(); + if (self->mInputMedia == nullptr) { + self->RawSink::unlock(); + PDRAW_LOG_ERRNO("invalid input media", EPROTO); + return; + } + + ret = mbuf_coded_video_frame_get_frame_info(out_frame, &info); + if (ret < 0) { + self->RawSink::unlock(); + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_frame_info", -ret); + return; + } + ret = mbuf_coded_video_frame_get_ancillary_data( + out_frame, + PDRAW_ANCILLARY_DATA_KEY_RAWVIDEOFRAME, + &ancillaryData); + if (ret < 0) { + self->RawSink::unlock(); + PDRAW_LOG_ERRNO( + "mbuf_coded_video_frame_get_ancillary_data:pdraw_in", + -ret); + return; + } + + in_meta = (RawVideoMedia::Frame *)mbuf_ancillary_data_get_buffer( + ancillaryData, NULL); + memset(&out_meta, 0, sizeof(out_meta)); + out_meta.ntpTimestamp = in_meta->ntpTimestamp; + out_meta.ntpUnskewedTimestamp = in_meta->ntpUnskewedTimestamp; + out_meta.ntpRawTimestamp = in_meta->ntpRawTimestamp; + out_meta.ntpRawUnskewedTimestamp = in_meta->ntpRawUnskewedTimestamp; + out_meta.playTimestamp = in_meta->playTimestamp; + out_meta.captureTimestamp = in_meta->captureTimestamp; + out_meta.localTimestamp = in_meta->localTimestamp; + out_meta.localTimestampPrecision = in_meta->localTimestampPrecision; + out_meta.recvStartTimestamp = in_meta->recvStartTimestamp; + out_meta.recvEndTimestamp = in_meta->recvEndTimestamp; + out_meta.demuxOutputTimestamp = in_meta->demuxOutputTimestamp; + out_meta.encoderOutputTimestamp = pdraw_getTimestampFromMbufFrame( + out_frame, VENC_ANCILLARY_KEY_OUTPUT_TIME); + out_meta.isRef = (info.type != VDEF_CODED_FRAME_TYPE_P_NON_REF); + out_meta.isSync = ((info.type == VDEF_CODED_FRAME_TYPE_IDR) || + (info.type == VDEF_CODED_FRAME_TYPE_P_IR_START)); + ret = mbuf_ancillary_data_unref(ancillaryData); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_ancillary_data_unref", -ret); + + /* Remove the PDrAW input ancillary data */ + ret = mbuf_coded_video_frame_remove_ancillary_data( + out_frame, PDRAW_ANCILLARY_DATA_KEY_RAWVIDEOFRAME); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_remove_ancillary_data", + -ret); + + ret = mbuf_coded_video_frame_add_ancillary_buffer( + out_frame, + PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME, + &out_meta, + sizeof(out_meta)); + if (ret < 0) { + self->RawSink::unlock(); + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_ancillary_buffer", + -ret); + return; + } + + self->RawSink::unlock(); + self->CodedSource::lock(); + + if (self->mOutputMedia == nullptr) { + ret = self->createOutputMedia(&info, out_meta); + if (ret < 0) { + self->CodedSource::unlock(); + PDRAW_LOG_ERRNO("createOutputMedia", -ret); + return; + } + } + + /* Push the frame (unless it is silent) */ + if (!(info.info.flags & VDEF_FRAME_FLAG_SILENT)) { + outputChannelCount = + self->getOutputChannelCount(self->mOutputMedia); + for (i = 0; i < outputChannelCount; i++) { + channel = self->getOutputChannel(self->mOutputMedia, i); + if (channel == nullptr) { + PDRAW_LOGE("failed to get channel at index %d", + i); + continue; + } + ret = channel->queue(out_frame); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->queue", -ret); + } + } else { + PDRAW_LOGD("silent frame (ignored)"); + } + + self->CodedSource::unlock(); +} + + +void VideoEncoder::flushCb(struct venc_encoder *enc, void *userdata) +{ + VideoEncoder *self = (VideoEncoder *)userdata; + + if (userdata == nullptr) { + PDRAW_LOG_ERRNO("userdata", EINVAL); + return; + } + + PDRAW_LOGD("encoder is flushed"); + self->mVencFlushPending = false; + + self->completeFlush(); +} + + +void VideoEncoder::stopCb(struct venc_encoder *enc, void *userdata) +{ + VideoEncoder *self = (VideoEncoder *)userdata; + + if (userdata == nullptr) { + PDRAW_LOG_ERRNO("userdata", EINVAL); + return; + } + + PDRAW_LOGD("encoder is stopped"); + self->mVencStopPending = false; + + self->mCompleteStopPendingCount++; + self->completeStop(); +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_encoder_video.hpp b/libpdraw/src/pdraw_encoder_video.hpp index 2d074a5..56b6ce0 100644 --- a/libpdraw/src/pdraw_encoder_video.hpp +++ b/libpdraw/src/pdraw_encoder_video.hpp @@ -1,103 +1,103 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Video encoder element - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_ENCODER_VIDEO_HPP_ -#define _PDRAW_ENCODER_VIDEO_HPP_ - -#include "pdraw_element.hpp" - -#include - -#include -#include -#include - -namespace Pdraw { - -class VideoEncoder : public RawToCodedFilterElement { -public: - VideoEncoder(Session *session, - Element::Listener *elementListener, - CodedSource::Listener *sourceListener); - - ~VideoEncoder(void); - - int start(void); - - int stop(void); - - void completeFlush(void); - - void completeStop(void); - -private: - int createOutputMedia(struct vdef_coded_frame *frame_info, - CodedVideoMedia::Frame &frame); - - int flush(void); - - int tryStop(void); - - void onChannelQueue(RawChannel *channel, - struct mbuf_raw_video_frame *frame); - - void onChannelFlush(RawChannel *channel); - - void onChannelFlushed(CodedChannel *channel); - - void onChannelTeardown(RawChannel *channel); - - void onChannelUnlink(CodedChannel *channel); - - static void frameOutputCb(struct venc_encoder *enc, - int status, - struct mbuf_coded_video_frame *out_frame, - void *userdata); - - static void flushCb(struct venc_encoder *enc, void *userdata); - - static void stopCb(struct venc_encoder *enc, void *userdata); - - RawVideoMedia *mInputMedia; - CodedVideoMedia *mOutputMedia; - struct mbuf_pool *mInputBufferPool; - struct mbuf_raw_video_frame_queue *mInputBufferQueue; - struct venc_encoder *mVenc; - bool mIsFlushed; - bool mInputChannelFlushPending; - bool mVencFlushPending; - bool mVencStopPending; - int mCompleteStopPendingCount; - static const struct venc_cbs mEncoderCbs; -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_ENCODER_VIDEO_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Video encoder element + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_ENCODER_VIDEO_HPP_ +#define _PDRAW_ENCODER_VIDEO_HPP_ + +#include "pdraw_element.hpp" + +#include + +#include +#include +#include + +namespace Pdraw { + +class VideoEncoder : public RawToCodedFilterElement { +public: + VideoEncoder(Session *session, + Element::Listener *elementListener, + CodedSource::Listener *sourceListener); + + ~VideoEncoder(void); + + int start(void); + + int stop(void); + + void completeFlush(void); + + void completeStop(void); + +private: + int createOutputMedia(struct vdef_coded_frame *frame_info, + CodedVideoMedia::Frame &frame); + + int flush(void); + + int tryStop(void); + + void onChannelQueue(RawChannel *channel, + struct mbuf_raw_video_frame *frame); + + void onChannelFlush(RawChannel *channel); + + void onChannelFlushed(CodedChannel *channel); + + void onChannelTeardown(RawChannel *channel); + + void onChannelUnlink(CodedChannel *channel); + + static void frameOutputCb(struct venc_encoder *enc, + int status, + struct mbuf_coded_video_frame *out_frame, + void *userdata); + + static void flushCb(struct venc_encoder *enc, void *userdata); + + static void stopCb(struct venc_encoder *enc, void *userdata); + + RawVideoMedia *mInputMedia; + CodedVideoMedia *mOutputMedia; + struct mbuf_pool *mInputBufferPool; + struct mbuf_raw_video_frame_queue *mInputBufferQueue; + struct venc_encoder *mVenc; + bool mIsFlushed; + bool mInputChannelFlushPending; + bool mVencFlushPending; + bool mVencStopPending; + int mCompleteStopPendingCount; + static const struct venc_cbs mEncoderCbs; +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_ENCODER_VIDEO_HPP_ */ diff --git a/libpdraw/src/pdraw_external_coded_video_sink.cpp b/libpdraw/src/pdraw_external_coded_video_sink.cpp index beabea2..e56dac3 100644 --- a/libpdraw/src/pdraw_external_coded_video_sink.cpp +++ b/libpdraw/src/pdraw_external_coded_video_sink.cpp @@ -1,1009 +1,1009 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Application external coded video sink - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_external_coded_video_sink -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_external_coded_video_sink.hpp" -#include "pdraw_session.hpp" - -#include - -namespace Pdraw { - -#define NB_SUPPORTED_FORMATS 4 -static struct vdef_coded_format supportedFormats[NB_SUPPORTED_FORMATS]; -static pthread_once_t supportedFormatsIsInit = PTHREAD_ONCE_INIT; -static void initializeSupportedFormats(void) -{ - supportedFormats[0] = vdef_h264_byte_stream; - supportedFormats[1] = vdef_h264_avcc; - supportedFormats[2] = vdef_h265_byte_stream; - supportedFormats[3] = vdef_h265_hvcc; -} - - -ExternalCodedVideoSink::ExternalCodedVideoSink( - Session *session, - const struct vdef_coded_format *requiredCodedFormat, - Element::Listener *elementListener, - IPdraw::ICodedVideoSink::Listener *listener, - IPdraw::ICodedVideoSink *sink, - const struct pdraw_video_sink_params *params) : - CodedSinkElement(session, elementListener, 1, nullptr, 0) -{ - Element::setClassName(__func__); - mVideoSinkListener = listener; - mVideoSink = sink; - mParams = *params; - mInputMedia = nullptr; - mInputFrameQueue = nullptr; - mIsFlushed = true; - mInputChannelFlushPending = false; - mTearingDown = false; - mNeedSync = true; - mIsRef = false; - mIsRecoveryPoint = false; - mFakeFrameNum = 0; - mMaxFrameNum = 0; - mH264Reader = nullptr; - - (void)pthread_once(&supportedFormatsIsInit, initializeSupportedFormats); - - if (requiredCodedFormat && - requiredCodedFormat->data_format != - VDEF_CODED_DATA_FORMAT_UNKNOWN && - requiredCodedFormat->encoding != VDEF_ENCODING_UNKNOWN) - setCodedVideoMediaFormatCaps(requiredCodedFormat, 1); - else - setCodedVideoMediaFormatCaps(supportedFormats, - NB_SUPPORTED_FORMATS); - - setState(CREATED); -} - - -ExternalCodedVideoSink::~ExternalCodedVideoSink(void) -{ - int ret; - - if (mState == STARTED) - PDRAW_LOGW("video sink is still running"); - - /* Remove any leftover idle callbacks */ - pomp_loop_idle_remove(mSession->getLoop(), callVideoSinkFlush, this); - - /* Flush and destroy the queue */ - if (mInputFrameQueue != nullptr) { - ret = mbuf_coded_video_frame_queue_flush(mInputFrameQueue); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_queue_flush", - -ret); - ret = mbuf_coded_video_frame_queue_destroy(mInputFrameQueue); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_queue_destroy", - -ret); - mInputFrameQueue = nullptr; - } - - if (mH264Reader) { - ret = h264_reader_destroy(mH264Reader); - if (ret < 0) - PDRAW_LOG_ERRNO("h264_reader_destroy", -ret); - mH264Reader = nullptr; - } -} - - -void ExternalCodedVideoSink::naluEndCb(struct h264_ctx *ctx, - enum h264_nalu_type type, - const uint8_t *buf, - size_t len, - const struct h264_nalu_header *nh, - void *userdata) -{ - ExternalCodedVideoSink *self = - reinterpret_cast(userdata); - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - PDRAW_LOG_ERRNO_RETURN_IF(nh == nullptr, EINVAL); - - self->mIsRef = (((type == H264_NALU_TYPE_SLICE) || - (type == H264_NALU_TYPE_SLICE_IDR)) && - (nh->nal_ref_idc != 0)); -} - - -void ExternalCodedVideoSink::sliceCb(struct h264_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h264_slice_header *sh, - void *userdata) -{ - ExternalCodedVideoSink *self = - reinterpret_cast(userdata); - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - PDRAW_LOG_ERRNO_RETURN_IF(buf == nullptr, EINVAL); - PDRAW_LOG_ERRNO_RETURN_IF(sh == nullptr, EINVAL); - - /* Deliberetly remove the const of the buf to modify the frame_num */ - uint8_t *data = (uint8_t *)buf; - struct h264_slice_header *fsh; - struct h264_bitstream bs; - int res = 0; - - fsh = (h264_slice_header *)calloc(1, sizeof(*fsh)); - PDRAW_LOG_ERRNO_RETURN_IF(fsh == nullptr, EINVAL); - h264_bs_init(&bs, data, len, 1); - - *fsh = *sh; - fsh->frame_num = self->mFakeFrameNum; - - res = h264_rewrite_slice_header(&bs, ctx, fsh); - if (res < 0) - PDRAW_LOG_ERRNO("h264_rewrite_slice_header", -res); - - free(fsh); -} - - -void ExternalCodedVideoSink::spsCb(struct h264_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h264_sps *sps, - void *userdata) -{ - ExternalCodedVideoSink *self = - reinterpret_cast(userdata); - - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - PDRAW_LOG_ERRNO_RETURN_IF(sps == nullptr, EINVAL); - - self->mMaxFrameNum = 1 << (sps->log2_max_frame_num_minus4 + 4); -} - - -void ExternalCodedVideoSink::seiRecoveryPointCb( - struct h264_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h264_sei_recovery_point *sei, - void *userdata) -{ - ExternalCodedVideoSink *self = - reinterpret_cast(userdata); - - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - self->mIsRecoveryPoint = true; -} - - -const struct h264_ctx_cbs ExternalCodedVideoSink::mH264ReaderCbs = { - .au_end = nullptr, - .nalu_begin = nullptr, - .nalu_end = &ExternalCodedVideoSink::naluEndCb, - .slice = &ExternalCodedVideoSink::sliceCb, - .slice_data_begin = nullptr, - .slice_data_end = nullptr, - .slice_data_mb = nullptr, - .sps = &ExternalCodedVideoSink::spsCb, - .pps = nullptr, - .aud = nullptr, - .sei = nullptr, - .sei_buffering_period = nullptr, - .sei_pic_timing = nullptr, - .sei_pan_scan_rect = nullptr, - .sei_filler_payload = nullptr, - .sei_user_data_registered = nullptr, - .sei_user_data_unregistered = nullptr, - .sei_recovery_point = &ExternalCodedVideoSink::seiRecoveryPointCb, -}; - - -int ExternalCodedVideoSink::start(void) -{ - if ((mState == STARTED) || (mState == STARTING)) { - return 0; - } - if (mState != CREATED) { - PDRAW_LOGE("video sink is not started"); - return -EPROTO; - } - setState(STARTING); - - /* Get the input media and port */ - CodedSink::lock(); - unsigned int inputMediaCount = getInputMediaCount(); - if (inputMediaCount != 1) { - CodedSink::unlock(); - PDRAW_LOGE("invalid input media count"); - return -EPROTO; - } - mInputMedia = getInputMedia(0); - if (mInputMedia == nullptr) { - CodedSink::unlock(); - PDRAW_LOGE("invalid input media"); - return -EPROTO; - } - InputPort *port; - port = getInputPort(mInputMedia); - if (port == nullptr) { - CodedSink::unlock(); - PDRAW_LOGE("invalid input port"); - return -EPROTO; - } - - /* Create the queue */ - struct mbuf_coded_video_frame_queue_args queueArgs = {}; - queueArgs.max_frames = mParams.queue_max_count; - int res = mbuf_coded_video_frame_queue_new_with_args(&queueArgs, - &mInputFrameQueue); - if (res < 0) { - CodedSink::unlock(); - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_queue_new_with_args", - -res); - return res; - } - - res = h264_reader_new(&mH264ReaderCbs, this, &mH264Reader); - if (res < 0) { - CodedSink::unlock(); - PDRAW_LOG_ERRNO("h264_reader_new", -res); - return res; - } - - /* Setup the input port */ - port->channel->setKey(this); - port->channel->setQueue(mInputFrameQueue); - - CodedSink::unlock(); - - setState(STARTED); - - return 0; -} - - -int ExternalCodedVideoSink::stop(void) -{ - int ret; - CodedChannel *channel = nullptr; - - if ((mState == STOPPED) || (mState == STOPPING)) - return 0; - if (mState != STARTED) { - PDRAW_LOGE("video sink is not started"); - return -EPROTO; - } - setState(STOPPING); - - /* TODO: - * Since pdraw_wrapper deletes the listener right after this function is - * called, we need to remove it here to avoid any call during the - * destruction process. - * - * A proper solution for this would be to make sure that the listener is - * NOT destroyed when stop is called, but rather when setState(STOPPED); - * is called, ensuring that the listener outlives this object. - */ - Element::lock(); - mVideoSinkListener = nullptr; - Element::unlock(); - - CodedSink::lock(); - - if (mInputMedia == nullptr) { - CodedSink::unlock(); - setState(STOPPED); - return 0; - } - - channel = getInputChannel(mInputMedia); - if (channel == nullptr) { - CodedSink::unlock(); - PDRAW_LOGE("failed to get channel"); - return -EPROTO; - } - - CodedSink::unlock(); - - ret = channelTeardown(channel); - if (ret < 0) - PDRAW_LOG_ERRNO("channelTeardown", -ret); - - return 0; -} - - -int ExternalCodedVideoSink::resync(void) -{ - int ret; - CodedChannel *channel = nullptr; - - if (mState != STARTED) { - PDRAW_LOGE("video sink is not started"); - return -EPROTO; - } - - CodedSink::lock(); - - ret = flush(); - if (ret < 0) { - CodedSink::unlock(); - PDRAW_LOG_ERRNO("flush", -ret); - return ret; - } - - channel = getInputChannel(mInputMedia); - if (channel == nullptr) { - CodedSink::unlock(); - PDRAW_LOGE("failed to get channel"); - return -EPROTO; - } - - ret = channel->resync(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->resync", -ret); - - mNeedSync = true; - CodedSink::unlock(); - - return ret; -} - - -int ExternalCodedVideoSink::flush(void) -{ - if (mIsFlushed) { - PDRAW_LOGD("video sink is already flushed, nothing to do"); - int ret = flushDone(); - if (ret < 0) - PDRAW_LOG_ERRNO("flushDone", -ret); - return ret; - } - /* Signal the application for flushing */ - pomp_loop_idle_add(mSession->getLoop(), callVideoSinkFlush, this); - return 0; -} - - -int ExternalCodedVideoSink::flushDone(void) -{ - int ret; - - CodedSink::lock(); - - if (mInputMedia == nullptr) - goto exit; - - if (mInputChannelFlushPending) { - CodedChannel *channel = getInputChannel(mInputMedia); - if (channel == nullptr) { - PDRAW_LOGE("failed to get channel"); - } else { - mIsFlushed = true; - mInputChannelFlushPending = false; - ret = channel->flushDone(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->flushDone", -ret); - } - } - -exit: - CodedSink::unlock(); - - if (mState == STOPPING) - setState(STOPPED); - - return 0; -} - - -int ExternalCodedVideoSink::prepareCodedVideoFrame( - CodedChannel *channel, - struct mbuf_coded_video_frame *frame) -{ - int ret; - CodedVideoMedia::Frame *in_meta; - struct pdraw_video_frame out_meta; - struct mbuf_ancillary_data *ancillaryData = nullptr; - - struct mbuf_coded_video_frame_queue *queue = channel->getQueue(); - if (queue == nullptr) { - PDRAW_LOGE("invalid queue"); - return -ENOENT; - } - if (queue != mInputFrameQueue) { - PDRAW_LOGE("invalid input buffer queue"); - return -EPROTO; - } - - ret = mbuf_coded_video_frame_get_frame_info(frame, &out_meta.coded); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_frame_info", -ret); - return ret; - } - - /* Get the CodedVideoMedia::Frame input metadata */ - ret = mbuf_coded_video_frame_get_ancillary_data( - frame, - PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME, - &ancillaryData); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_ancillary_data", - -ret); - return ret; - } - - in_meta = (CodedVideoMedia::Frame *)mbuf_ancillary_data_get_buffer( - ancillaryData, NULL); - - if (!vdef_coded_format_intersect(&out_meta.coded.format, - mCodedVideoMediaFormatCaps, - mCodedVideoMediaFormatCapsCount)) { - PDRAW_LOGE("unsupported coded video input format"); - ret = -EPROTO; - goto out; - } - - out_meta.format = VDEF_FRAME_TYPE_CODED; - out_meta.ntp_timestamp = in_meta->ntpTimestamp; - out_meta.ntp_unskewed_timestamp = in_meta->ntpUnskewedTimestamp; - out_meta.ntp_raw_timestamp = in_meta->ntpRawTimestamp; - out_meta.ntp_raw_unskewed_timestamp = in_meta->ntpRawUnskewedTimestamp; - out_meta.play_timestamp = in_meta->playTimestamp; - out_meta.capture_timestamp = in_meta->captureTimestamp; - out_meta.local_timestamp = in_meta->localTimestamp; - out_meta.is_ref = in_meta->isRef; - out_meta.is_sync = in_meta->isSync; - - /* If the frame is handled by multiple external video sinks, this key - * might already have been filled by another sink, so we don't consider - * -EEXIST as an error */ - ret = mbuf_coded_video_frame_add_ancillary_buffer( - frame, - PDRAW_ANCILLARY_DATA_KEY_VIDEOFRAME, - &out_meta, - sizeof(out_meta)); - if (ret < 0 && ret != -EEXIST) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_ancillary_buffer", - -ret); - goto out; - } - ret = 0; - -out: - if (ancillaryData != nullptr) - mbuf_ancillary_data_unref(ancillaryData); - return ret; -} - - -int ExternalCodedVideoSink::writeGreyIdr(CodedChannel *channel, - struct CodedVideoMedia::Frame *inFrame, - struct vdef_coded_frame *inInfo, - uint64_t *ntpDelta, - uint64_t *ntpUnskewedDelta, - uint64_t *ntpRawDelta, - uint64_t *ntpRawUnskewedDelta, - uint64_t *playDelta) -{ - int ret; - struct mbuf_mem *idr_mem = nullptr; - struct mbuf_coded_video_frame *idr_frame = nullptr; - struct h264_bitstream bs = {}; - struct h264_slice_header *sh = nullptr; - uint32_t mbTotal, sc; - const uint8_t *sps, *pps; - uint8_t *data; - size_t len, spsSize, ppsSize; - struct h264_nalu_header nh = {}; - struct vdef_coded_frame idr_info; - struct pdraw_video_frame out_meta; - struct vdef_nalu nalu = {}; - - PDRAW_LOG_ERRNO_RETURN_ERR_IF(channel == nullptr, EINVAL); - PDRAW_LOG_ERRNO_RETURN_ERR_IF(inFrame == nullptr, EINVAL); - PDRAW_LOG_ERRNO_RETURN_ERR_IF(inInfo == nullptr, EINVAL); - PDRAW_LOG_ERRNO_RETURN_ERR_IF(ntpDelta == nullptr, EINVAL); - PDRAW_LOG_ERRNO_RETURN_ERR_IF(ntpUnskewedDelta == nullptr, EINVAL); - PDRAW_LOG_ERRNO_RETURN_ERR_IF(ntpRawDelta == nullptr, EINVAL); - PDRAW_LOG_ERRNO_RETURN_ERR_IF(ntpRawUnskewedDelta == nullptr, EINVAL); - PDRAW_LOG_ERRNO_RETURN_ERR_IF(playDelta == nullptr, EINVAL); - - ret = mInputMedia->getPs( - nullptr, nullptr, &sps, &spsSize, &pps, &ppsSize); - if (ret < 0) { - PDRAW_LOG_ERRNO("media->getPs", -ret); - return ret; - } - - /* Give SPS and PPS to h264_reader */ - ret = h264_reader_parse_nalu(mH264Reader, 0, sps, spsSize); - if (ret < 0) { - PDRAW_LOG_ERRNO("h264_reader_parse_nalu", -ret); - return ret; - } - - ret = h264_reader_parse_nalu(mH264Reader, 0, pps, ppsSize); - if (ret < 0) { - PDRAW_LOG_ERRNO("h264_reader_parse_nalu", -ret); - return ret; - } - - struct h264_ctx *ctx = h264_reader_get_ctx(mH264Reader); - size_t outBufSize = mInputMedia->info.resolution.width * - mInputMedia->info.resolution.height * 3 / 4; - - if (!vdef_coded_format_intersect(&inInfo->format, - mCodedVideoMediaFormatCaps, - mCodedVideoMediaFormatCapsCount)) { - PDRAW_LOGE("unsupported coded video input format"); - return -EPROTO; - } - - /* Create buffer */ - ret = mbuf_mem_generic_new(outBufSize, &idr_mem); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_mem_generic_new", -ret); - goto out; - } - - idr_info = *inInfo; - idr_info.type = VDEF_CODED_FRAME_TYPE_IDR; - idr_info.info.flags |= VDEF_FRAME_FLAG_SILENT; - - out_meta.format = VDEF_FRAME_TYPE_CODED; - out_meta.is_ref = 1; - out_meta.is_sync = 1; - out_meta.ntp_timestamp = inFrame->ntpTimestamp; - out_meta.ntp_unskewed_timestamp = inFrame->ntpUnskewedTimestamp; - out_meta.ntp_raw_timestamp = inFrame->ntpRawTimestamp; - out_meta.ntp_raw_unskewed_timestamp = inFrame->ntpRawUnskewedTimestamp; - out_meta.play_timestamp = inFrame->playTimestamp; - out_meta.capture_timestamp = 0; - out_meta.local_timestamp = 0; - - *ntpDelta = (inFrame->ntpTimestamp == 0) ? 1 : 0; - if (inFrame->ntpTimestamp != 0) - out_meta.ntp_timestamp--; - - *ntpUnskewedDelta = (inFrame->ntpTimestamp == 0) ? 1 : 0; - if (inFrame->ntpTimestamp != 0) - out_meta.ntp_unskewed_timestamp--; - - *ntpRawDelta = (inFrame->ntpTimestamp == 0) ? 1 : 0; - if (inFrame->ntpTimestamp != 0) - out_meta.ntp_raw_timestamp--; - - *ntpRawUnskewedDelta = (inFrame->ntpTimestamp == 0) ? 1 : 0; - if (inFrame->ntpTimestamp != 0) - out_meta.ntp_raw_unskewed_timestamp--; - - *playDelta = (inFrame->ntpTimestamp == 0) ? 1 : 0; - if (inFrame->ntpTimestamp != 0) - out_meta.play_timestamp--; - - /* Start NALU */ - ret = h264_ctx_clear_nalu(ctx); - if (ret < 0) { - PDRAW_LOG_ERRNO("h264_ctx_clear_nalu", -ret); - goto out; - } - - /* Setup NALU header */ - nh.nal_ref_idc = 3; - nh.nal_unit_type = H264_NALU_TYPE_SLICE_IDR; - ret = h264_ctx_set_nalu_header(ctx, &nh); - if (ret < 0) { - PDRAW_LOG_ERRNO("h264_ctx_set_nalu_header", -ret); - goto out; - } - - /* Setup slice header */ - sh = (struct h264_slice_header *)calloc(1, sizeof(*sh)); - if (sh == nullptr) { - ret = -ENOMEM; - PDRAW_LOG_ERRNO("calloc", -ret); - goto out; - } - sh->first_mb_in_slice = 0; - sh->slice_type = H264_SLICE_TYPE_I; - sh->frame_num = 0; - sh->pic_order_cnt_lsb = 0; - sh->redundant_pic_cnt = 0; - sh->direct_spatial_mv_pred_flag = 0; - sh->slice_qp_delta = 0; - sh->disable_deblocking_filter_idc = 2; - sh->slice_alpha_c0_offset_div2 = 0; - sh->slice_beta_offset_div2 = 0; - ret = h264_ctx_set_slice_header(ctx, sh); - if (ret < 0) { - PDRAW_LOG_ERRNO("h264_ctx_set_slice_header", -ret); - goto out; - } - - /* Setup bitstream */ - ret = mbuf_mem_get_data(idr_mem, (void **)&data, &len); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_mem_get_data", -ret); - goto out; - } - - if (len <= 4) { - ret = -ENOBUFS; - PDRAW_LOG_ERRNO("", -ret); - goto out; - } - - h264_bs_init(&bs, data + 4, len - 4, 1); - - /* Write slice */ - mbTotal = (mInputMedia->info.resolution.width + 15) / 16 * - (mInputMedia->info.resolution.height + 15) / 16; - - ret = h264_write_grey_i_slice(&bs, ctx, mbTotal); - if (ret < 0) { - PDRAW_LOG_ERRNO("h264_write_grey_i_slice", -ret); - goto out; - } - - switch (inInfo->format.data_format) { - case VDEF_CODED_DATA_FORMAT_AVCC: - sc = htonl(bs.off); - memcpy(data, &sc, sizeof(sc)); - break; - case VDEF_CODED_DATA_FORMAT_BYTE_STREAM: - sc = htonl(0x00000001); - memcpy(data, &sc, sizeof(sc)); - break; - default: - ret = -ENOSYS; - PDRAW_LOG_ERRNO("unsupported data format", -ret); - goto out; - } - - ret = mbuf_coded_video_frame_new(&idr_info, &idr_frame); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_new", -ret); - goto out; - } - nalu.size = bs.off + 4; - nalu.h264.type = H264_NALU_TYPE_SLICE_IDR; - nalu.h264.slice_type = H264_SLICE_TYPE_I; - nalu.h264.slice_mb_count = mbTotal; - ret = mbuf_coded_video_frame_add_nalu(idr_frame, idr_mem, 0, &nalu); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_nalu", -ret); - goto out; - } - ret = mbuf_coded_video_frame_finalize(idr_frame); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_finalize", -ret); - goto out; - } - ret = mbuf_coded_video_frame_add_ancillary_buffer( - idr_frame, - PDRAW_ANCILLARY_DATA_KEY_VIDEOFRAME, - &out_meta, - sizeof(out_meta)); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_ancillary_buffer", - -ret); - goto out; - } - - CodedSink::onChannelQueue(channel, idr_frame); - mIsFlushed = false; -out: - free(sh); - if (idr_mem) - mbuf_mem_unref(idr_mem); - if (idr_frame) - mbuf_coded_video_frame_unref(idr_frame); - return ret; -} - - -void ExternalCodedVideoSink::onChannelQueue( - CodedChannel *channel, - struct mbuf_coded_video_frame *frame) -{ - int ret; - uint64_t ntpDelta = 0, ntpUnskewedDelta = 0, ntpRawDelta = 0, - ntpRawUnskewedDelta = 0, playDelta = 0; - size_t off; - bool isIdr = false; - - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - if (frame == nullptr) { - PDRAW_LOG_ERRNO("frame", EINVAL); - return; - } - if (mState != STARTED) { - PDRAW_LOGE("video sink is not started"); - return; - } - if (mInputChannelFlushPending) { - PDRAW_LOGI("frame input: flush pending, discard frame"); - return; - } - CodedSink::lock(); - - if (mInputMedia->format.encoding == VDEF_ENCODING_H264) { - struct vdef_coded_frame frame_info; - mIsRef = false; - mIsRecoveryPoint = false; - - ret = mbuf_coded_video_frame_get_frame_info(frame, &frame_info); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_frame_info", - -ret); - goto end; - } - - isIdr = (frame_info.type == VDEF_CODED_FRAME_TYPE_IDR); - - if (isIdr) { - mNeedSync = false; - mFakeFrameNum = 0; - mIsRef = true; - } else if (mNeedSync) { - struct mbuf_ancillary_data *adata; - struct CodedVideoMedia::Frame *meta; - ret = mbuf_coded_video_frame_get_ancillary_data( - frame, - PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME, - &adata); - if (ret < 0) { - PDRAW_LOG_ERRNO( - "mbuf_coded_video_frame_get_ancillary_data", - -ret); - goto end; - } - meta = (struct CodedVideoMedia::Frame *) - mbuf_ancillary_data_get_buffer(adata, NULL); - ret = writeGreyIdr(channel, - meta, - &frame_info, - &ntpDelta, - &ntpUnskewedDelta, - &ntpRawDelta, - &ntpRawUnskewedDelta, - &playDelta); - mbuf_ancillary_data_unref(adata); - if (ret < 0) { - PDRAW_LOG_ERRNO("writeGreyIdr", -ret); - goto end; - } - mNeedSync = false; - mFakeFrameNum = 1; - - meta->ntpTimestamp += ntpDelta; - meta->ntpUnskewedTimestamp += ntpUnskewedDelta; - meta->ntpRawTimestamp += ntpRawDelta; - meta->ntpRawUnskewedTimestamp += ntpRawUnskewedDelta; - meta->playTimestamp += playDelta; - } - - if (mParams.fake_frame_num) { - ssize_t tmp; - size_t frame_len; - struct mbuf_mem *copy_mem; - struct mbuf_coded_video_frame *copy_frame; - void *data; - - tmp = mbuf_coded_video_frame_get_packed_size(frame); - if (tmp < 0) { - ret = tmp; - PDRAW_LOG_ERRNO( - "mbuf_coded_video_frame_get_packed_size", - -ret); - goto end; - } - frame_len = tmp; - - ret = mbuf_mem_generic_new(frame_len, ©_mem); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_mem_generic_new", -ret); - goto end; - } - - ret = mbuf_coded_video_frame_copy( - frame, copy_mem, ©_frame); - if (ret < 0) { - mbuf_mem_unref(copy_mem); - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_copy", - -ret); - goto end; - } - - /* Modify the frame */ - ret = mbuf_mem_get_data(copy_mem, &data, &frame_len); - if (ret < 0) { - mbuf_mem_unref(copy_mem); - mbuf_coded_video_frame_unref(copy_frame); - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_copy", - -ret); - goto end; - } - - ret = h264_reader_parse(mH264Reader, - 0, - (const uint8_t *)data, - frame_len, - &off); - if (ret < 0) { - mbuf_mem_unref(copy_mem); - mbuf_coded_video_frame_unref(copy_frame); - PDRAW_LOG_ERRNO("h264_reader_parse", -ret); - goto end; - } - - ret = mbuf_coded_video_frame_finalize(copy_frame); - if (ret < 0) { - mbuf_mem_unref(copy_mem); - mbuf_coded_video_frame_unref(copy_frame); - PDRAW_LOG_ERRNO( - "mbuf_coded_video_frame_finalize", - -ret); - goto end; - } - - if (mIsRef) { - /* Update the fake frame_num */ - mFakeFrameNum = (mIsRecoveryPoint) - ? 1 - : (mFakeFrameNum + 1) % - mMaxFrameNum; - } - ret = prepareCodedVideoFrame(channel, copy_frame); - mbuf_mem_unref(copy_mem); - mbuf_coded_video_frame_unref(copy_frame); - goto end; - } - } - ret = prepareCodedVideoFrame(channel, frame); - -end: - if (ret < 0) { - CodedSink::unlock(); - return; - } - - CodedSink::onChannelQueue(channel, frame); - mIsFlushed = false; - CodedSink::unlock(); -} - - -void ExternalCodedVideoSink::onChannelFlush(CodedChannel *channel) -{ - int ret; - - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - PDRAW_LOGD("flushing input channel"); - mInputChannelFlushPending = true; - - ret = flush(); - if (ret < 0) - PDRAW_LOG_ERRNO("flush", -ret); -} - - -void ExternalCodedVideoSink::onChannelTeardown(CodedChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - PDRAW_LOGD("tearing down input channel"); - - int ret = channelTeardown(channel); - if (ret < 0) - PDRAW_LOG_ERRNO("channelTeardown", -ret); -} - - -int ExternalCodedVideoSink::channelTeardown(CodedChannel *channel) -{ - int ret; - - if (channel == nullptr) - return -EINVAL; - - CodedSink::lock(); - - if (mInputMedia == nullptr) { - /* The channel is already torn down, nothing more to do */ - CodedSink::unlock(); - return 0; - } - - if (mTearingDown) { - /* The teardown may already be in progress but mInputMedia - * is not yet set to nullptr. - * Eg. removeInputMedia() utimately calls the app's - * mediaRemoved() callback, which can call the VideoSink - * stop() function, which calls channelTeardown() again. */ - CodedSink::unlock(); - return 0; - } - mTearingDown = true; - - /* Remove the input port */ - channel->setQueue(nullptr); - - ret = removeInputMedia(mInputMedia); - if (ret < 0) - PDRAW_LOG_ERRNO("removeInputMedia", -ret); - else - mInputMedia = nullptr; - - mTearingDown = false; - CodedSink::unlock(); - - ret = flush(); - if (ret < 0) - PDRAW_LOG_ERRNO("flush", -ret); - - return ret; -} - -/** - * Video sink listener calls from idle functions - */ -void ExternalCodedVideoSink::callVideoSinkFlush(void *userdata) -{ - ExternalCodedVideoSink *self = - reinterpret_cast(userdata); - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - IPdraw::ICodedVideoSink::Listener *listener = - self->getVideoSinkListener(); - - if (listener == nullptr) { - self->flushDone(); - } else { - listener->onCodedVideoSinkFlush(self->mSession, - self->getVideoSink()); - } -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Application external coded video sink + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_external_coded_video_sink +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_external_coded_video_sink.hpp" +#include "pdraw_session.hpp" + +#include + +namespace Pdraw { + +#define NB_SUPPORTED_FORMATS 4 +static struct vdef_coded_format supportedFormats[NB_SUPPORTED_FORMATS]; +static pthread_once_t supportedFormatsIsInit = PTHREAD_ONCE_INIT; +static void initializeSupportedFormats(void) +{ + supportedFormats[0] = vdef_h264_byte_stream; + supportedFormats[1] = vdef_h264_avcc; + supportedFormats[2] = vdef_h265_byte_stream; + supportedFormats[3] = vdef_h265_hvcc; +} + + +ExternalCodedVideoSink::ExternalCodedVideoSink( + Session *session, + const struct vdef_coded_format *requiredCodedFormat, + Element::Listener *elementListener, + IPdraw::ICodedVideoSink::Listener *listener, + IPdraw::ICodedVideoSink *sink, + const struct pdraw_video_sink_params *params) : + CodedSinkElement(session, elementListener, 1, nullptr, 0) +{ + Element::setClassName(__func__); + mVideoSinkListener = listener; + mVideoSink = sink; + mParams = *params; + mInputMedia = nullptr; + mInputFrameQueue = nullptr; + mIsFlushed = true; + mInputChannelFlushPending = false; + mTearingDown = false; + mNeedSync = true; + mIsRef = false; + mIsRecoveryPoint = false; + mFakeFrameNum = 0; + mMaxFrameNum = 0; + mH264Reader = nullptr; + + (void)pthread_once(&supportedFormatsIsInit, initializeSupportedFormats); + + if (requiredCodedFormat && + requiredCodedFormat->data_format != + VDEF_CODED_DATA_FORMAT_UNKNOWN && + requiredCodedFormat->encoding != VDEF_ENCODING_UNKNOWN) + setCodedVideoMediaFormatCaps(requiredCodedFormat, 1); + else + setCodedVideoMediaFormatCaps(supportedFormats, + NB_SUPPORTED_FORMATS); + + setState(CREATED); +} + + +ExternalCodedVideoSink::~ExternalCodedVideoSink(void) +{ + int ret; + + if (mState == STARTED) + PDRAW_LOGW("video sink is still running"); + + /* Remove any leftover idle callbacks */ + pomp_loop_idle_remove(mSession->getLoop(), callVideoSinkFlush, this); + + /* Flush and destroy the queue */ + if (mInputFrameQueue != nullptr) { + ret = mbuf_coded_video_frame_queue_flush(mInputFrameQueue); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_queue_flush", + -ret); + ret = mbuf_coded_video_frame_queue_destroy(mInputFrameQueue); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_queue_destroy", + -ret); + mInputFrameQueue = nullptr; + } + + if (mH264Reader) { + ret = h264_reader_destroy(mH264Reader); + if (ret < 0) + PDRAW_LOG_ERRNO("h264_reader_destroy", -ret); + mH264Reader = nullptr; + } +} + + +void ExternalCodedVideoSink::naluEndCb(struct h264_ctx *ctx, + enum h264_nalu_type type, + const uint8_t *buf, + size_t len, + const struct h264_nalu_header *nh, + void *userdata) +{ + ExternalCodedVideoSink *self = + reinterpret_cast(userdata); + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + PDRAW_LOG_ERRNO_RETURN_IF(nh == nullptr, EINVAL); + + self->mIsRef = (((type == H264_NALU_TYPE_SLICE) || + (type == H264_NALU_TYPE_SLICE_IDR)) && + (nh->nal_ref_idc != 0)); +} + + +void ExternalCodedVideoSink::sliceCb(struct h264_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h264_slice_header *sh, + void *userdata) +{ + ExternalCodedVideoSink *self = + reinterpret_cast(userdata); + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + PDRAW_LOG_ERRNO_RETURN_IF(buf == nullptr, EINVAL); + PDRAW_LOG_ERRNO_RETURN_IF(sh == nullptr, EINVAL); + + /* Deliberetly remove the const of the buf to modify the frame_num */ + uint8_t *data = (uint8_t *)buf; + struct h264_slice_header *fsh; + struct h264_bitstream bs; + int res = 0; + + fsh = (h264_slice_header *)calloc(1, sizeof(*fsh)); + PDRAW_LOG_ERRNO_RETURN_IF(fsh == nullptr, EINVAL); + h264_bs_init(&bs, data, len, 1); + + *fsh = *sh; + fsh->frame_num = self->mFakeFrameNum; + + res = h264_rewrite_slice_header(&bs, ctx, fsh); + if (res < 0) + PDRAW_LOG_ERRNO("h264_rewrite_slice_header", -res); + + free(fsh); +} + + +void ExternalCodedVideoSink::spsCb(struct h264_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h264_sps *sps, + void *userdata) +{ + ExternalCodedVideoSink *self = + reinterpret_cast(userdata); + + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + PDRAW_LOG_ERRNO_RETURN_IF(sps == nullptr, EINVAL); + + self->mMaxFrameNum = 1 << (sps->log2_max_frame_num_minus4 + 4); +} + + +void ExternalCodedVideoSink::seiRecoveryPointCb( + struct h264_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h264_sei_recovery_point *sei, + void *userdata) +{ + ExternalCodedVideoSink *self = + reinterpret_cast(userdata); + + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + self->mIsRecoveryPoint = true; +} + + +const struct h264_ctx_cbs ExternalCodedVideoSink::mH264ReaderCbs = { + .au_end = nullptr, + .nalu_begin = nullptr, + .nalu_end = &ExternalCodedVideoSink::naluEndCb, + .slice = &ExternalCodedVideoSink::sliceCb, + .slice_data_begin = nullptr, + .slice_data_end = nullptr, + .slice_data_mb = nullptr, + .sps = &ExternalCodedVideoSink::spsCb, + .pps = nullptr, + .aud = nullptr, + .sei = nullptr, + .sei_buffering_period = nullptr, + .sei_pic_timing = nullptr, + .sei_pan_scan_rect = nullptr, + .sei_filler_payload = nullptr, + .sei_user_data_registered = nullptr, + .sei_user_data_unregistered = nullptr, + .sei_recovery_point = &ExternalCodedVideoSink::seiRecoveryPointCb, +}; + + +int ExternalCodedVideoSink::start(void) +{ + if ((mState == STARTED) || (mState == STARTING)) { + return 0; + } + if (mState != CREATED) { + PDRAW_LOGE("video sink is not started"); + return -EPROTO; + } + setState(STARTING); + + /* Get the input media and port */ + CodedSink::lock(); + unsigned int inputMediaCount = getInputMediaCount(); + if (inputMediaCount != 1) { + CodedSink::unlock(); + PDRAW_LOGE("invalid input media count"); + return -EPROTO; + } + mInputMedia = getInputMedia(0); + if (mInputMedia == nullptr) { + CodedSink::unlock(); + PDRAW_LOGE("invalid input media"); + return -EPROTO; + } + InputPort *port; + port = getInputPort(mInputMedia); + if (port == nullptr) { + CodedSink::unlock(); + PDRAW_LOGE("invalid input port"); + return -EPROTO; + } + + /* Create the queue */ + struct mbuf_coded_video_frame_queue_args queueArgs = {}; + queueArgs.max_frames = mParams.queue_max_count; + int res = mbuf_coded_video_frame_queue_new_with_args(&queueArgs, + &mInputFrameQueue); + if (res < 0) { + CodedSink::unlock(); + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_queue_new_with_args", + -res); + return res; + } + + res = h264_reader_new(&mH264ReaderCbs, this, &mH264Reader); + if (res < 0) { + CodedSink::unlock(); + PDRAW_LOG_ERRNO("h264_reader_new", -res); + return res; + } + + /* Setup the input port */ + port->channel->setKey(this); + port->channel->setQueue(mInputFrameQueue); + + CodedSink::unlock(); + + setState(STARTED); + + return 0; +} + + +int ExternalCodedVideoSink::stop(void) +{ + int ret; + CodedChannel *channel = nullptr; + + if ((mState == STOPPED) || (mState == STOPPING)) + return 0; + if (mState != STARTED) { + PDRAW_LOGE("video sink is not started"); + return -EPROTO; + } + setState(STOPPING); + + /* TODO: + * Since pdraw_wrapper deletes the listener right after this function is + * called, we need to remove it here to avoid any call during the + * destruction process. + * + * A proper solution for this would be to make sure that the listener is + * NOT destroyed when stop is called, but rather when setState(STOPPED); + * is called, ensuring that the listener outlives this object. + */ + Element::lock(); + mVideoSinkListener = nullptr; + Element::unlock(); + + CodedSink::lock(); + + if (mInputMedia == nullptr) { + CodedSink::unlock(); + setState(STOPPED); + return 0; + } + + channel = getInputChannel(mInputMedia); + if (channel == nullptr) { + CodedSink::unlock(); + PDRAW_LOGE("failed to get channel"); + return -EPROTO; + } + + CodedSink::unlock(); + + ret = channelTeardown(channel); + if (ret < 0) + PDRAW_LOG_ERRNO("channelTeardown", -ret); + + return 0; +} + + +int ExternalCodedVideoSink::resync(void) +{ + int ret; + CodedChannel *channel = nullptr; + + if (mState != STARTED) { + PDRAW_LOGE("video sink is not started"); + return -EPROTO; + } + + CodedSink::lock(); + + ret = flush(); + if (ret < 0) { + CodedSink::unlock(); + PDRAW_LOG_ERRNO("flush", -ret); + return ret; + } + + channel = getInputChannel(mInputMedia); + if (channel == nullptr) { + CodedSink::unlock(); + PDRAW_LOGE("failed to get channel"); + return -EPROTO; + } + + ret = channel->resync(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->resync", -ret); + + mNeedSync = true; + CodedSink::unlock(); + + return ret; +} + + +int ExternalCodedVideoSink::flush(void) +{ + if (mIsFlushed) { + PDRAW_LOGD("video sink is already flushed, nothing to do"); + int ret = flushDone(); + if (ret < 0) + PDRAW_LOG_ERRNO("flushDone", -ret); + return ret; + } + /* Signal the application for flushing */ + pomp_loop_idle_add(mSession->getLoop(), callVideoSinkFlush, this); + return 0; +} + + +int ExternalCodedVideoSink::flushDone(void) +{ + int ret; + + CodedSink::lock(); + + if (mInputMedia == nullptr) + goto exit; + + if (mInputChannelFlushPending) { + CodedChannel *channel = getInputChannel(mInputMedia); + if (channel == nullptr) { + PDRAW_LOGE("failed to get channel"); + } else { + mIsFlushed = true; + mInputChannelFlushPending = false; + ret = channel->flushDone(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->flushDone", -ret); + } + } + +exit: + CodedSink::unlock(); + + if (mState == STOPPING) + setState(STOPPED); + + return 0; +} + + +int ExternalCodedVideoSink::prepareCodedVideoFrame( + CodedChannel *channel, + struct mbuf_coded_video_frame *frame) +{ + int ret; + CodedVideoMedia::Frame *in_meta; + struct pdraw_video_frame out_meta; + struct mbuf_ancillary_data *ancillaryData = nullptr; + + struct mbuf_coded_video_frame_queue *queue = channel->getQueue(); + if (queue == nullptr) { + PDRAW_LOGE("invalid queue"); + return -ENOENT; + } + if (queue != mInputFrameQueue) { + PDRAW_LOGE("invalid input buffer queue"); + return -EPROTO; + } + + ret = mbuf_coded_video_frame_get_frame_info(frame, &out_meta.coded); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_frame_info", -ret); + return ret; + } + + /* Get the CodedVideoMedia::Frame input metadata */ + ret = mbuf_coded_video_frame_get_ancillary_data( + frame, + PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME, + &ancillaryData); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_ancillary_data", + -ret); + return ret; + } + + in_meta = (CodedVideoMedia::Frame *)mbuf_ancillary_data_get_buffer( + ancillaryData, NULL); + + if (!vdef_coded_format_intersect(&out_meta.coded.format, + mCodedVideoMediaFormatCaps, + mCodedVideoMediaFormatCapsCount)) { + PDRAW_LOGE("unsupported coded video input format"); + ret = -EPROTO; + goto out; + } + + out_meta.format = VDEF_FRAME_TYPE_CODED; + out_meta.ntp_timestamp = in_meta->ntpTimestamp; + out_meta.ntp_unskewed_timestamp = in_meta->ntpUnskewedTimestamp; + out_meta.ntp_raw_timestamp = in_meta->ntpRawTimestamp; + out_meta.ntp_raw_unskewed_timestamp = in_meta->ntpRawUnskewedTimestamp; + out_meta.play_timestamp = in_meta->playTimestamp; + out_meta.capture_timestamp = in_meta->captureTimestamp; + out_meta.local_timestamp = in_meta->localTimestamp; + out_meta.is_ref = in_meta->isRef; + out_meta.is_sync = in_meta->isSync; + + /* If the frame is handled by multiple external video sinks, this key + * might already have been filled by another sink, so we don't consider + * -EEXIST as an error */ + ret = mbuf_coded_video_frame_add_ancillary_buffer( + frame, + PDRAW_ANCILLARY_DATA_KEY_VIDEOFRAME, + &out_meta, + sizeof(out_meta)); + if (ret < 0 && ret != -EEXIST) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_ancillary_buffer", + -ret); + goto out; + } + ret = 0; + +out: + if (ancillaryData != nullptr) + mbuf_ancillary_data_unref(ancillaryData); + return ret; +} + + +int ExternalCodedVideoSink::writeGreyIdr(CodedChannel *channel, + struct CodedVideoMedia::Frame *inFrame, + struct vdef_coded_frame *inInfo, + uint64_t *ntpDelta, + uint64_t *ntpUnskewedDelta, + uint64_t *ntpRawDelta, + uint64_t *ntpRawUnskewedDelta, + uint64_t *playDelta) +{ + int ret; + struct mbuf_mem *idr_mem = nullptr; + struct mbuf_coded_video_frame *idr_frame = nullptr; + struct h264_bitstream bs = {}; + struct h264_slice_header *sh = nullptr; + uint32_t mbTotal, sc; + const uint8_t *sps, *pps; + uint8_t *data; + size_t len, spsSize, ppsSize; + struct h264_nalu_header nh = {}; + struct vdef_coded_frame idr_info; + struct pdraw_video_frame out_meta; + struct vdef_nalu nalu = {}; + + PDRAW_LOG_ERRNO_RETURN_ERR_IF(channel == nullptr, EINVAL); + PDRAW_LOG_ERRNO_RETURN_ERR_IF(inFrame == nullptr, EINVAL); + PDRAW_LOG_ERRNO_RETURN_ERR_IF(inInfo == nullptr, EINVAL); + PDRAW_LOG_ERRNO_RETURN_ERR_IF(ntpDelta == nullptr, EINVAL); + PDRAW_LOG_ERRNO_RETURN_ERR_IF(ntpUnskewedDelta == nullptr, EINVAL); + PDRAW_LOG_ERRNO_RETURN_ERR_IF(ntpRawDelta == nullptr, EINVAL); + PDRAW_LOG_ERRNO_RETURN_ERR_IF(ntpRawUnskewedDelta == nullptr, EINVAL); + PDRAW_LOG_ERRNO_RETURN_ERR_IF(playDelta == nullptr, EINVAL); + + ret = mInputMedia->getPs( + nullptr, nullptr, &sps, &spsSize, &pps, &ppsSize); + if (ret < 0) { + PDRAW_LOG_ERRNO("media->getPs", -ret); + return ret; + } + + /* Give SPS and PPS to h264_reader */ + ret = h264_reader_parse_nalu(mH264Reader, 0, sps, spsSize); + if (ret < 0) { + PDRAW_LOG_ERRNO("h264_reader_parse_nalu", -ret); + return ret; + } + + ret = h264_reader_parse_nalu(mH264Reader, 0, pps, ppsSize); + if (ret < 0) { + PDRAW_LOG_ERRNO("h264_reader_parse_nalu", -ret); + return ret; + } + + struct h264_ctx *ctx = h264_reader_get_ctx(mH264Reader); + size_t outBufSize = mInputMedia->info.resolution.width * + mInputMedia->info.resolution.height * 3 / 4; + + if (!vdef_coded_format_intersect(&inInfo->format, + mCodedVideoMediaFormatCaps, + mCodedVideoMediaFormatCapsCount)) { + PDRAW_LOGE("unsupported coded video input format"); + return -EPROTO; + } + + /* Create buffer */ + ret = mbuf_mem_generic_new(outBufSize, &idr_mem); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_mem_generic_new", -ret); + goto out; + } + + idr_info = *inInfo; + idr_info.type = VDEF_CODED_FRAME_TYPE_IDR; + idr_info.info.flags |= VDEF_FRAME_FLAG_SILENT; + + out_meta.format = VDEF_FRAME_TYPE_CODED; + out_meta.is_ref = 1; + out_meta.is_sync = 1; + out_meta.ntp_timestamp = inFrame->ntpTimestamp; + out_meta.ntp_unskewed_timestamp = inFrame->ntpUnskewedTimestamp; + out_meta.ntp_raw_timestamp = inFrame->ntpRawTimestamp; + out_meta.ntp_raw_unskewed_timestamp = inFrame->ntpRawUnskewedTimestamp; + out_meta.play_timestamp = inFrame->playTimestamp; + out_meta.capture_timestamp = 0; + out_meta.local_timestamp = 0; + + *ntpDelta = (inFrame->ntpTimestamp == 0) ? 1 : 0; + if (inFrame->ntpTimestamp != 0) + out_meta.ntp_timestamp--; + + *ntpUnskewedDelta = (inFrame->ntpTimestamp == 0) ? 1 : 0; + if (inFrame->ntpTimestamp != 0) + out_meta.ntp_unskewed_timestamp--; + + *ntpRawDelta = (inFrame->ntpTimestamp == 0) ? 1 : 0; + if (inFrame->ntpTimestamp != 0) + out_meta.ntp_raw_timestamp--; + + *ntpRawUnskewedDelta = (inFrame->ntpTimestamp == 0) ? 1 : 0; + if (inFrame->ntpTimestamp != 0) + out_meta.ntp_raw_unskewed_timestamp--; + + *playDelta = (inFrame->ntpTimestamp == 0) ? 1 : 0; + if (inFrame->ntpTimestamp != 0) + out_meta.play_timestamp--; + + /* Start NALU */ + ret = h264_ctx_clear_nalu(ctx); + if (ret < 0) { + PDRAW_LOG_ERRNO("h264_ctx_clear_nalu", -ret); + goto out; + } + + /* Setup NALU header */ + nh.nal_ref_idc = 3; + nh.nal_unit_type = H264_NALU_TYPE_SLICE_IDR; + ret = h264_ctx_set_nalu_header(ctx, &nh); + if (ret < 0) { + PDRAW_LOG_ERRNO("h264_ctx_set_nalu_header", -ret); + goto out; + } + + /* Setup slice header */ + sh = (struct h264_slice_header *)calloc(1, sizeof(*sh)); + if (sh == nullptr) { + ret = -ENOMEM; + PDRAW_LOG_ERRNO("calloc", -ret); + goto out; + } + sh->first_mb_in_slice = 0; + sh->slice_type = H264_SLICE_TYPE_I; + sh->frame_num = 0; + sh->pic_order_cnt_lsb = 0; + sh->redundant_pic_cnt = 0; + sh->direct_spatial_mv_pred_flag = 0; + sh->slice_qp_delta = 0; + sh->disable_deblocking_filter_idc = 2; + sh->slice_alpha_c0_offset_div2 = 0; + sh->slice_beta_offset_div2 = 0; + ret = h264_ctx_set_slice_header(ctx, sh); + if (ret < 0) { + PDRAW_LOG_ERRNO("h264_ctx_set_slice_header", -ret); + goto out; + } + + /* Setup bitstream */ + ret = mbuf_mem_get_data(idr_mem, (void **)&data, &len); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_mem_get_data", -ret); + goto out; + } + + if (len <= 4) { + ret = -ENOBUFS; + PDRAW_LOG_ERRNO("", -ret); + goto out; + } + + h264_bs_init(&bs, data + 4, len - 4, 1); + + /* Write slice */ + mbTotal = (mInputMedia->info.resolution.width + 15) / 16 * + (mInputMedia->info.resolution.height + 15) / 16; + + ret = h264_write_grey_i_slice(&bs, ctx, mbTotal); + if (ret < 0) { + PDRAW_LOG_ERRNO("h264_write_grey_i_slice", -ret); + goto out; + } + + switch (inInfo->format.data_format) { + case VDEF_CODED_DATA_FORMAT_AVCC: + sc = htonl(bs.off); + memcpy(data, &sc, sizeof(sc)); + break; + case VDEF_CODED_DATA_FORMAT_BYTE_STREAM: + sc = htonl(0x00000001); + memcpy(data, &sc, sizeof(sc)); + break; + default: + ret = -ENOSYS; + PDRAW_LOG_ERRNO("unsupported data format", -ret); + goto out; + } + + ret = mbuf_coded_video_frame_new(&idr_info, &idr_frame); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_new", -ret); + goto out; + } + nalu.size = bs.off + 4; + nalu.h264.type = H264_NALU_TYPE_SLICE_IDR; + nalu.h264.slice_type = H264_SLICE_TYPE_I; + nalu.h264.slice_mb_count = mbTotal; + ret = mbuf_coded_video_frame_add_nalu(idr_frame, idr_mem, 0, &nalu); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_nalu", -ret); + goto out; + } + ret = mbuf_coded_video_frame_finalize(idr_frame); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_finalize", -ret); + goto out; + } + ret = mbuf_coded_video_frame_add_ancillary_buffer( + idr_frame, + PDRAW_ANCILLARY_DATA_KEY_VIDEOFRAME, + &out_meta, + sizeof(out_meta)); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_add_ancillary_buffer", + -ret); + goto out; + } + + CodedSink::onChannelQueue(channel, idr_frame); + mIsFlushed = false; +out: + free(sh); + if (idr_mem) + mbuf_mem_unref(idr_mem); + if (idr_frame) + mbuf_coded_video_frame_unref(idr_frame); + return ret; +} + + +void ExternalCodedVideoSink::onChannelQueue( + CodedChannel *channel, + struct mbuf_coded_video_frame *frame) +{ + int ret; + uint64_t ntpDelta = 0, ntpUnskewedDelta = 0, ntpRawDelta = 0, + ntpRawUnskewedDelta = 0, playDelta = 0; + size_t off; + bool isIdr = false; + + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + if (frame == nullptr) { + PDRAW_LOG_ERRNO("frame", EINVAL); + return; + } + if (mState != STARTED) { + PDRAW_LOGE("video sink is not started"); + return; + } + if (mInputChannelFlushPending) { + PDRAW_LOGI("frame input: flush pending, discard frame"); + return; + } + CodedSink::lock(); + + if (mInputMedia->format.encoding == VDEF_ENCODING_H264) { + struct vdef_coded_frame frame_info; + mIsRef = false; + mIsRecoveryPoint = false; + + ret = mbuf_coded_video_frame_get_frame_info(frame, &frame_info); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_frame_info", + -ret); + goto end; + } + + isIdr = (frame_info.type == VDEF_CODED_FRAME_TYPE_IDR); + + if (isIdr) { + mNeedSync = false; + mFakeFrameNum = 0; + mIsRef = true; + } else if (mNeedSync) { + struct mbuf_ancillary_data *adata; + struct CodedVideoMedia::Frame *meta; + ret = mbuf_coded_video_frame_get_ancillary_data( + frame, + PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME, + &adata); + if (ret < 0) { + PDRAW_LOG_ERRNO( + "mbuf_coded_video_frame_get_ancillary_data", + -ret); + goto end; + } + meta = (struct CodedVideoMedia::Frame *) + mbuf_ancillary_data_get_buffer(adata, NULL); + ret = writeGreyIdr(channel, + meta, + &frame_info, + &ntpDelta, + &ntpUnskewedDelta, + &ntpRawDelta, + &ntpRawUnskewedDelta, + &playDelta); + mbuf_ancillary_data_unref(adata); + if (ret < 0) { + PDRAW_LOG_ERRNO("writeGreyIdr", -ret); + goto end; + } + mNeedSync = false; + mFakeFrameNum = 1; + + meta->ntpTimestamp += ntpDelta; + meta->ntpUnskewedTimestamp += ntpUnskewedDelta; + meta->ntpRawTimestamp += ntpRawDelta; + meta->ntpRawUnskewedTimestamp += ntpRawUnskewedDelta; + meta->playTimestamp += playDelta; + } + + if (mParams.fake_frame_num) { + ssize_t tmp; + size_t frame_len; + struct mbuf_mem *copy_mem; + struct mbuf_coded_video_frame *copy_frame; + void *data; + + tmp = mbuf_coded_video_frame_get_packed_size(frame); + if (tmp < 0) { + ret = tmp; + PDRAW_LOG_ERRNO( + "mbuf_coded_video_frame_get_packed_size", + -ret); + goto end; + } + frame_len = tmp; + + ret = mbuf_mem_generic_new(frame_len, ©_mem); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_mem_generic_new", -ret); + goto end; + } + + ret = mbuf_coded_video_frame_copy( + frame, copy_mem, ©_frame); + if (ret < 0) { + mbuf_mem_unref(copy_mem); + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_copy", + -ret); + goto end; + } + + /* Modify the frame */ + ret = mbuf_mem_get_data(copy_mem, &data, &frame_len); + if (ret < 0) { + mbuf_mem_unref(copy_mem); + mbuf_coded_video_frame_unref(copy_frame); + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_copy", + -ret); + goto end; + } + + ret = h264_reader_parse(mH264Reader, + 0, + (const uint8_t *)data, + frame_len, + &off); + if (ret < 0) { + mbuf_mem_unref(copy_mem); + mbuf_coded_video_frame_unref(copy_frame); + PDRAW_LOG_ERRNO("h264_reader_parse", -ret); + goto end; + } + + ret = mbuf_coded_video_frame_finalize(copy_frame); + if (ret < 0) { + mbuf_mem_unref(copy_mem); + mbuf_coded_video_frame_unref(copy_frame); + PDRAW_LOG_ERRNO( + "mbuf_coded_video_frame_finalize", + -ret); + goto end; + } + + if (mIsRef) { + /* Update the fake frame_num */ + mFakeFrameNum = (mIsRecoveryPoint) + ? 1 + : (mFakeFrameNum + 1) % + mMaxFrameNum; + } + ret = prepareCodedVideoFrame(channel, copy_frame); + mbuf_mem_unref(copy_mem); + mbuf_coded_video_frame_unref(copy_frame); + goto end; + } + } + ret = prepareCodedVideoFrame(channel, frame); + +end: + if (ret < 0) { + CodedSink::unlock(); + return; + } + + CodedSink::onChannelQueue(channel, frame); + mIsFlushed = false; + CodedSink::unlock(); +} + + +void ExternalCodedVideoSink::onChannelFlush(CodedChannel *channel) +{ + int ret; + + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + PDRAW_LOGD("flushing input channel"); + mInputChannelFlushPending = true; + + ret = flush(); + if (ret < 0) + PDRAW_LOG_ERRNO("flush", -ret); +} + + +void ExternalCodedVideoSink::onChannelTeardown(CodedChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + PDRAW_LOGD("tearing down input channel"); + + int ret = channelTeardown(channel); + if (ret < 0) + PDRAW_LOG_ERRNO("channelTeardown", -ret); +} + + +int ExternalCodedVideoSink::channelTeardown(CodedChannel *channel) +{ + int ret; + + if (channel == nullptr) + return -EINVAL; + + CodedSink::lock(); + + if (mInputMedia == nullptr) { + /* The channel is already torn down, nothing more to do */ + CodedSink::unlock(); + return 0; + } + + if (mTearingDown) { + /* The teardown may already be in progress but mInputMedia + * is not yet set to nullptr. + * Eg. removeInputMedia() utimately calls the app's + * mediaRemoved() callback, which can call the VideoSink + * stop() function, which calls channelTeardown() again. */ + CodedSink::unlock(); + return 0; + } + mTearingDown = true; + + /* Remove the input port */ + channel->setQueue(nullptr); + + ret = removeInputMedia(mInputMedia); + if (ret < 0) + PDRAW_LOG_ERRNO("removeInputMedia", -ret); + else + mInputMedia = nullptr; + + mTearingDown = false; + CodedSink::unlock(); + + ret = flush(); + if (ret < 0) + PDRAW_LOG_ERRNO("flush", -ret); + + return ret; +} + +/** + * Video sink listener calls from idle functions + */ +void ExternalCodedVideoSink::callVideoSinkFlush(void *userdata) +{ + ExternalCodedVideoSink *self = + reinterpret_cast(userdata); + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + IPdraw::ICodedVideoSink::Listener *listener = + self->getVideoSinkListener(); + + if (listener == nullptr) { + self->flushDone(); + } else { + listener->onCodedVideoSinkFlush(self->mSession, + self->getVideoSink()); + } +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_external_coded_video_sink.hpp b/libpdraw/src/pdraw_external_coded_video_sink.hpp index c6f9631..33501bc 100644 --- a/libpdraw/src/pdraw_external_coded_video_sink.hpp +++ b/libpdraw/src/pdraw_external_coded_video_sink.hpp @@ -1,153 +1,153 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Application external coded video sink - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_EXTERNAL_CODED_VIDEO_SINK_HPP_ -#define _PDRAW_EXTERNAL_CODED_VIDEO_SINK_HPP_ - -#include "pdraw_element.hpp" - -#include - -#include -#include -#include - -namespace Pdraw { - - -class ExternalCodedVideoSink : public CodedSinkElement { -public: - ExternalCodedVideoSink( - Session *session, - const struct vdef_coded_format *requiredCodedFormat, - Element::Listener *elementListener, - IPdraw::ICodedVideoSink::Listener *listener, - IPdraw::ICodedVideoSink *sink, - const struct pdraw_video_sink_params *params); - - ~ExternalCodedVideoSink(void); - - int start(void); - - int stop(void); - - int resync(void); - - - int flushDone(void); - - struct mbuf_coded_video_frame_queue *getQueue(void) - { - return mInputFrameQueue; - } - - IPdraw::ICodedVideoSink *getVideoSink(void) - { - return mVideoSink; - } - - IPdraw::ICodedVideoSink::Listener *getVideoSinkListener(void) - { - return mVideoSinkListener; - } - -private: - int flush(void); - - int channelTeardown(CodedChannel *channel); - - void onChannelQueue(CodedChannel *channel, - struct mbuf_coded_video_frame *frame); - - void onChannelFlush(CodedChannel *channel); - - void onChannelTeardown(CodedChannel *channel); - - int prepareCodedVideoFrame(CodedChannel *channel, - struct mbuf_coded_video_frame *frame); - - int writeGreyIdr(CodedChannel *channel, - struct CodedVideoMedia::Frame *inFrame, - struct vdef_coded_frame *inInfo, - uint64_t *ntpDelta, - uint64_t *ntpUnskewedDelta, - uint64_t *ntpRawDelta, - uint64_t *ntpRawUnskewedDelta, - uint64_t *playDelta); - - static void naluEndCb(struct h264_ctx *ctx, - enum h264_nalu_type type, - const uint8_t *buf, - size_t len, - const struct h264_nalu_header *nh, - void *userdata); - - static void sliceCb(struct h264_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h264_slice_header *sh, - void *userdata); - - static void spsCb(struct h264_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h264_sps *sps, - void *userdata); - - static void - seiRecoveryPointCb(struct h264_ctx *ctx, - const uint8_t *buf, - size_t len, - const struct h264_sei_recovery_point *sei, - void *userdata); - - /* Video sink listener calls from idle functions */ - static void callVideoSinkFlush(void *userdata); - - IPdraw::ICodedVideoSink *mVideoSink; - IPdraw::ICodedVideoSink::Listener *mVideoSinkListener; - struct pdraw_video_sink_params mParams; - CodedVideoMedia *mInputMedia; - struct mbuf_coded_video_frame_queue *mInputFrameQueue; - bool mIsFlushed; - bool mInputChannelFlushPending; - bool mTearingDown; - bool mNeedSync; - struct h264_reader *mH264Reader; - bool mIsRef; - bool mIsRecoveryPoint; - unsigned int mFakeFrameNum; - unsigned int mMaxFrameNum; - static const struct h264_ctx_cbs mH264ReaderCbs; -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_EXTERNAL_CODED_VIDEO_SINK_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Application external coded video sink + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_EXTERNAL_CODED_VIDEO_SINK_HPP_ +#define _PDRAW_EXTERNAL_CODED_VIDEO_SINK_HPP_ + +#include "pdraw_element.hpp" + +#include + +#include +#include +#include + +namespace Pdraw { + + +class ExternalCodedVideoSink : public CodedSinkElement { +public: + ExternalCodedVideoSink( + Session *session, + const struct vdef_coded_format *requiredCodedFormat, + Element::Listener *elementListener, + IPdraw::ICodedVideoSink::Listener *listener, + IPdraw::ICodedVideoSink *sink, + const struct pdraw_video_sink_params *params); + + ~ExternalCodedVideoSink(void); + + int start(void); + + int stop(void); + + int resync(void); + + + int flushDone(void); + + struct mbuf_coded_video_frame_queue *getQueue(void) + { + return mInputFrameQueue; + } + + IPdraw::ICodedVideoSink *getVideoSink(void) + { + return mVideoSink; + } + + IPdraw::ICodedVideoSink::Listener *getVideoSinkListener(void) + { + return mVideoSinkListener; + } + +private: + int flush(void); + + int channelTeardown(CodedChannel *channel); + + void onChannelQueue(CodedChannel *channel, + struct mbuf_coded_video_frame *frame); + + void onChannelFlush(CodedChannel *channel); + + void onChannelTeardown(CodedChannel *channel); + + int prepareCodedVideoFrame(CodedChannel *channel, + struct mbuf_coded_video_frame *frame); + + int writeGreyIdr(CodedChannel *channel, + struct CodedVideoMedia::Frame *inFrame, + struct vdef_coded_frame *inInfo, + uint64_t *ntpDelta, + uint64_t *ntpUnskewedDelta, + uint64_t *ntpRawDelta, + uint64_t *ntpRawUnskewedDelta, + uint64_t *playDelta); + + static void naluEndCb(struct h264_ctx *ctx, + enum h264_nalu_type type, + const uint8_t *buf, + size_t len, + const struct h264_nalu_header *nh, + void *userdata); + + static void sliceCb(struct h264_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h264_slice_header *sh, + void *userdata); + + static void spsCb(struct h264_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h264_sps *sps, + void *userdata); + + static void + seiRecoveryPointCb(struct h264_ctx *ctx, + const uint8_t *buf, + size_t len, + const struct h264_sei_recovery_point *sei, + void *userdata); + + /* Video sink listener calls from idle functions */ + static void callVideoSinkFlush(void *userdata); + + IPdraw::ICodedVideoSink *mVideoSink; + IPdraw::ICodedVideoSink::Listener *mVideoSinkListener; + struct pdraw_video_sink_params mParams; + CodedVideoMedia *mInputMedia; + struct mbuf_coded_video_frame_queue *mInputFrameQueue; + bool mIsFlushed; + bool mInputChannelFlushPending; + bool mTearingDown; + bool mNeedSync; + struct h264_reader *mH264Reader; + bool mIsRef; + bool mIsRecoveryPoint; + unsigned int mFakeFrameNum; + unsigned int mMaxFrameNum; + static const struct h264_ctx_cbs mH264ReaderCbs; +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_EXTERNAL_CODED_VIDEO_SINK_HPP_ */ diff --git a/libpdraw/src/pdraw_external_raw_video_sink.cpp b/libpdraw/src/pdraw_external_raw_video_sink.cpp index e73d68a..f484b62 100644 --- a/libpdraw/src/pdraw_external_raw_video_sink.cpp +++ b/libpdraw/src/pdraw_external_raw_video_sink.cpp @@ -1,471 +1,471 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Application external raw video sink - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_external_raw_video_sink -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_external_raw_video_sink.hpp" -#include "pdraw_session.hpp" - -#include - -namespace Pdraw { - - -#define NB_SUPPORTED_FORMATS 4 -static struct vdef_raw_format supportedFormats[NB_SUPPORTED_FORMATS]; -static pthread_once_t supportedFormatsIsInit = PTHREAD_ONCE_INIT; -static void initializeSupportedFormats(void) -{ - supportedFormats[0] = vdef_i420; - supportedFormats[1] = vdef_nv12; - supportedFormats[2] = vdef_i420_10_16le; - supportedFormats[3] = vdef_nv12_10_16le_high; -} - - -ExternalRawVideoSink::ExternalRawVideoSink( - Session *session, - Element::Listener *elementListener, - IPdraw::IRawVideoSink::Listener *listener, - IPdraw::IRawVideoSink *sink, - const struct pdraw_video_sink_params *params) : - RawSinkElement(session, elementListener, 1, nullptr, 0) -{ - Element::setClassName(__func__); - mVideoSinkListener = listener; - mVideoSink = sink; - mParams = *params; - mInputMedia = nullptr; - mInputFrameQueue = nullptr; - mIsFlushed = true; - mInputChannelFlushPending = false; - mTearingDown = false; - - (void)pthread_once(&supportedFormatsIsInit, initializeSupportedFormats); - setRawVideoMediaFormatCaps(supportedFormats, NB_SUPPORTED_FORMATS); - - setState(CREATED); -} - - -ExternalRawVideoSink::~ExternalRawVideoSink(void) -{ - int ret; - - if (mState == STARTED) - PDRAW_LOGW("video sink is still running"); - - /* Remove any leftover idle callbacks */ - pomp_loop_idle_remove(mSession->getLoop(), callVideoSinkFlush, this); - - /* Flush and destroy the queue */ - if (mInputFrameQueue != nullptr) { - ret = mbuf_raw_video_frame_queue_flush(mInputFrameQueue); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_flush", - -ret); - ret = mbuf_raw_video_frame_queue_destroy(mInputFrameQueue); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_destroy", - -ret); - mInputFrameQueue = nullptr; - } -} - - -int ExternalRawVideoSink::start(void) -{ - if ((mState == STARTED) || (mState == STARTING)) { - return 0; - } - if (mState != CREATED) { - PDRAW_LOGE("video sink is not started"); - return -EPROTO; - } - setState(STARTING); - - /* Get the input media and port */ - RawSink::lock(); - unsigned int inputMediaCount = getInputMediaCount(); - if (inputMediaCount != 1) { - RawSink::unlock(); - PDRAW_LOGE("invalid input media count"); - return -EPROTO; - } - mInputMedia = getInputMedia(0); - if (mInputMedia == nullptr) { - RawSink::unlock(); - PDRAW_LOGE("invalid input media"); - return -EPROTO; - } - InputPort *port; - port = getInputPort(mInputMedia); - if (port == nullptr) { - RawSink::unlock(); - PDRAW_LOGE("invalid input port"); - return -EPROTO; - } - - /* Create the queue */ - struct mbuf_raw_video_frame_queue_args queueArgs = {}; - queueArgs.max_frames = mParams.queue_max_count; - int res = mbuf_raw_video_frame_queue_new_with_args(&queueArgs, - &mInputFrameQueue); - if (res < 0) { - RawSink::unlock(); - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_new_with_args", - -res); - return res; - } - - /* Setup the input port */ - port->channel->setKey(this); - port->channel->setQueue(mInputFrameQueue); - - RawSink::unlock(); - - setState(STARTED); - - return 0; -} - - -int ExternalRawVideoSink::stop(void) -{ - int ret; - RawChannel *channel = nullptr; - - if ((mState == STOPPED) || (mState == STOPPING)) - return 0; - if (mState != STARTED) { - PDRAW_LOGE("video sink is not started"); - return -EPROTO; - } - setState(STOPPING); - - /* TODO: - * Since pdraw_wrapper deletes the listener right after this function is - * called, we need to remove it here to avoid any call during the - * destruction process. - * - * A proper solution for this would be to make sure that the listener is - * NOT destroyed when stop is called, but rather when setState(STOPPED); - * is called, ensuring that the listener outlives this object. - */ - Element::lock(); - mVideoSinkListener = nullptr; - Element::unlock(); - - RawSink::lock(); - - if (mInputMedia == nullptr) { - RawSink::unlock(); - setState(STOPPED); - return 0; - } - - channel = getInputChannel(mInputMedia); - if (channel == nullptr) { - RawSink::unlock(); - PDRAW_LOGE("failed to get channel"); - return -EPROTO; - } - - RawSink::unlock(); - - ret = channelTeardown(channel); - if (ret < 0) - PDRAW_LOG_ERRNO("channelTeardown", -ret); - - return 0; -} - - -int ExternalRawVideoSink::flush(void) -{ - if (mIsFlushed) { - PDRAW_LOGD("video sink is already flushed, nothing to do"); - int ret = flushDone(); - if (ret < 0) - PDRAW_LOG_ERRNO("flushDone", -ret); - return ret; - } - /* Signal the application for flushing */ - pomp_loop_idle_add(mSession->getLoop(), callVideoSinkFlush, this); - return 0; -} - - -int ExternalRawVideoSink::flushDone(void) -{ - int ret; - - RawSink::lock(); - - if (mInputMedia == nullptr) - goto exit; - - if (mInputChannelFlushPending) { - RawChannel *channel = getInputChannel(mInputMedia); - if (channel == nullptr) { - PDRAW_LOGE("failed to get channel"); - } else { - mIsFlushed = true; - mInputChannelFlushPending = false; - ret = channel->flushDone(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->flushDone", -ret); - } - } - -exit: - RawSink::unlock(); - - if (mState == STOPPING) - setState(STOPPED); - - return 0; -} - - -int ExternalRawVideoSink::prepareRawVideoFrame( - RawChannel *channel, - struct mbuf_raw_video_frame *frame) -{ - int ret; - RawVideoMedia::Frame *in_meta; - struct pdraw_video_frame out_meta; - struct mbuf_ancillary_data *ancillaryData = nullptr; - - if (mInputMedia == nullptr) { - PDRAW_LOGE("invalid input media"); - return -ENOENT; - } - struct mbuf_raw_video_frame_queue *queue = channel->getQueue(); - if (queue == nullptr) { - PDRAW_LOGE("invalid queue"); - return -ENOENT; - } - if (queue != mInputFrameQueue) { - PDRAW_LOGE("invalid input buffer queue"); - return -EPROTO; - } - - ret = mbuf_raw_video_frame_get_frame_info(frame, &out_meta.raw); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_frame_info", -ret); - return ret; - } - - /* Get the RawVideoMedia::Frame input metadata */ - ret = mbuf_raw_video_frame_get_ancillary_data( - frame, PDRAW_ANCILLARY_DATA_KEY_RAWVIDEOFRAME, &ancillaryData); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_ancillary_data", - -ret); - return ret; - } - - in_meta = (RawVideoMedia::Frame *)mbuf_ancillary_data_get_buffer( - ancillaryData, NULL); - - if (!vdef_raw_format_intersect(&out_meta.raw.format, - mRawVideoMediaFormatCaps, - mRawVideoMediaFormatCapsCount)) { - PDRAW_LOGE("unsupported raw video input format"); - return -EPROTO; - } - out_meta.format = VDEF_FRAME_TYPE_RAW; - out_meta.ntp_timestamp = in_meta->ntpTimestamp; - out_meta.ntp_unskewed_timestamp = in_meta->ntpUnskewedTimestamp; - out_meta.ntp_raw_timestamp = in_meta->ntpRawTimestamp; - out_meta.ntp_raw_unskewed_timestamp = in_meta->ntpRawUnskewedTimestamp; - out_meta.play_timestamp = in_meta->playTimestamp; - out_meta.capture_timestamp = in_meta->captureTimestamp; - out_meta.local_timestamp = in_meta->localTimestamp; - - /* If the frame is handled by multuple external video sinks, this key - * might already have been filled by another sink, so we don't consider - * -EEXIST as an error */ - ret = mbuf_raw_video_frame_add_ancillary_buffer( - frame, - PDRAW_ANCILLARY_DATA_KEY_VIDEOFRAME, - &out_meta, - sizeof(out_meta)); - if (ret < 0 && ret != -EEXIST) { - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_add_ancillary_buffer", - -ret); - goto out; - } - ret = 0; - -out: - if (ancillaryData != nullptr) - mbuf_ancillary_data_unref(ancillaryData); - return ret; -} - - -void ExternalRawVideoSink::onChannelQueue(RawChannel *channel, - struct mbuf_raw_video_frame *frame) -{ - int ret; - - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - if (frame == nullptr) { - PDRAW_LOG_ERRNO("frame", EINVAL); - return; - } - if (mState != STARTED) { - PDRAW_LOGE("video sink is not started"); - return; - } - if (mInputChannelFlushPending) { - PDRAW_LOGI("frame input: flush pending, discard frame"); - return; - } - RawSink::lock(); - - ret = prepareRawVideoFrame(channel, frame); - if (ret < 0) { - RawSink::unlock(); - return; - } - - RawSink::onChannelQueue(channel, frame); - mIsFlushed = false; - RawSink::unlock(); -} - - -void ExternalRawVideoSink::onChannelFlush(RawChannel *channel) -{ - int ret; - - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - PDRAW_LOGD("flushing input channel"); - mInputChannelFlushPending = true; - - ret = flush(); - if (ret < 0) - PDRAW_LOG_ERRNO("flush", -ret); -} - - -void ExternalRawVideoSink::onChannelTeardown(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - PDRAW_LOGD("tearing down input channel"); - - int ret = channelTeardown(channel); - if (ret < 0) - PDRAW_LOG_ERRNO("channelTeardown", -ret); -} - - -int ExternalRawVideoSink::channelTeardown(RawChannel *channel) -{ - int ret; - - if (channel == nullptr) - return -EINVAL; - - RawSink::lock(); - - if (mInputMedia == nullptr) { - /* The channel is already torn down, nothing more to do */ - RawSink::unlock(); - return 0; - } - - if (mTearingDown) { - /* The teardown may already be in progress but mInputMedia - * is not yet set to nullptr. - * Eg. removeInputMedia() utimately calls the app's - * mediaRemoved() callback, which can call the VideoSink - * stop() function, which calls channelTeardown() again. */ - RawSink::unlock(); - return 0; - } - mTearingDown = true; - - /* Remove the input port */ - channel->setQueue(nullptr); - - ret = removeInputMedia(mInputMedia); - if (ret < 0) - PDRAW_LOG_ERRNO("removeInputMedia", -ret); - else - mInputMedia = nullptr; - - mTearingDown = false; - RawSink::unlock(); - - ret = flush(); - if (ret < 0) - PDRAW_LOG_ERRNO("flush", -ret); - - return ret; -} - -/** - * Video sink listener calls from idle functions - */ -void ExternalRawVideoSink::callVideoSinkFlush(void *userdata) -{ - ExternalRawVideoSink *self = - reinterpret_cast(userdata); - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - IPdraw::IRawVideoSink::Listener *listener = - self->getVideoSinkListener(); - - if (listener == nullptr) { - self->flushDone(); - } else { - listener->onRawVideoSinkFlush(self->mSession, - self->getVideoSink()); - } -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Application external raw video sink + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_external_raw_video_sink +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_external_raw_video_sink.hpp" +#include "pdraw_session.hpp" + +#include + +namespace Pdraw { + + +#define NB_SUPPORTED_FORMATS 4 +static struct vdef_raw_format supportedFormats[NB_SUPPORTED_FORMATS]; +static pthread_once_t supportedFormatsIsInit = PTHREAD_ONCE_INIT; +static void initializeSupportedFormats(void) +{ + supportedFormats[0] = vdef_i420; + supportedFormats[1] = vdef_nv12; + supportedFormats[2] = vdef_i420_10_16le; + supportedFormats[3] = vdef_nv12_10_16le_high; +} + + +ExternalRawVideoSink::ExternalRawVideoSink( + Session *session, + Element::Listener *elementListener, + IPdraw::IRawVideoSink::Listener *listener, + IPdraw::IRawVideoSink *sink, + const struct pdraw_video_sink_params *params) : + RawSinkElement(session, elementListener, 1, nullptr, 0) +{ + Element::setClassName(__func__); + mVideoSinkListener = listener; + mVideoSink = sink; + mParams = *params; + mInputMedia = nullptr; + mInputFrameQueue = nullptr; + mIsFlushed = true; + mInputChannelFlushPending = false; + mTearingDown = false; + + (void)pthread_once(&supportedFormatsIsInit, initializeSupportedFormats); + setRawVideoMediaFormatCaps(supportedFormats, NB_SUPPORTED_FORMATS); + + setState(CREATED); +} + + +ExternalRawVideoSink::~ExternalRawVideoSink(void) +{ + int ret; + + if (mState == STARTED) + PDRAW_LOGW("video sink is still running"); + + /* Remove any leftover idle callbacks */ + pomp_loop_idle_remove(mSession->getLoop(), callVideoSinkFlush, this); + + /* Flush and destroy the queue */ + if (mInputFrameQueue != nullptr) { + ret = mbuf_raw_video_frame_queue_flush(mInputFrameQueue); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_flush", + -ret); + ret = mbuf_raw_video_frame_queue_destroy(mInputFrameQueue); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_destroy", + -ret); + mInputFrameQueue = nullptr; + } +} + + +int ExternalRawVideoSink::start(void) +{ + if ((mState == STARTED) || (mState == STARTING)) { + return 0; + } + if (mState != CREATED) { + PDRAW_LOGE("video sink is not started"); + return -EPROTO; + } + setState(STARTING); + + /* Get the input media and port */ + RawSink::lock(); + unsigned int inputMediaCount = getInputMediaCount(); + if (inputMediaCount != 1) { + RawSink::unlock(); + PDRAW_LOGE("invalid input media count"); + return -EPROTO; + } + mInputMedia = getInputMedia(0); + if (mInputMedia == nullptr) { + RawSink::unlock(); + PDRAW_LOGE("invalid input media"); + return -EPROTO; + } + InputPort *port; + port = getInputPort(mInputMedia); + if (port == nullptr) { + RawSink::unlock(); + PDRAW_LOGE("invalid input port"); + return -EPROTO; + } + + /* Create the queue */ + struct mbuf_raw_video_frame_queue_args queueArgs = {}; + queueArgs.max_frames = mParams.queue_max_count; + int res = mbuf_raw_video_frame_queue_new_with_args(&queueArgs, + &mInputFrameQueue); + if (res < 0) { + RawSink::unlock(); + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_new_with_args", + -res); + return res; + } + + /* Setup the input port */ + port->channel->setKey(this); + port->channel->setQueue(mInputFrameQueue); + + RawSink::unlock(); + + setState(STARTED); + + return 0; +} + + +int ExternalRawVideoSink::stop(void) +{ + int ret; + RawChannel *channel = nullptr; + + if ((mState == STOPPED) || (mState == STOPPING)) + return 0; + if (mState != STARTED) { + PDRAW_LOGE("video sink is not started"); + return -EPROTO; + } + setState(STOPPING); + + /* TODO: + * Since pdraw_wrapper deletes the listener right after this function is + * called, we need to remove it here to avoid any call during the + * destruction process. + * + * A proper solution for this would be to make sure that the listener is + * NOT destroyed when stop is called, but rather when setState(STOPPED); + * is called, ensuring that the listener outlives this object. + */ + Element::lock(); + mVideoSinkListener = nullptr; + Element::unlock(); + + RawSink::lock(); + + if (mInputMedia == nullptr) { + RawSink::unlock(); + setState(STOPPED); + return 0; + } + + channel = getInputChannel(mInputMedia); + if (channel == nullptr) { + RawSink::unlock(); + PDRAW_LOGE("failed to get channel"); + return -EPROTO; + } + + RawSink::unlock(); + + ret = channelTeardown(channel); + if (ret < 0) + PDRAW_LOG_ERRNO("channelTeardown", -ret); + + return 0; +} + + +int ExternalRawVideoSink::flush(void) +{ + if (mIsFlushed) { + PDRAW_LOGD("video sink is already flushed, nothing to do"); + int ret = flushDone(); + if (ret < 0) + PDRAW_LOG_ERRNO("flushDone", -ret); + return ret; + } + /* Signal the application for flushing */ + pomp_loop_idle_add(mSession->getLoop(), callVideoSinkFlush, this); + return 0; +} + + +int ExternalRawVideoSink::flushDone(void) +{ + int ret; + + RawSink::lock(); + + if (mInputMedia == nullptr) + goto exit; + + if (mInputChannelFlushPending) { + RawChannel *channel = getInputChannel(mInputMedia); + if (channel == nullptr) { + PDRAW_LOGE("failed to get channel"); + } else { + mIsFlushed = true; + mInputChannelFlushPending = false; + ret = channel->flushDone(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->flushDone", -ret); + } + } + +exit: + RawSink::unlock(); + + if (mState == STOPPING) + setState(STOPPED); + + return 0; +} + + +int ExternalRawVideoSink::prepareRawVideoFrame( + RawChannel *channel, + struct mbuf_raw_video_frame *frame) +{ + int ret; + RawVideoMedia::Frame *in_meta; + struct pdraw_video_frame out_meta; + struct mbuf_ancillary_data *ancillaryData = nullptr; + + if (mInputMedia == nullptr) { + PDRAW_LOGE("invalid input media"); + return -ENOENT; + } + struct mbuf_raw_video_frame_queue *queue = channel->getQueue(); + if (queue == nullptr) { + PDRAW_LOGE("invalid queue"); + return -ENOENT; + } + if (queue != mInputFrameQueue) { + PDRAW_LOGE("invalid input buffer queue"); + return -EPROTO; + } + + ret = mbuf_raw_video_frame_get_frame_info(frame, &out_meta.raw); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_frame_info", -ret); + return ret; + } + + /* Get the RawVideoMedia::Frame input metadata */ + ret = mbuf_raw_video_frame_get_ancillary_data( + frame, PDRAW_ANCILLARY_DATA_KEY_RAWVIDEOFRAME, &ancillaryData); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_ancillary_data", + -ret); + return ret; + } + + in_meta = (RawVideoMedia::Frame *)mbuf_ancillary_data_get_buffer( + ancillaryData, NULL); + + if (!vdef_raw_format_intersect(&out_meta.raw.format, + mRawVideoMediaFormatCaps, + mRawVideoMediaFormatCapsCount)) { + PDRAW_LOGE("unsupported raw video input format"); + return -EPROTO; + } + out_meta.format = VDEF_FRAME_TYPE_RAW; + out_meta.ntp_timestamp = in_meta->ntpTimestamp; + out_meta.ntp_unskewed_timestamp = in_meta->ntpUnskewedTimestamp; + out_meta.ntp_raw_timestamp = in_meta->ntpRawTimestamp; + out_meta.ntp_raw_unskewed_timestamp = in_meta->ntpRawUnskewedTimestamp; + out_meta.play_timestamp = in_meta->playTimestamp; + out_meta.capture_timestamp = in_meta->captureTimestamp; + out_meta.local_timestamp = in_meta->localTimestamp; + + /* If the frame is handled by multuple external video sinks, this key + * might already have been filled by another sink, so we don't consider + * -EEXIST as an error */ + ret = mbuf_raw_video_frame_add_ancillary_buffer( + frame, + PDRAW_ANCILLARY_DATA_KEY_VIDEOFRAME, + &out_meta, + sizeof(out_meta)); + if (ret < 0 && ret != -EEXIST) { + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_add_ancillary_buffer", + -ret); + goto out; + } + ret = 0; + +out: + if (ancillaryData != nullptr) + mbuf_ancillary_data_unref(ancillaryData); + return ret; +} + + +void ExternalRawVideoSink::onChannelQueue(RawChannel *channel, + struct mbuf_raw_video_frame *frame) +{ + int ret; + + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + if (frame == nullptr) { + PDRAW_LOG_ERRNO("frame", EINVAL); + return; + } + if (mState != STARTED) { + PDRAW_LOGE("video sink is not started"); + return; + } + if (mInputChannelFlushPending) { + PDRAW_LOGI("frame input: flush pending, discard frame"); + return; + } + RawSink::lock(); + + ret = prepareRawVideoFrame(channel, frame); + if (ret < 0) { + RawSink::unlock(); + return; + } + + RawSink::onChannelQueue(channel, frame); + mIsFlushed = false; + RawSink::unlock(); +} + + +void ExternalRawVideoSink::onChannelFlush(RawChannel *channel) +{ + int ret; + + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + PDRAW_LOGD("flushing input channel"); + mInputChannelFlushPending = true; + + ret = flush(); + if (ret < 0) + PDRAW_LOG_ERRNO("flush", -ret); +} + + +void ExternalRawVideoSink::onChannelTeardown(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + PDRAW_LOGD("tearing down input channel"); + + int ret = channelTeardown(channel); + if (ret < 0) + PDRAW_LOG_ERRNO("channelTeardown", -ret); +} + + +int ExternalRawVideoSink::channelTeardown(RawChannel *channel) +{ + int ret; + + if (channel == nullptr) + return -EINVAL; + + RawSink::lock(); + + if (mInputMedia == nullptr) { + /* The channel is already torn down, nothing more to do */ + RawSink::unlock(); + return 0; + } + + if (mTearingDown) { + /* The teardown may already be in progress but mInputMedia + * is not yet set to nullptr. + * Eg. removeInputMedia() utimately calls the app's + * mediaRemoved() callback, which can call the VideoSink + * stop() function, which calls channelTeardown() again. */ + RawSink::unlock(); + return 0; + } + mTearingDown = true; + + /* Remove the input port */ + channel->setQueue(nullptr); + + ret = removeInputMedia(mInputMedia); + if (ret < 0) + PDRAW_LOG_ERRNO("removeInputMedia", -ret); + else + mInputMedia = nullptr; + + mTearingDown = false; + RawSink::unlock(); + + ret = flush(); + if (ret < 0) + PDRAW_LOG_ERRNO("flush", -ret); + + return ret; +} + +/** + * Video sink listener calls from idle functions + */ +void ExternalRawVideoSink::callVideoSinkFlush(void *userdata) +{ + ExternalRawVideoSink *self = + reinterpret_cast(userdata); + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + IPdraw::IRawVideoSink::Listener *listener = + self->getVideoSinkListener(); + + if (listener == nullptr) { + self->flushDone(); + } else { + listener->onRawVideoSinkFlush(self->mSession, + self->getVideoSink()); + } +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_external_raw_video_sink.hpp b/libpdraw/src/pdraw_external_raw_video_sink.hpp index 0662e9b..c62950d 100644 --- a/libpdraw/src/pdraw_external_raw_video_sink.hpp +++ b/libpdraw/src/pdraw_external_raw_video_sink.hpp @@ -1,105 +1,105 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Application external raw video sink - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_EXTERNAL_RAW_VIDEO_SINK_HPP_ -#define _PDRAW_EXTERNAL_RAW_VIDEO_SINK_HPP_ - -#include "pdraw_element.hpp" - -#include - -#include -#include - -namespace Pdraw { - - -class ExternalRawVideoSink : public RawSinkElement { -public: - ExternalRawVideoSink(Session *session, - Element::Listener *elementListener, - IPdraw::IRawVideoSink::Listener *listener, - IPdraw::IRawVideoSink *sink, - const struct pdraw_video_sink_params *params); - - ~ExternalRawVideoSink(void); - - int start(void); - - int stop(void); - - int flushDone(void); - - struct mbuf_raw_video_frame_queue *getQueue(void) - { - return mInputFrameQueue; - } - - IPdraw::IRawVideoSink *getVideoSink(void) - { - return mVideoSink; - } - - IPdraw::IRawVideoSink::Listener *getVideoSinkListener(void) - { - return mVideoSinkListener; - } - -private: - int flush(void); - - int channelTeardown(RawChannel *channel); - - void onChannelQueue(RawChannel *channel, - struct mbuf_raw_video_frame *frame); - - void onChannelFlush(RawChannel *channel); - - void onChannelTeardown(RawChannel *channel); - - int prepareRawVideoFrame(RawChannel *channel, - struct mbuf_raw_video_frame *frame); - - /* Video sink listener calls from idle functions */ - static void callVideoSinkFlush(void *userdata); - - IPdraw::IRawVideoSink *mVideoSink; - IPdraw::IRawVideoSink::Listener *mVideoSinkListener; - struct pdraw_video_sink_params mParams; - RawVideoMedia *mInputMedia; - struct mbuf_raw_video_frame_queue *mInputFrameQueue; - bool mIsFlushed; - bool mInputChannelFlushPending; - bool mTearingDown; -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_EXTERNAL_RAW_VIDEO_SINK_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Application external raw video sink + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_EXTERNAL_RAW_VIDEO_SINK_HPP_ +#define _PDRAW_EXTERNAL_RAW_VIDEO_SINK_HPP_ + +#include "pdraw_element.hpp" + +#include + +#include +#include + +namespace Pdraw { + + +class ExternalRawVideoSink : public RawSinkElement { +public: + ExternalRawVideoSink(Session *session, + Element::Listener *elementListener, + IPdraw::IRawVideoSink::Listener *listener, + IPdraw::IRawVideoSink *sink, + const struct pdraw_video_sink_params *params); + + ~ExternalRawVideoSink(void); + + int start(void); + + int stop(void); + + int flushDone(void); + + struct mbuf_raw_video_frame_queue *getQueue(void) + { + return mInputFrameQueue; + } + + IPdraw::IRawVideoSink *getVideoSink(void) + { + return mVideoSink; + } + + IPdraw::IRawVideoSink::Listener *getVideoSinkListener(void) + { + return mVideoSinkListener; + } + +private: + int flush(void); + + int channelTeardown(RawChannel *channel); + + void onChannelQueue(RawChannel *channel, + struct mbuf_raw_video_frame *frame); + + void onChannelFlush(RawChannel *channel); + + void onChannelTeardown(RawChannel *channel); + + int prepareRawVideoFrame(RawChannel *channel, + struct mbuf_raw_video_frame *frame); + + /* Video sink listener calls from idle functions */ + static void callVideoSinkFlush(void *userdata); + + IPdraw::IRawVideoSink *mVideoSink; + IPdraw::IRawVideoSink::Listener *mVideoSinkListener; + struct pdraw_video_sink_params mParams; + RawVideoMedia *mInputMedia; + struct mbuf_raw_video_frame_queue *mInputFrameQueue; + bool mIsFlushed; + bool mInputChannelFlushPending; + bool mTearingDown; +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_EXTERNAL_RAW_VIDEO_SINK_HPP_ */ diff --git a/libpdraw/src/pdraw_gles2_common.hpp b/libpdraw/src/pdraw_gles2_common.hpp index 3204133..6c24cab 100644 --- a/libpdraw/src/pdraw_gles2_common.hpp +++ b/libpdraw/src/pdraw_gles2_common.hpp @@ -1,79 +1,79 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * OpenGL ES 2.0 common header - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_GLES2_COMMON_HPP_ -#define _PDRAW_GLES2_COMMON_HPP_ - -#ifdef USE_GLES2 - -# if defined(__APPLE__) -# include -# if TARGET_OS_IPHONE -# include -# include -# elif defined(USE_GLFW3) -# include -# include -# include -# endif -# elif defined(_WIN32) -# include -# elif defined(USE_GLFW3) -# define GLFW_INCLUDE_ES2 -# include -# else -# include -# endif - -/* Uncomment to enable extra GL error checking */ -//# define CHECK_GL_ERRORS -# if defined(CHECK_GL_ERRORS) -# warning CHECK_GL_ERRORS is enabled -# include -# define GLCHK(X) \ - do { \ - GLenum err = GL_NO_ERROR; \ - X; \ - while ((err = glGetError())) { \ - ULOGE("GL error 0x%x in " #X \ - " file %s line %d", \ - err, \ - __FILE__, \ - __LINE__); \ - assert(err == GL_NO_ERROR); \ - } \ - } while (0) -# else -# define GLCHK(X) X -# endif /* CHECK_GL_ERRORS */ - -#endif /* USE_GLES2 */ - -#endif /* !_PDRAW_GLES2_COMMON_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * OpenGL ES 2.0 common header + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_GLES2_COMMON_HPP_ +#define _PDRAW_GLES2_COMMON_HPP_ + +#ifdef USE_GLES2 + +# if defined(__APPLE__) +# include +# if TARGET_OS_IPHONE +# include +# include +# elif defined(USE_GLFW3) +# include +# include +# include +# endif +# elif defined(_WIN32) +# include +# elif defined(USE_GLFW3) +# define GLFW_INCLUDE_ES2 +# include +# else +# include +# endif + +/* Uncomment to enable extra GL error checking */ +//# define CHECK_GL_ERRORS +# if defined(CHECK_GL_ERRORS) +# warning CHECK_GL_ERRORS is enabled +# include +# define GLCHK(X) \ + do { \ + GLenum err = GL_NO_ERROR; \ + X; \ + while ((err = glGetError())) { \ + ULOGE("GL error 0x%x in " #X \ + " file %s line %d", \ + err, \ + __FILE__, \ + __LINE__); \ + assert(err == GL_NO_ERROR); \ + } \ + } while (0) +# else +# define GLCHK(X) X +# endif /* CHECK_GL_ERRORS */ + +#endif /* USE_GLES2 */ + +#endif /* !_PDRAW_GLES2_COMMON_HPP_ */ diff --git a/libpdraw/src/pdraw_gles2_hmd.cpp b/libpdraw/src/pdraw_gles2_hmd.cpp index 7123451..4fffaff 100644 --- a/libpdraw/src/pdraw_gles2_hmd.cpp +++ b/libpdraw/src/pdraw_gles2_hmd.cpp @@ -1,555 +1,555 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * OpenGL ES 2.0 HMD distortion correction - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * Extracted from FPVToolbox by FrĂ©dĂ©ric Bertolus - * https://github.com/niavok/fpvtoolbox - * and translated from Java to C++ - */ - -#define ULOG_TAG pdraw_gles2hmd -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_gles2_hmd.hpp" - -#ifdef USE_GLES2 - -namespace Pdraw { - - -# define GLES2_HMD_INCH_TO_MILLIMETER (25.4f) -# define GLES2_HMD_OFFSET_COCKPITGLASSES (34.66f) -# define GLES2_HMD_OFFSET_COCKPITGLASSES_2 (41.0f) -# define GLES2_HMD_IPD_COCKPITGLASSES (63.0f) -# define GLES2_HMD_IPD_COCKPITGLASSES_2 (67.0f) - - -extern const float pdraw_gles2HmdColors[14884]; -extern const uint32_t pdraw_gles2HmdIndices[21600]; -extern const float pdraw_gles2HmdTexCoords[7442]; -extern const float pdraw_gles2HmdTexCoordsCockpitglassesRed[7442]; -extern const float pdraw_gles2HmdTexCoordsCockpitglassesBlue[7442]; -extern const float pdraw_gles2HmdPositionsCockpitglasses[7442]; -extern const float pdraw_gles2HmdPositionsCockpitglasses2[7442]; - -extern const GLchar *pdraw_gles2HmdVertexShader; -extern const GLchar *pdraw_gles2HmdFragmentShader; - - -Gles2HmdEye::Gles2HmdEye(unsigned int firstTexUnit, - enum pdraw_hmd_model hmdModel, - float scale, - float panH, - float panV, - float metricsWidth, - float metricsHeight, - float eyeOffsetX, - float eyeOffsetY) -{ - mRotation = 0; - mHmdModel = hmdModel; - mScale = scale; - mPanH = panH; - mPanV = panV; - mMetricsWidth = metricsWidth; - mMetricsHeight = metricsHeight; - mEyeOffsetX = eyeOffsetX; - mEyeOffsetY = eyeOffsetY; - mFirstTexUnit = firstTexUnit; - mProgram = 0; - mIndicesBufferHandle = 0; - mPositionBufferHandle = 0; - mColorBufferHandle = 0; - mTexCoord0BufferHandle = 0; - mTexCoord1BufferHandle = 0; - mTexCoord2BufferHandle = 0; - mProgramTexture = 0; - mProgramEyeToSourceUVScale = 0; - mProgramEyeToSourceUVOffset = 0; - mProgramEyeToSourceScale = 0; - mProgramEyeToSourceOffset = 0; - mProgramChromaticAberrationCorrection = 0; - mProgramRotation = 0; - mProgramLensLimits = 0; - mProgramPosition = 0; - mProgramColor = 0; - mProgramTexCoord0 = 0; - mProgramTexCoord1 = 0; - mProgramTexCoord2 = 0; - - GLint vertexShader = 0, fragmentShader = 0; - GLint success = 0; - - GLCHK(); - - GLuint buffer[6]; - memset(buffer, 0, sizeof(buffer)); - GLCHK(glGenBuffers(6, buffer)); - - vertexShader = glCreateShader(GL_VERTEX_SHADER); - if ((vertexShader == 0) || (vertexShader == GL_INVALID_ENUM)) { - ULOGE("failed to create vertex shader"); - goto err; - } - - glShaderSource(vertexShader, 1, &pdraw_gles2HmdVertexShader, nullptr); - glCompileShader(vertexShader); - glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); - if (!success) { - GLchar infoLog[512]; - glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog); - ULOGE("vertex shader compilation failed '%s'", infoLog); - goto err; - } - - fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - if ((fragmentShader == 0) || (fragmentShader == GL_INVALID_ENUM)) { - ULOGE("failed to create fragment shader"); - goto err; - } - - glShaderSource( - fragmentShader, 1, &pdraw_gles2HmdFragmentShader, nullptr); - glCompileShader(fragmentShader); - glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); - if (!success) { - GLchar infoLog[512]; - glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog); - ULOGE("fragment shader compilation failed '%s'", infoLog); - goto err; - } - - /* Link shaders */ - mProgram = glCreateProgram(); - glAttachShader(mProgram, vertexShader); - glAttachShader(mProgram, fragmentShader); - glLinkProgram(mProgram); - glGetProgramiv(mProgram, GL_LINK_STATUS, &success); - if (!success) { - GLchar infoLog[512]; - glGetProgramInfoLog(mProgram, 512, nullptr, infoLog); - ULOGE("program link failed '%s'", infoLog); - goto err; - } - - glDeleteShader(vertexShader); - vertexShader = 0; - glDeleteShader(fragmentShader); - fragmentShader = 0; - - GLCHK(); - - switch (mHmdModel) { - default: - case PDRAW_HMD_MODEL_COCKPITGLASSES: - mIndicesBufferHandle = buffer[0]; - GLCHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, - mIndicesBufferHandle)); - GLCHK(glBufferData(GL_ELEMENT_ARRAY_BUFFER, - sizeof(pdraw_gles2HmdIndices), - pdraw_gles2HmdIndices, - GL_STATIC_DRAW)); - - mPositionBufferHandle = buffer[1]; - GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mPositionBufferHandle)); - GLCHK(glBufferData( - GL_ARRAY_BUFFER, - sizeof(pdraw_gles2HmdPositionsCockpitglasses), - pdraw_gles2HmdPositionsCockpitglasses, - GL_STATIC_DRAW)); - - mColorBufferHandle = buffer[2]; - GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mColorBufferHandle)); - GLCHK(glBufferData(GL_ARRAY_BUFFER, - sizeof(pdraw_gles2HmdColors), - pdraw_gles2HmdColors, - GL_STATIC_DRAW)); - - mTexCoord0BufferHandle = buffer[3]; - GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mTexCoord0BufferHandle)); - GLCHK(glBufferData( - GL_ARRAY_BUFFER, - sizeof(pdraw_gles2HmdTexCoordsCockpitglassesRed), - pdraw_gles2HmdTexCoordsCockpitglassesRed, - GL_STATIC_DRAW)); - - mTexCoord1BufferHandle = buffer[4]; - GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mTexCoord1BufferHandle)); - GLCHK(glBufferData(GL_ARRAY_BUFFER, - sizeof(pdraw_gles2HmdTexCoords), - pdraw_gles2HmdTexCoords, - GL_STATIC_DRAW)); - - mTexCoord2BufferHandle = buffer[5]; - GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mTexCoord2BufferHandle)); - GLCHK(glBufferData( - GL_ARRAY_BUFFER, - sizeof(pdraw_gles2HmdTexCoordsCockpitglassesBlue), - pdraw_gles2HmdTexCoordsCockpitglassesBlue, - GL_STATIC_DRAW)); - break; - case PDRAW_HMD_MODEL_COCKPITGLASSES_2: - mIndicesBufferHandle = buffer[0]; - GLCHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, - mIndicesBufferHandle)); - GLCHK(glBufferData(GL_ELEMENT_ARRAY_BUFFER, - sizeof(pdraw_gles2HmdIndices), - pdraw_gles2HmdIndices, - GL_STATIC_DRAW)); - - mPositionBufferHandle = buffer[1]; - GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mPositionBufferHandle)); - GLCHK(glBufferData( - GL_ARRAY_BUFFER, - sizeof(pdraw_gles2HmdPositionsCockpitglasses2), - pdraw_gles2HmdPositionsCockpitglasses2, - GL_STATIC_DRAW)); - - mColorBufferHandle = buffer[2]; - GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mColorBufferHandle)); - GLCHK(glBufferData(GL_ARRAY_BUFFER, - sizeof(pdraw_gles2HmdColors), - pdraw_gles2HmdColors, - GL_STATIC_DRAW)); - - mTexCoord0BufferHandle = buffer[3]; - GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mTexCoord0BufferHandle)); - GLCHK(glBufferData(GL_ARRAY_BUFFER, - sizeof(pdraw_gles2HmdTexCoords), - pdraw_gles2HmdTexCoords, - GL_STATIC_DRAW)); - - mTexCoord1BufferHandle = buffer[4]; - GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mTexCoord1BufferHandle)); - GLCHK(glBufferData(GL_ARRAY_BUFFER, - sizeof(pdraw_gles2HmdTexCoords), - pdraw_gles2HmdTexCoords, - GL_STATIC_DRAW)); - - mTexCoord2BufferHandle = buffer[5]; - GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mTexCoord2BufferHandle)); - GLCHK(glBufferData(GL_ARRAY_BUFFER, - sizeof(pdraw_gles2HmdTexCoords), - pdraw_gles2HmdTexCoords, - GL_STATIC_DRAW)); - break; - } - - mProgramTexture = glGetUniformLocation(mProgram, "Texture0"); - if (mProgramTexture < 0) - ULOGE("failed to get uniform location 'Texture0'"); - - mProgramEyeToSourceUVScale = - glGetUniformLocation(mProgram, "EyeToSourceUVScale"); - if (mProgramEyeToSourceUVScale < 0) - ULOGE("failed to get uniform location 'EyeToSourceUVScale'"); - - mProgramEyeToSourceUVOffset = - glGetUniformLocation(mProgram, "EyeToSourceUVOffset"); - if (mProgramEyeToSourceUVOffset < 0) - ULOGE("failed to get uniform location 'EyeToSourceUVOffset'"); - - mProgramEyeToSourceScale = - glGetUniformLocation(mProgram, "EyeToSourceScale"); - if (mProgramEyeToSourceScale < 0) - ULOGE("failed to get uniform location 'EyeToSourceScale'"); - - mProgramEyeToSourceOffset = - glGetUniformLocation(mProgram, "EyeToSourceOffset"); - if (mProgramEyeToSourceOffset < 0) - ULOGE("failed to get uniform location 'EyeToSourceOffset'"); - - mProgramChromaticAberrationCorrection = - glGetUniformLocation(mProgram, "ChromaticAberrationCorrection"); - if (mProgramChromaticAberrationCorrection < 0) { - ULOGE("failed to get uniform location " - "'ChromaticAberrationCorrection'"); - } - - mProgramRotation = glGetUniformLocation(mProgram, "Rotation"); - if (mProgramRotation < 0) - ULOGE("failed to get uniform location 'Rotation'"); - - mProgramLensLimits = glGetUniformLocation(mProgram, "LensLimits"); - if (mProgramLensLimits < 0) - ULOGE("failed to get uniform location 'LensLimits'"); - - mProgramPosition = glGetAttribLocation(mProgram, "Position"); - if (mProgramPosition < 0) - ULOGE("failed to get attribute location 'Position'"); - - mProgramColor = glGetAttribLocation(mProgram, "Color"); - if (mProgramColor < 0) - ULOGE("failed to get attribute location 'Color'"); - - mProgramTexCoord0 = glGetAttribLocation(mProgram, "TexCoord0"); - if (mProgramTexCoord0 < 0) - ULOGE("failed to get attribute location 'TexCoord0'"); - - mProgramTexCoord1 = glGetAttribLocation(mProgram, "TexCoord1"); - if (mProgramTexCoord1 < 0) - ULOGE("failed to get attribute location 'TexCoord1'"); - - mProgramTexCoord2 = glGetAttribLocation(mProgram, "TexCoord2"); - if (mProgramTexCoord2 < 0) - ULOGE("failed to get attribute location 'TexCoord2'"); - - GLCHK(); - - return; - -err: - if ((buffer[0]) || (buffer[1]) || (buffer[2]) || (buffer[3]) || - (buffer[4]) || (buffer[5])) - GLCHK(glDeleteBuffers(6, buffer)); - if (vertexShader > 0) - GLCHK(glDeleteShader(vertexShader)); - if (fragmentShader > 0) - GLCHK(glDeleteShader(fragmentShader)); - if (mProgram > 0) - GLCHK(glDeleteProgram(mProgram)); - mProgram = 0; - mIndicesBufferHandle = 0; - mPositionBufferHandle = 0; - mColorBufferHandle = 0; - mTexCoord0BufferHandle = 0; - mTexCoord1BufferHandle = 0; - mTexCoord2BufferHandle = 0; -} - - -Gles2HmdEye::~Gles2HmdEye(void) -{ - int count = 0; - GLuint buffer[6]; - if (mIndicesBufferHandle > 0) - buffer[count++] = mIndicesBufferHandle; - if (mPositionBufferHandle > 0) - buffer[count++] = mPositionBufferHandle; - if (mColorBufferHandle > 0) - buffer[count++] = mColorBufferHandle; - if (mTexCoord0BufferHandle > 0) - buffer[count++] = mTexCoord0BufferHandle; - if (mTexCoord1BufferHandle > 0) - buffer[count++] = mTexCoord1BufferHandle; - if (mTexCoord2BufferHandle > 0) - buffer[count++] = mTexCoord2BufferHandle; - if (count > 0) - GLCHK(glDeleteBuffers(count, buffer)); - if (mProgram > 0) - GLCHK(glDeleteProgram(mProgram)); -} - - -int Gles2HmdEye::renderEye(GLuint texture, - unsigned int textureWidth, - unsigned int textureHeight) -{ - GLCHK(glUseProgram(mProgram)); - - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit)); - /* GLCHK(glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture)); */ - GLCHK(glBindTexture(GL_TEXTURE_2D, texture)); - GLCHK(glUniform1i(mProgramTexture, mFirstTexUnit)); - - GLCHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndicesBufferHandle)); - - GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mPositionBufferHandle)); - GLCHK(glEnableVertexAttribArray(mProgramPosition)); - GLCHK(glVertexAttribPointer( - mProgramPosition, 2, GL_FLOAT, false, 0, 0)); - - GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mColorBufferHandle)); - GLCHK(glEnableVertexAttribArray(mProgramColor)); - GLCHK(glVertexAttribPointer(mProgramColor, 4, GL_FLOAT, false, 0, 0)); - - GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mTexCoord0BufferHandle)); - GLCHK(glEnableVertexAttribArray(mProgramTexCoord0)); - GLCHK(glVertexAttribPointer( - mProgramTexCoord0, 2, GL_FLOAT, false, 0, 0)); - - GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mTexCoord1BufferHandle)); - GLCHK(glEnableVertexAttribArray(mProgramTexCoord1)); - GLCHK(glVertexAttribPointer( - mProgramTexCoord1, 2, GL_FLOAT, false, 0, 0)); - - GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mTexCoord2BufferHandle)); - GLCHK(glEnableVertexAttribArray(mProgramTexCoord2)); - GLCHK(glVertexAttribPointer( - mProgramTexCoord2, 2, GL_FLOAT, false, 0, 0)); - - float ratio; - if ((mRotation == 90) || (mRotation == 270)) - ratio = (float)textureHeight / (float)textureWidth; - else - ratio = (float)textureWidth / (float)textureHeight; - - if (ratio > 1.) { - GLCHK(glUniform2f( - mProgramEyeToSourceUVScale, mScale, mScale * ratio)); - } else { - GLCHK(glUniform2f( - mProgramEyeToSourceUVScale, mScale / ratio, mScale)); - } - - GLCHK(glUniform2f(mProgramEyeToSourceUVOffset, mPanH, mPanV)); - GLCHK(glUniform1i(mProgramChromaticAberrationCorrection, 0)); - GLCHK(glUniform1i(mProgramRotation, mRotation)); - GLCHK(glUniform1i(mProgramLensLimits, 0)); - GLCHK(glUniform2f(mProgramEyeToSourceScale, - 2.f / mMetricsWidth, - -2.f / mMetricsHeight)); - GLCHK(glUniform2f(mProgramEyeToSourceOffset, - 2.f * mEyeOffsetX / mMetricsWidth, - 2.f * mEyeOffsetY / mMetricsHeight - 1.f)); - - GLCHK(glDrawElements(GL_TRIANGLES, - sizeof(pdraw_gles2HmdIndices) / sizeof(float), - GL_UNSIGNED_INT, - 0)); - - GLCHK(glDisableVertexAttribArray(mProgramPosition)); - GLCHK(glDisableVertexAttribArray(mProgramColor)); - GLCHK(glDisableVertexAttribArray(mProgramTexCoord0)); - GLCHK(glDisableVertexAttribArray(mProgramTexCoord1)); - GLCHK(glDisableVertexAttribArray(mProgramTexCoord2)); - - GLCHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); - GLCHK(glBindBuffer(GL_ARRAY_BUFFER, 0)); - - return 0; -} - - -Gles2Hmd::Gles2Hmd(Session *session, - unsigned int firstTexUnit, - unsigned int width, - unsigned int height, - float ipdOffset, - float xOffset, - float yOffset) -{ - Settings *settings; - - mSession = session; - mHmdModel = PDRAW_HMD_MODEL_UNKNOWN; - mIpdOffset = ipdOffset; - mXOffset = xOffset; - mYOffset = yOffset; - mXdpi = SETTINGS_DISPLAY_XDPI; - mYdpi = SETTINGS_DISPLAY_YDPI; - mDeviceMarginTop = 0.f; - mDeviceMarginBottom = 0.f; - mDeviceMarginLeft = 0.f; - mDeviceMarginRight = 0.f; - - settings = mSession->getSettings(); - settings->lock(); - settings->getDisplayScreenSettings(&mXdpi, - &mYdpi, - &mDeviceMarginTop, - &mDeviceMarginBottom, - &mDeviceMarginLeft, - &mDeviceMarginRight); - mHmdModel = settings->getHmdModelSetting(); - settings->unlock(); - - switch (mHmdModel) { - default: - case PDRAW_HMD_MODEL_COCKPITGLASSES: - mHmdOffset = GLES2_HMD_OFFSET_COCKPITGLASSES; - mHmdIpd = GLES2_HMD_IPD_COCKPITGLASSES; - break; - case PDRAW_HMD_MODEL_COCKPITGLASSES_2: - mHmdOffset = GLES2_HMD_OFFSET_COCKPITGLASSES_2; - mHmdIpd = GLES2_HMD_IPD_COCKPITGLASSES_2; - break; - } - - mMetricsWidth = (float)width / mXdpi * GLES2_HMD_INCH_TO_MILLIMETER; - mMetricsHeight = (float)height / mYdpi * GLES2_HMD_INCH_TO_MILLIMETER; - - mLeftEye = new Gles2HmdEye(firstTexUnit, - mHmdModel, - 1.f, - 0.f, - 0.f, - mMetricsWidth, - mMetricsHeight, - -(mHmdIpd + mIpdOffset) / 2.f + mXOffset, - mHmdOffset - mDeviceMarginBottom - mYOffset); - mRightEye = - new Gles2HmdEye(firstTexUnit, - mHmdModel, - 1.f, - 0.f, - 0.f, - mMetricsWidth, - mMetricsHeight, - (mHmdIpd + mIpdOffset) / 2.f + mXOffset, - mHmdOffset - mDeviceMarginBottom - mYOffset); -} - - -Gles2Hmd::~Gles2Hmd(void) -{ - if (mLeftEye) - delete (mLeftEye); - if (mRightEye) - delete (mRightEye); -} - - -int Gles2Hmd::renderHmd(GLuint texture, - unsigned int textureWidth, - unsigned int textureHeight) -{ - int ret = 0; - - if (mLeftEye != nullptr) { - ret = mLeftEye->renderEye(texture, textureWidth, textureHeight); - if (ret != 0) - return ret; - } - - if (mRightEye != nullptr) { - ret = mRightEye->renderEye( - texture, textureWidth, textureHeight); - if (ret != 0) - return ret; - } - - return 0; -} - -} /* namespace Pdraw */ - -#endif /* USE_GLES2 */ +/** + * Parrot Drones Awesome Video Viewer Library + * OpenGL ES 2.0 HMD distortion correction + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Extracted from FPVToolbox by FrĂ©dĂ©ric Bertolus + * https://github.com/niavok/fpvtoolbox + * and translated from Java to C++ + */ + +#define ULOG_TAG pdraw_gles2hmd +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_gles2_hmd.hpp" + +#ifdef USE_GLES2 + +namespace Pdraw { + + +# define GLES2_HMD_INCH_TO_MILLIMETER (25.4f) +# define GLES2_HMD_OFFSET_COCKPITGLASSES (34.66f) +# define GLES2_HMD_OFFSET_COCKPITGLASSES_2 (41.0f) +# define GLES2_HMD_IPD_COCKPITGLASSES (63.0f) +# define GLES2_HMD_IPD_COCKPITGLASSES_2 (67.0f) + + +extern const float pdraw_gles2HmdColors[14884]; +extern const uint32_t pdraw_gles2HmdIndices[21600]; +extern const float pdraw_gles2HmdTexCoords[7442]; +extern const float pdraw_gles2HmdTexCoordsCockpitglassesRed[7442]; +extern const float pdraw_gles2HmdTexCoordsCockpitglassesBlue[7442]; +extern const float pdraw_gles2HmdPositionsCockpitglasses[7442]; +extern const float pdraw_gles2HmdPositionsCockpitglasses2[7442]; + +extern const GLchar *pdraw_gles2HmdVertexShader; +extern const GLchar *pdraw_gles2HmdFragmentShader; + + +Gles2HmdEye::Gles2HmdEye(unsigned int firstTexUnit, + enum pdraw_hmd_model hmdModel, + float scale, + float panH, + float panV, + float metricsWidth, + float metricsHeight, + float eyeOffsetX, + float eyeOffsetY) +{ + mRotation = 0; + mHmdModel = hmdModel; + mScale = scale; + mPanH = panH; + mPanV = panV; + mMetricsWidth = metricsWidth; + mMetricsHeight = metricsHeight; + mEyeOffsetX = eyeOffsetX; + mEyeOffsetY = eyeOffsetY; + mFirstTexUnit = firstTexUnit; + mProgram = 0; + mIndicesBufferHandle = 0; + mPositionBufferHandle = 0; + mColorBufferHandle = 0; + mTexCoord0BufferHandle = 0; + mTexCoord1BufferHandle = 0; + mTexCoord2BufferHandle = 0; + mProgramTexture = 0; + mProgramEyeToSourceUVScale = 0; + mProgramEyeToSourceUVOffset = 0; + mProgramEyeToSourceScale = 0; + mProgramEyeToSourceOffset = 0; + mProgramChromaticAberrationCorrection = 0; + mProgramRotation = 0; + mProgramLensLimits = 0; + mProgramPosition = 0; + mProgramColor = 0; + mProgramTexCoord0 = 0; + mProgramTexCoord1 = 0; + mProgramTexCoord2 = 0; + + GLint vertexShader = 0, fragmentShader = 0; + GLint success = 0; + + GLCHK(); + + GLuint buffer[6]; + memset(buffer, 0, sizeof(buffer)); + GLCHK(glGenBuffers(6, buffer)); + + vertexShader = glCreateShader(GL_VERTEX_SHADER); + if ((vertexShader == 0) || (vertexShader == GL_INVALID_ENUM)) { + ULOGE("failed to create vertex shader"); + goto err; + } + + glShaderSource(vertexShader, 1, &pdraw_gles2HmdVertexShader, nullptr); + glCompileShader(vertexShader); + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar infoLog[512]; + glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog); + ULOGE("vertex shader compilation failed '%s'", infoLog); + goto err; + } + + fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + if ((fragmentShader == 0) || (fragmentShader == GL_INVALID_ENUM)) { + ULOGE("failed to create fragment shader"); + goto err; + } + + glShaderSource( + fragmentShader, 1, &pdraw_gles2HmdFragmentShader, nullptr); + glCompileShader(fragmentShader); + glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar infoLog[512]; + glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog); + ULOGE("fragment shader compilation failed '%s'", infoLog); + goto err; + } + + /* Link shaders */ + mProgram = glCreateProgram(); + glAttachShader(mProgram, vertexShader); + glAttachShader(mProgram, fragmentShader); + glLinkProgram(mProgram); + glGetProgramiv(mProgram, GL_LINK_STATUS, &success); + if (!success) { + GLchar infoLog[512]; + glGetProgramInfoLog(mProgram, 512, nullptr, infoLog); + ULOGE("program link failed '%s'", infoLog); + goto err; + } + + glDeleteShader(vertexShader); + vertexShader = 0; + glDeleteShader(fragmentShader); + fragmentShader = 0; + + GLCHK(); + + switch (mHmdModel) { + default: + case PDRAW_HMD_MODEL_COCKPITGLASSES: + mIndicesBufferHandle = buffer[0]; + GLCHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, + mIndicesBufferHandle)); + GLCHK(glBufferData(GL_ELEMENT_ARRAY_BUFFER, + sizeof(pdraw_gles2HmdIndices), + pdraw_gles2HmdIndices, + GL_STATIC_DRAW)); + + mPositionBufferHandle = buffer[1]; + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mPositionBufferHandle)); + GLCHK(glBufferData( + GL_ARRAY_BUFFER, + sizeof(pdraw_gles2HmdPositionsCockpitglasses), + pdraw_gles2HmdPositionsCockpitglasses, + GL_STATIC_DRAW)); + + mColorBufferHandle = buffer[2]; + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mColorBufferHandle)); + GLCHK(glBufferData(GL_ARRAY_BUFFER, + sizeof(pdraw_gles2HmdColors), + pdraw_gles2HmdColors, + GL_STATIC_DRAW)); + + mTexCoord0BufferHandle = buffer[3]; + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mTexCoord0BufferHandle)); + GLCHK(glBufferData( + GL_ARRAY_BUFFER, + sizeof(pdraw_gles2HmdTexCoordsCockpitglassesRed), + pdraw_gles2HmdTexCoordsCockpitglassesRed, + GL_STATIC_DRAW)); + + mTexCoord1BufferHandle = buffer[4]; + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mTexCoord1BufferHandle)); + GLCHK(glBufferData(GL_ARRAY_BUFFER, + sizeof(pdraw_gles2HmdTexCoords), + pdraw_gles2HmdTexCoords, + GL_STATIC_DRAW)); + + mTexCoord2BufferHandle = buffer[5]; + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mTexCoord2BufferHandle)); + GLCHK(glBufferData( + GL_ARRAY_BUFFER, + sizeof(pdraw_gles2HmdTexCoordsCockpitglassesBlue), + pdraw_gles2HmdTexCoordsCockpitglassesBlue, + GL_STATIC_DRAW)); + break; + case PDRAW_HMD_MODEL_COCKPITGLASSES_2: + mIndicesBufferHandle = buffer[0]; + GLCHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, + mIndicesBufferHandle)); + GLCHK(glBufferData(GL_ELEMENT_ARRAY_BUFFER, + sizeof(pdraw_gles2HmdIndices), + pdraw_gles2HmdIndices, + GL_STATIC_DRAW)); + + mPositionBufferHandle = buffer[1]; + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mPositionBufferHandle)); + GLCHK(glBufferData( + GL_ARRAY_BUFFER, + sizeof(pdraw_gles2HmdPositionsCockpitglasses2), + pdraw_gles2HmdPositionsCockpitglasses2, + GL_STATIC_DRAW)); + + mColorBufferHandle = buffer[2]; + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mColorBufferHandle)); + GLCHK(glBufferData(GL_ARRAY_BUFFER, + sizeof(pdraw_gles2HmdColors), + pdraw_gles2HmdColors, + GL_STATIC_DRAW)); + + mTexCoord0BufferHandle = buffer[3]; + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mTexCoord0BufferHandle)); + GLCHK(glBufferData(GL_ARRAY_BUFFER, + sizeof(pdraw_gles2HmdTexCoords), + pdraw_gles2HmdTexCoords, + GL_STATIC_DRAW)); + + mTexCoord1BufferHandle = buffer[4]; + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mTexCoord1BufferHandle)); + GLCHK(glBufferData(GL_ARRAY_BUFFER, + sizeof(pdraw_gles2HmdTexCoords), + pdraw_gles2HmdTexCoords, + GL_STATIC_DRAW)); + + mTexCoord2BufferHandle = buffer[5]; + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mTexCoord2BufferHandle)); + GLCHK(glBufferData(GL_ARRAY_BUFFER, + sizeof(pdraw_gles2HmdTexCoords), + pdraw_gles2HmdTexCoords, + GL_STATIC_DRAW)); + break; + } + + mProgramTexture = glGetUniformLocation(mProgram, "Texture0"); + if (mProgramTexture < 0) + ULOGE("failed to get uniform location 'Texture0'"); + + mProgramEyeToSourceUVScale = + glGetUniformLocation(mProgram, "EyeToSourceUVScale"); + if (mProgramEyeToSourceUVScale < 0) + ULOGE("failed to get uniform location 'EyeToSourceUVScale'"); + + mProgramEyeToSourceUVOffset = + glGetUniformLocation(mProgram, "EyeToSourceUVOffset"); + if (mProgramEyeToSourceUVOffset < 0) + ULOGE("failed to get uniform location 'EyeToSourceUVOffset'"); + + mProgramEyeToSourceScale = + glGetUniformLocation(mProgram, "EyeToSourceScale"); + if (mProgramEyeToSourceScale < 0) + ULOGE("failed to get uniform location 'EyeToSourceScale'"); + + mProgramEyeToSourceOffset = + glGetUniformLocation(mProgram, "EyeToSourceOffset"); + if (mProgramEyeToSourceOffset < 0) + ULOGE("failed to get uniform location 'EyeToSourceOffset'"); + + mProgramChromaticAberrationCorrection = + glGetUniformLocation(mProgram, "ChromaticAberrationCorrection"); + if (mProgramChromaticAberrationCorrection < 0) { + ULOGE("failed to get uniform location " + "'ChromaticAberrationCorrection'"); + } + + mProgramRotation = glGetUniformLocation(mProgram, "Rotation"); + if (mProgramRotation < 0) + ULOGE("failed to get uniform location 'Rotation'"); + + mProgramLensLimits = glGetUniformLocation(mProgram, "LensLimits"); + if (mProgramLensLimits < 0) + ULOGE("failed to get uniform location 'LensLimits'"); + + mProgramPosition = glGetAttribLocation(mProgram, "Position"); + if (mProgramPosition < 0) + ULOGE("failed to get attribute location 'Position'"); + + mProgramColor = glGetAttribLocation(mProgram, "Color"); + if (mProgramColor < 0) + ULOGE("failed to get attribute location 'Color'"); + + mProgramTexCoord0 = glGetAttribLocation(mProgram, "TexCoord0"); + if (mProgramTexCoord0 < 0) + ULOGE("failed to get attribute location 'TexCoord0'"); + + mProgramTexCoord1 = glGetAttribLocation(mProgram, "TexCoord1"); + if (mProgramTexCoord1 < 0) + ULOGE("failed to get attribute location 'TexCoord1'"); + + mProgramTexCoord2 = glGetAttribLocation(mProgram, "TexCoord2"); + if (mProgramTexCoord2 < 0) + ULOGE("failed to get attribute location 'TexCoord2'"); + + GLCHK(); + + return; + +err: + if ((buffer[0]) || (buffer[1]) || (buffer[2]) || (buffer[3]) || + (buffer[4]) || (buffer[5])) + GLCHK(glDeleteBuffers(6, buffer)); + if (vertexShader > 0) + GLCHK(glDeleteShader(vertexShader)); + if (fragmentShader > 0) + GLCHK(glDeleteShader(fragmentShader)); + if (mProgram > 0) + GLCHK(glDeleteProgram(mProgram)); + mProgram = 0; + mIndicesBufferHandle = 0; + mPositionBufferHandle = 0; + mColorBufferHandle = 0; + mTexCoord0BufferHandle = 0; + mTexCoord1BufferHandle = 0; + mTexCoord2BufferHandle = 0; +} + + +Gles2HmdEye::~Gles2HmdEye(void) +{ + int count = 0; + GLuint buffer[6]; + if (mIndicesBufferHandle > 0) + buffer[count++] = mIndicesBufferHandle; + if (mPositionBufferHandle > 0) + buffer[count++] = mPositionBufferHandle; + if (mColorBufferHandle > 0) + buffer[count++] = mColorBufferHandle; + if (mTexCoord0BufferHandle > 0) + buffer[count++] = mTexCoord0BufferHandle; + if (mTexCoord1BufferHandle > 0) + buffer[count++] = mTexCoord1BufferHandle; + if (mTexCoord2BufferHandle > 0) + buffer[count++] = mTexCoord2BufferHandle; + if (count > 0) + GLCHK(glDeleteBuffers(count, buffer)); + if (mProgram > 0) + GLCHK(glDeleteProgram(mProgram)); +} + + +int Gles2HmdEye::renderEye(GLuint texture, + unsigned int textureWidth, + unsigned int textureHeight) +{ + GLCHK(glUseProgram(mProgram)); + + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit)); + /* GLCHK(glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture)); */ + GLCHK(glBindTexture(GL_TEXTURE_2D, texture)); + GLCHK(glUniform1i(mProgramTexture, mFirstTexUnit)); + + GLCHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndicesBufferHandle)); + + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mPositionBufferHandle)); + GLCHK(glEnableVertexAttribArray(mProgramPosition)); + GLCHK(glVertexAttribPointer( + mProgramPosition, 2, GL_FLOAT, false, 0, 0)); + + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mColorBufferHandle)); + GLCHK(glEnableVertexAttribArray(mProgramColor)); + GLCHK(glVertexAttribPointer(mProgramColor, 4, GL_FLOAT, false, 0, 0)); + + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mTexCoord0BufferHandle)); + GLCHK(glEnableVertexAttribArray(mProgramTexCoord0)); + GLCHK(glVertexAttribPointer( + mProgramTexCoord0, 2, GL_FLOAT, false, 0, 0)); + + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mTexCoord1BufferHandle)); + GLCHK(glEnableVertexAttribArray(mProgramTexCoord1)); + GLCHK(glVertexAttribPointer( + mProgramTexCoord1, 2, GL_FLOAT, false, 0, 0)); + + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, mTexCoord2BufferHandle)); + GLCHK(glEnableVertexAttribArray(mProgramTexCoord2)); + GLCHK(glVertexAttribPointer( + mProgramTexCoord2, 2, GL_FLOAT, false, 0, 0)); + + float ratio; + if ((mRotation == 90) || (mRotation == 270)) + ratio = (float)textureHeight / (float)textureWidth; + else + ratio = (float)textureWidth / (float)textureHeight; + + if (ratio > 1.) { + GLCHK(glUniform2f( + mProgramEyeToSourceUVScale, mScale, mScale * ratio)); + } else { + GLCHK(glUniform2f( + mProgramEyeToSourceUVScale, mScale / ratio, mScale)); + } + + GLCHK(glUniform2f(mProgramEyeToSourceUVOffset, mPanH, mPanV)); + GLCHK(glUniform1i(mProgramChromaticAberrationCorrection, 0)); + GLCHK(glUniform1i(mProgramRotation, mRotation)); + GLCHK(glUniform1i(mProgramLensLimits, 0)); + GLCHK(glUniform2f(mProgramEyeToSourceScale, + 2.f / mMetricsWidth, + -2.f / mMetricsHeight)); + GLCHK(glUniform2f(mProgramEyeToSourceOffset, + 2.f * mEyeOffsetX / mMetricsWidth, + 2.f * mEyeOffsetY / mMetricsHeight - 1.f)); + + GLCHK(glDrawElements(GL_TRIANGLES, + sizeof(pdraw_gles2HmdIndices) / sizeof(float), + GL_UNSIGNED_INT, + 0)); + + GLCHK(glDisableVertexAttribArray(mProgramPosition)); + GLCHK(glDisableVertexAttribArray(mProgramColor)); + GLCHK(glDisableVertexAttribArray(mProgramTexCoord0)); + GLCHK(glDisableVertexAttribArray(mProgramTexCoord1)); + GLCHK(glDisableVertexAttribArray(mProgramTexCoord2)); + + GLCHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + return 0; +} + + +Gles2Hmd::Gles2Hmd(Session *session, + unsigned int firstTexUnit, + unsigned int width, + unsigned int height, + float ipdOffset, + float xOffset, + float yOffset) +{ + Settings *settings; + + mSession = session; + mHmdModel = PDRAW_HMD_MODEL_UNKNOWN; + mIpdOffset = ipdOffset; + mXOffset = xOffset; + mYOffset = yOffset; + mXdpi = SETTINGS_DISPLAY_XDPI; + mYdpi = SETTINGS_DISPLAY_YDPI; + mDeviceMarginTop = 0.f; + mDeviceMarginBottom = 0.f; + mDeviceMarginLeft = 0.f; + mDeviceMarginRight = 0.f; + + settings = mSession->getSettings(); + settings->lock(); + settings->getDisplayScreenSettings(&mXdpi, + &mYdpi, + &mDeviceMarginTop, + &mDeviceMarginBottom, + &mDeviceMarginLeft, + &mDeviceMarginRight); + mHmdModel = settings->getHmdModelSetting(); + settings->unlock(); + + switch (mHmdModel) { + default: + case PDRAW_HMD_MODEL_COCKPITGLASSES: + mHmdOffset = GLES2_HMD_OFFSET_COCKPITGLASSES; + mHmdIpd = GLES2_HMD_IPD_COCKPITGLASSES; + break; + case PDRAW_HMD_MODEL_COCKPITGLASSES_2: + mHmdOffset = GLES2_HMD_OFFSET_COCKPITGLASSES_2; + mHmdIpd = GLES2_HMD_IPD_COCKPITGLASSES_2; + break; + } + + mMetricsWidth = (float)width / mXdpi * GLES2_HMD_INCH_TO_MILLIMETER; + mMetricsHeight = (float)height / mYdpi * GLES2_HMD_INCH_TO_MILLIMETER; + + mLeftEye = new Gles2HmdEye(firstTexUnit, + mHmdModel, + 1.f, + 0.f, + 0.f, + mMetricsWidth, + mMetricsHeight, + -(mHmdIpd + mIpdOffset) / 2.f + mXOffset, + mHmdOffset - mDeviceMarginBottom - mYOffset); + mRightEye = + new Gles2HmdEye(firstTexUnit, + mHmdModel, + 1.f, + 0.f, + 0.f, + mMetricsWidth, + mMetricsHeight, + (mHmdIpd + mIpdOffset) / 2.f + mXOffset, + mHmdOffset - mDeviceMarginBottom - mYOffset); +} + + +Gles2Hmd::~Gles2Hmd(void) +{ + if (mLeftEye) + delete (mLeftEye); + if (mRightEye) + delete (mRightEye); +} + + +int Gles2Hmd::renderHmd(GLuint texture, + unsigned int textureWidth, + unsigned int textureHeight) +{ + int ret = 0; + + if (mLeftEye != nullptr) { + ret = mLeftEye->renderEye(texture, textureWidth, textureHeight); + if (ret != 0) + return ret; + } + + if (mRightEye != nullptr) { + ret = mRightEye->renderEye( + texture, textureWidth, textureHeight); + if (ret != 0) + return ret; + } + + return 0; +} + +} /* namespace Pdraw */ + +#endif /* USE_GLES2 */ diff --git a/libpdraw/src/pdraw_gles2_hmd.hpp b/libpdraw/src/pdraw_gles2_hmd.hpp index b9914fc..1a928d8 100644 --- a/libpdraw/src/pdraw_gles2_hmd.hpp +++ b/libpdraw/src/pdraw_gles2_hmd.hpp @@ -1,151 +1,151 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * OpenGL ES 2.0 HMD distortion correction - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * Extracted from FPVToolbox by FrĂ©dĂ©ric Bertolus - * https://github.com/niavok/fpvtoolbox - * and translated from Java to C++ - */ - -#ifndef _PDRAW_GLES2_HMD_HPP_ -#define _PDRAW_GLES2_HMD_HPP_ - -#ifdef USE_GLES2 - -# include "pdraw_gles2_common.hpp" -# include "pdraw_session.hpp" -# include "pdraw_settings.hpp" - -# include - -namespace Pdraw { - - -# define GLES2_HMD_TEX_UNIT_COUNT 1 - - -class Gles2HmdEye { -public: - Gles2HmdEye(unsigned int firstTexUnit, - enum pdraw_hmd_model hmdModel, - float scale, - float panH, - float panV, - float metricsWidth, - float metricsHeight, - float eyeOffsetX, - float eyeOffsetY); - - ~Gles2HmdEye(void); - - int renderEye(GLuint texture, - unsigned int textureWidth, - unsigned int textureHeight); - -private: - unsigned int mRotation; - enum pdraw_hmd_model mHmdModel; - float mScale; - float mPanH; - float mPanV; - float mMetricsWidth; - float mMetricsHeight; - float mEyeOffsetX; - float mEyeOffsetY; - unsigned int mFirstTexUnit; - GLint mProgram; - GLint mIndicesBufferHandle; - GLint mPositionBufferHandle; - GLint mColorBufferHandle; - GLint mTexCoord0BufferHandle; - GLint mTexCoord1BufferHandle; - GLint mTexCoord2BufferHandle; - GLint mProgramTexture; - GLint mProgramEyeToSourceUVScale; - GLint mProgramEyeToSourceUVOffset; - GLint mProgramEyeToSourceScale; - GLint mProgramEyeToSourceOffset; - GLint mProgramChromaticAberrationCorrection; - GLint mProgramRotation; - GLint mProgramLensLimits; - GLint mProgramPosition; - GLint mProgramColor; - GLint mProgramTexCoord0; - GLint mProgramTexCoord1; - GLint mProgramTexCoord2; -}; - - -class Gles2Hmd { -public: - Gles2Hmd(Session *session, - unsigned int firstTexUnit, - unsigned int width, - unsigned int height, - float ipdOffset = 0.f, - float xOffset = 0.f, - float yOffset = 0.f); - - ~Gles2Hmd(void); - - static int getTexUnitCount(void) - { - return GLES2_HMD_TEX_UNIT_COUNT; - } - - int renderHmd(GLuint texture, - unsigned int textureWidth, - unsigned int textureHeight); - -private: - Session *mSession; - enum pdraw_hmd_model mHmdModel; - float mHmdOffset; - float mHmdIpd; - float mIpdOffset; - float mXOffset; - float mYOffset; - float mXdpi; - float mYdpi; - float mDeviceMarginTop; - float mDeviceMarginBottom; - float mDeviceMarginLeft; - float mDeviceMarginRight; - float mMetricsWidth; - float mMetricsHeight; - Gles2HmdEye *mLeftEye; - Gles2HmdEye *mRightEye; -}; - -} /* namespace Pdraw */ - -#endif /* USE_GLES2 */ - -#endif /* !_PDRAW_GLES2_HMD_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * OpenGL ES 2.0 HMD distortion correction + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Extracted from FPVToolbox by FrĂ©dĂ©ric Bertolus + * https://github.com/niavok/fpvtoolbox + * and translated from Java to C++ + */ + +#ifndef _PDRAW_GLES2_HMD_HPP_ +#define _PDRAW_GLES2_HMD_HPP_ + +#ifdef USE_GLES2 + +# include "pdraw_gles2_common.hpp" +# include "pdraw_session.hpp" +# include "pdraw_settings.hpp" + +# include + +namespace Pdraw { + + +# define GLES2_HMD_TEX_UNIT_COUNT 1 + + +class Gles2HmdEye { +public: + Gles2HmdEye(unsigned int firstTexUnit, + enum pdraw_hmd_model hmdModel, + float scale, + float panH, + float panV, + float metricsWidth, + float metricsHeight, + float eyeOffsetX, + float eyeOffsetY); + + ~Gles2HmdEye(void); + + int renderEye(GLuint texture, + unsigned int textureWidth, + unsigned int textureHeight); + +private: + unsigned int mRotation; + enum pdraw_hmd_model mHmdModel; + float mScale; + float mPanH; + float mPanV; + float mMetricsWidth; + float mMetricsHeight; + float mEyeOffsetX; + float mEyeOffsetY; + unsigned int mFirstTexUnit; + GLint mProgram; + GLint mIndicesBufferHandle; + GLint mPositionBufferHandle; + GLint mColorBufferHandle; + GLint mTexCoord0BufferHandle; + GLint mTexCoord1BufferHandle; + GLint mTexCoord2BufferHandle; + GLint mProgramTexture; + GLint mProgramEyeToSourceUVScale; + GLint mProgramEyeToSourceUVOffset; + GLint mProgramEyeToSourceScale; + GLint mProgramEyeToSourceOffset; + GLint mProgramChromaticAberrationCorrection; + GLint mProgramRotation; + GLint mProgramLensLimits; + GLint mProgramPosition; + GLint mProgramColor; + GLint mProgramTexCoord0; + GLint mProgramTexCoord1; + GLint mProgramTexCoord2; +}; + + +class Gles2Hmd { +public: + Gles2Hmd(Session *session, + unsigned int firstTexUnit, + unsigned int width, + unsigned int height, + float ipdOffset = 0.f, + float xOffset = 0.f, + float yOffset = 0.f); + + ~Gles2Hmd(void); + + static int getTexUnitCount(void) + { + return GLES2_HMD_TEX_UNIT_COUNT; + } + + int renderHmd(GLuint texture, + unsigned int textureWidth, + unsigned int textureHeight); + +private: + Session *mSession; + enum pdraw_hmd_model mHmdModel; + float mHmdOffset; + float mHmdIpd; + float mIpdOffset; + float mXOffset; + float mYOffset; + float mXdpi; + float mYdpi; + float mDeviceMarginTop; + float mDeviceMarginBottom; + float mDeviceMarginLeft; + float mDeviceMarginRight; + float mMetricsWidth; + float mMetricsHeight; + Gles2HmdEye *mLeftEye; + Gles2HmdEye *mRightEye; +}; + +} /* namespace Pdraw */ + +#endif /* USE_GLES2 */ + +#endif /* !_PDRAW_GLES2_HMD_HPP_ */ diff --git a/libpdraw/src/pdraw_gles2_hmd_colors.cpp b/libpdraw/src/pdraw_gles2_hmd_colors.cpp index 4b9d897..8807813 100644 --- a/libpdraw/src/pdraw_gles2_hmd_colors.cpp +++ b/libpdraw/src/pdraw_gles2_hmd_colors.cpp @@ -1,3767 +1,3767 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * OpenGL ES 2.0 HMD distortion correction - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * Extracted from FPVToolbox by FrĂ©dĂ©ric Bertolus - * https://github.com/niavok/fpvtoolbox - * and translated from Java to C++ - */ - -#ifdef USE_GLES2 - -namespace Pdraw { - -extern const float pdraw_gles2HmdColors[14884] = { - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, - 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, -}; - -} /* namespace Pdraw */ - -#endif /* USE_GLES2 */ +/** + * Parrot Drones Awesome Video Viewer Library + * OpenGL ES 2.0 HMD distortion correction + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Extracted from FPVToolbox by Frédéric Bertolus + * https://github.com/niavok/fpvtoolbox + * and translated from Java to C++ + */ + +#ifdef USE_GLES2 + +namespace Pdraw { + +extern const float pdraw_gles2HmdColors[14884] = { + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 1.000000000000, 1.000000000000, 1.000000000000, 1.000000000000, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.833333313465, 0.833333313465, 0.833333313465, 0.833333313465, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.666666686535, 0.666666686535, 0.666666686535, 0.666666686535, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.500000000000, 0.500000000000, 0.500000000000, 0.500000000000, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.333333343267, 0.333333343267, 0.333333343267, 0.333333343267, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.166666671634, 0.166666671634, 0.166666671634, 0.166666671634, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, +}; + +} /* namespace Pdraw */ + +#endif /* USE_GLES2 */ diff --git a/libpdraw/src/pdraw_gles2_hmd_indices.cpp b/libpdraw/src/pdraw_gles2_hmd_indices.cpp index 3540135..4ba89b5 100644 --- a/libpdraw/src/pdraw_gles2_hmd_indices.cpp +++ b/libpdraw/src/pdraw_gles2_hmd_indices.cpp @@ -1,1848 +1,1848 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * OpenGL ES 2.0 HMD distortion correction - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * Extracted from FPVToolbox by Frédéric Bertolus - * https://github.com/niavok/fpvtoolbox - * and translated from Java to C++ - */ - -#ifdef USE_GLES2 - -# include - -namespace Pdraw { - -extern const uint32_t pdraw_gles2HmdIndices[21600] = { - 0, 61, 1, 1, 61, 62, 61, 122, 62, 62, 122, 123, - 122, 183, 123, 123, 183, 184, 183, 244, 184, 184, 244, 245, - 244, 305, 245, 245, 305, 306, 305, 366, 306, 306, 366, 367, - 366, 427, 367, 367, 427, 428, 427, 488, 428, 428, 488, 489, - 488, 549, 489, 489, 549, 550, 549, 610, 550, 550, 610, 611, - 610, 671, 611, 611, 671, 672, 671, 732, 672, 672, 732, 733, - 732, 793, 733, 733, 793, 794, 793, 854, 794, 794, 854, 855, - 854, 915, 855, 855, 915, 916, 915, 976, 916, 916, 976, 977, - 976, 1037, 977, 977, 1037, 1038, 1037, 1098, 1038, 1038, 1098, 1099, - 1098, 1159, 1099, 1099, 1159, 1160, 1159, 1220, 1160, 1160, 1220, 1221, - 1220, 1281, 1221, 1221, 1281, 1282, 1281, 1342, 1282, 1282, 1342, 1343, - 1342, 1403, 1343, 1343, 1403, 1404, 1403, 1464, 1404, 1404, 1464, 1465, - 1464, 1525, 1465, 1465, 1525, 1526, 1525, 1586, 1526, 1526, 1586, 1587, - 1586, 1647, 1587, 1587, 1647, 1648, 1647, 1708, 1648, 1648, 1708, 1709, - 1708, 1769, 1709, 1709, 1769, 1770, 1769, 1830, 1770, 1770, 1830, 1831, - 1830, 1891, 1831, 1831, 1891, 1892, 1891, 1952, 1892, 1892, 1952, 1953, - 1952, 2013, 1953, 1953, 2013, 2014, 2013, 2074, 2014, 2014, 2074, 2075, - 2074, 2135, 2075, 2075, 2135, 2136, 2135, 2196, 2136, 2136, 2196, 2197, - 2196, 2257, 2197, 2197, 2257, 2258, 2257, 2318, 2258, 2258, 2318, 2319, - 2318, 2379, 2319, 2319, 2379, 2380, 2379, 2440, 2380, 2380, 2440, 2441, - 2440, 2501, 2441, 2441, 2501, 2502, 2501, 2562, 2502, 2502, 2562, 2563, - 2562, 2623, 2563, 2563, 2623, 2624, 2623, 2684, 2624, 2624, 2684, 2685, - 2684, 2745, 2685, 2685, 2745, 2746, 2745, 2806, 2746, 2746, 2806, 2807, - 2806, 2867, 2807, 2807, 2867, 2868, 2867, 2928, 2868, 2868, 2928, 2929, - 2928, 2989, 2929, 2929, 2989, 2990, 2989, 3050, 2990, 2990, 3050, 3051, - 3050, 3111, 3051, 3051, 3111, 3112, 3111, 3172, 3112, 3112, 3172, 3173, - 3172, 3233, 3173, 3173, 3233, 3234, 3233, 3294, 3234, 3234, 3294, 3295, - 3294, 3355, 3295, 3295, 3355, 3356, 3355, 3416, 3356, 3356, 3416, 3417, - 3416, 3477, 3417, 3417, 3477, 3478, 3477, 3538, 3478, 3478, 3538, 3539, - 3538, 3599, 3539, 3539, 3599, 3600, 3599, 3660, 3600, 3600, 3660, 3661, - 1, 62, 2, 2, 62, 63, 62, 123, 63, 63, 123, 124, - 123, 184, 124, 124, 184, 185, 184, 245, 185, 185, 245, 246, - 245, 306, 246, 246, 306, 307, 306, 367, 307, 307, 367, 368, - 367, 428, 368, 368, 428, 429, 428, 489, 429, 429, 489, 490, - 489, 550, 490, 490, 550, 551, 550, 611, 551, 551, 611, 612, - 611, 672, 612, 612, 672, 673, 672, 733, 673, 673, 733, 734, - 733, 794, 734, 734, 794, 795, 794, 855, 795, 795, 855, 856, - 855, 916, 856, 856, 916, 917, 916, 977, 917, 917, 977, 978, - 977, 1038, 978, 978, 1038, 1039, 1038, 1099, 1039, 1039, 1099, 1100, - 1099, 1160, 1100, 1100, 1160, 1161, 1160, 1221, 1161, 1161, 1221, 1222, - 1221, 1282, 1222, 1222, 1282, 1283, 1282, 1343, 1283, 1283, 1343, 1344, - 1343, 1404, 1344, 1344, 1404, 1405, 1404, 1465, 1405, 1405, 1465, 1466, - 1465, 1526, 1466, 1466, 1526, 1527, 1526, 1587, 1527, 1527, 1587, 1588, - 1587, 1648, 1588, 1588, 1648, 1649, 1648, 1709, 1649, 1649, 1709, 1710, - 1709, 1770, 1710, 1710, 1770, 1771, 1770, 1831, 1771, 1771, 1831, 1832, - 1831, 1892, 1832, 1832, 1892, 1893, 1892, 1953, 1893, 1893, 1953, 1954, - 1953, 2014, 1954, 1954, 2014, 2015, 2014, 2075, 2015, 2015, 2075, 2076, - 2075, 2136, 2076, 2076, 2136, 2137, 2136, 2197, 2137, 2137, 2197, 2198, - 2197, 2258, 2198, 2198, 2258, 2259, 2258, 2319, 2259, 2259, 2319, 2320, - 2319, 2380, 2320, 2320, 2380, 2381, 2380, 2441, 2381, 2381, 2441, 2442, - 2441, 2502, 2442, 2442, 2502, 2503, 2502, 2563, 2503, 2503, 2563, 2564, - 2563, 2624, 2564, 2564, 2624, 2625, 2624, 2685, 2625, 2625, 2685, 2686, - 2685, 2746, 2686, 2686, 2746, 2747, 2746, 2807, 2747, 2747, 2807, 2808, - 2807, 2868, 2808, 2808, 2868, 2869, 2868, 2929, 2869, 2869, 2929, 2930, - 2929, 2990, 2930, 2930, 2990, 2991, 2990, 3051, 2991, 2991, 3051, 3052, - 3051, 3112, 3052, 3052, 3112, 3113, 3112, 3173, 3113, 3113, 3173, 3174, - 3173, 3234, 3174, 3174, 3234, 3235, 3234, 3295, 3235, 3235, 3295, 3296, - 3295, 3356, 3296, 3296, 3356, 3357, 3356, 3417, 3357, 3357, 3417, 3418, - 3417, 3478, 3418, 3418, 3478, 3479, 3478, 3539, 3479, 3479, 3539, 3540, - 3539, 3600, 3540, 3540, 3600, 3601, 3600, 3661, 3601, 3601, 3661, 3662, - 2, 63, 3, 3, 63, 64, 63, 124, 64, 64, 124, 125, - 124, 185, 125, 125, 185, 186, 185, 246, 186, 186, 246, 247, - 246, 307, 247, 247, 307, 308, 307, 368, 308, 308, 368, 369, - 368, 429, 369, 369, 429, 430, 429, 490, 430, 430, 490, 491, - 490, 551, 491, 491, 551, 552, 551, 612, 552, 552, 612, 613, - 612, 673, 613, 613, 673, 674, 673, 734, 674, 674, 734, 735, - 734, 795, 735, 735, 795, 796, 795, 856, 796, 796, 856, 857, - 856, 917, 857, 857, 917, 918, 917, 978, 918, 918, 978, 979, - 978, 1039, 979, 979, 1039, 1040, 1039, 1100, 1040, 1040, 1100, 1101, - 1100, 1161, 1101, 1101, 1161, 1162, 1161, 1222, 1162, 1162, 1222, 1223, - 1222, 1283, 1223, 1223, 1283, 1284, 1283, 1344, 1284, 1284, 1344, 1345, - 1344, 1405, 1345, 1345, 1405, 1406, 1405, 1466, 1406, 1406, 1466, 1467, - 1466, 1527, 1467, 1467, 1527, 1528, 1527, 1588, 1528, 1528, 1588, 1589, - 1588, 1649, 1589, 1589, 1649, 1650, 1649, 1710, 1650, 1650, 1710, 1711, - 1710, 1771, 1711, 1711, 1771, 1772, 1771, 1832, 1772, 1772, 1832, 1833, - 1832, 1893, 1833, 1833, 1893, 1894, 1893, 1954, 1894, 1894, 1954, 1955, - 1954, 2015, 1955, 1955, 2015, 2016, 2015, 2076, 2016, 2016, 2076, 2077, - 2076, 2137, 2077, 2077, 2137, 2138, 2137, 2198, 2138, 2138, 2198, 2199, - 2198, 2259, 2199, 2199, 2259, 2260, 2259, 2320, 2260, 2260, 2320, 2321, - 2320, 2381, 2321, 2321, 2381, 2382, 2381, 2442, 2382, 2382, 2442, 2443, - 2442, 2503, 2443, 2443, 2503, 2504, 2503, 2564, 2504, 2504, 2564, 2565, - 2564, 2625, 2565, 2565, 2625, 2626, 2625, 2686, 2626, 2626, 2686, 2687, - 2686, 2747, 2687, 2687, 2747, 2748, 2747, 2808, 2748, 2748, 2808, 2809, - 2808, 2869, 2809, 2809, 2869, 2870, 2869, 2930, 2870, 2870, 2930, 2931, - 2930, 2991, 2931, 2931, 2991, 2992, 2991, 3052, 2992, 2992, 3052, 3053, - 3052, 3113, 3053, 3053, 3113, 3114, 3113, 3174, 3114, 3114, 3174, 3175, - 3174, 3235, 3175, 3175, 3235, 3236, 3235, 3296, 3236, 3236, 3296, 3297, - 3296, 3357, 3297, 3297, 3357, 3358, 3357, 3418, 3358, 3358, 3418, 3419, - 3418, 3479, 3419, 3419, 3479, 3480, 3479, 3540, 3480, 3480, 3540, 3541, - 3540, 3601, 3541, 3541, 3601, 3602, 3601, 3662, 3602, 3602, 3662, 3663, - 3, 64, 4, 4, 64, 65, 64, 125, 65, 65, 125, 126, - 125, 186, 126, 126, 186, 187, 186, 247, 187, 187, 247, 248, - 247, 308, 248, 248, 308, 309, 308, 369, 309, 309, 369, 370, - 369, 430, 370, 370, 430, 431, 430, 491, 431, 431, 491, 492, - 491, 552, 492, 492, 552, 553, 552, 613, 553, 553, 613, 614, - 613, 674, 614, 614, 674, 675, 674, 735, 675, 675, 735, 736, - 735, 796, 736, 736, 796, 797, 796, 857, 797, 797, 857, 858, - 857, 918, 858, 858, 918, 919, 918, 979, 919, 919, 979, 980, - 979, 1040, 980, 980, 1040, 1041, 1040, 1101, 1041, 1041, 1101, 1102, - 1101, 1162, 1102, 1102, 1162, 1163, 1162, 1223, 1163, 1163, 1223, 1224, - 1223, 1284, 1224, 1224, 1284, 1285, 1284, 1345, 1285, 1285, 1345, 1346, - 1345, 1406, 1346, 1346, 1406, 1407, 1406, 1467, 1407, 1407, 1467, 1468, - 1467, 1528, 1468, 1468, 1528, 1529, 1528, 1589, 1529, 1529, 1589, 1590, - 1589, 1650, 1590, 1590, 1650, 1651, 1650, 1711, 1651, 1651, 1711, 1712, - 1711, 1772, 1712, 1712, 1772, 1773, 1772, 1833, 1773, 1773, 1833, 1834, - 1833, 1894, 1834, 1834, 1894, 1895, 1894, 1955, 1895, 1895, 1955, 1956, - 1955, 2016, 1956, 1956, 2016, 2017, 2016, 2077, 2017, 2017, 2077, 2078, - 2077, 2138, 2078, 2078, 2138, 2139, 2138, 2199, 2139, 2139, 2199, 2200, - 2199, 2260, 2200, 2200, 2260, 2261, 2260, 2321, 2261, 2261, 2321, 2322, - 2321, 2382, 2322, 2322, 2382, 2383, 2382, 2443, 2383, 2383, 2443, 2444, - 2443, 2504, 2444, 2444, 2504, 2505, 2504, 2565, 2505, 2505, 2565, 2566, - 2565, 2626, 2566, 2566, 2626, 2627, 2626, 2687, 2627, 2627, 2687, 2688, - 2687, 2748, 2688, 2688, 2748, 2749, 2748, 2809, 2749, 2749, 2809, 2810, - 2809, 2870, 2810, 2810, 2870, 2871, 2870, 2931, 2871, 2871, 2931, 2932, - 2931, 2992, 2932, 2932, 2992, 2993, 2992, 3053, 2993, 2993, 3053, 3054, - 3053, 3114, 3054, 3054, 3114, 3115, 3114, 3175, 3115, 3115, 3175, 3176, - 3175, 3236, 3176, 3176, 3236, 3237, 3236, 3297, 3237, 3237, 3297, 3298, - 3297, 3358, 3298, 3298, 3358, 3359, 3358, 3419, 3359, 3359, 3419, 3420, - 3419, 3480, 3420, 3420, 3480, 3481, 3480, 3541, 3481, 3481, 3541, 3542, - 3541, 3602, 3542, 3542, 3602, 3603, 3602, 3663, 3603, 3603, 3663, 3664, - 4, 65, 5, 5, 65, 66, 65, 126, 66, 66, 126, 127, - 126, 187, 127, 127, 187, 188, 187, 248, 188, 188, 248, 249, - 248, 309, 249, 249, 309, 310, 309, 370, 310, 310, 370, 371, - 370, 431, 371, 371, 431, 432, 431, 492, 432, 432, 492, 493, - 492, 553, 493, 493, 553, 554, 553, 614, 554, 554, 614, 615, - 614, 675, 615, 615, 675, 676, 675, 736, 676, 676, 736, 737, - 736, 797, 737, 737, 797, 798, 797, 858, 798, 798, 858, 859, - 858, 919, 859, 859, 919, 920, 919, 980, 920, 920, 980, 981, - 980, 1041, 981, 981, 1041, 1042, 1041, 1102, 1042, 1042, 1102, 1103, - 1102, 1163, 1103, 1103, 1163, 1164, 1163, 1224, 1164, 1164, 1224, 1225, - 1224, 1285, 1225, 1225, 1285, 1286, 1285, 1346, 1286, 1286, 1346, 1347, - 1346, 1407, 1347, 1347, 1407, 1408, 1407, 1468, 1408, 1408, 1468, 1469, - 1468, 1529, 1469, 1469, 1529, 1530, 1529, 1590, 1530, 1530, 1590, 1591, - 1590, 1651, 1591, 1591, 1651, 1652, 1651, 1712, 1652, 1652, 1712, 1713, - 1712, 1773, 1713, 1713, 1773, 1774, 1773, 1834, 1774, 1774, 1834, 1835, - 1834, 1895, 1835, 1835, 1895, 1896, 1895, 1956, 1896, 1896, 1956, 1957, - 1956, 2017, 1957, 1957, 2017, 2018, 2017, 2078, 2018, 2018, 2078, 2079, - 2078, 2139, 2079, 2079, 2139, 2140, 2139, 2200, 2140, 2140, 2200, 2201, - 2200, 2261, 2201, 2201, 2261, 2262, 2261, 2322, 2262, 2262, 2322, 2323, - 2322, 2383, 2323, 2323, 2383, 2384, 2383, 2444, 2384, 2384, 2444, 2445, - 2444, 2505, 2445, 2445, 2505, 2506, 2505, 2566, 2506, 2506, 2566, 2567, - 2566, 2627, 2567, 2567, 2627, 2628, 2627, 2688, 2628, 2628, 2688, 2689, - 2688, 2749, 2689, 2689, 2749, 2750, 2749, 2810, 2750, 2750, 2810, 2811, - 2810, 2871, 2811, 2811, 2871, 2872, 2871, 2932, 2872, 2872, 2932, 2933, - 2932, 2993, 2933, 2933, 2993, 2994, 2993, 3054, 2994, 2994, 3054, 3055, - 3054, 3115, 3055, 3055, 3115, 3116, 3115, 3176, 3116, 3116, 3176, 3177, - 3176, 3237, 3177, 3177, 3237, 3238, 3237, 3298, 3238, 3238, 3298, 3299, - 3298, 3359, 3299, 3299, 3359, 3360, 3359, 3420, 3360, 3360, 3420, 3421, - 3420, 3481, 3421, 3421, 3481, 3482, 3481, 3542, 3482, 3482, 3542, 3543, - 3542, 3603, 3543, 3543, 3603, 3604, 3603, 3664, 3604, 3604, 3664, 3665, - 5, 66, 6, 6, 66, 67, 66, 127, 67, 67, 127, 128, - 127, 188, 128, 128, 188, 189, 188, 249, 189, 189, 249, 250, - 249, 310, 250, 250, 310, 311, 310, 371, 311, 311, 371, 372, - 371, 432, 372, 372, 432, 433, 432, 493, 433, 433, 493, 494, - 493, 554, 494, 494, 554, 555, 554, 615, 555, 555, 615, 616, - 615, 676, 616, 616, 676, 677, 676, 737, 677, 677, 737, 738, - 737, 798, 738, 738, 798, 799, 798, 859, 799, 799, 859, 860, - 859, 920, 860, 860, 920, 921, 920, 981, 921, 921, 981, 982, - 981, 1042, 982, 982, 1042, 1043, 1042, 1103, 1043, 1043, 1103, 1104, - 1103, 1164, 1104, 1104, 1164, 1165, 1164, 1225, 1165, 1165, 1225, 1226, - 1225, 1286, 1226, 1226, 1286, 1287, 1286, 1347, 1287, 1287, 1347, 1348, - 1347, 1408, 1348, 1348, 1408, 1409, 1408, 1469, 1409, 1409, 1469, 1470, - 1469, 1530, 1470, 1470, 1530, 1531, 1530, 1591, 1531, 1531, 1591, 1592, - 1591, 1652, 1592, 1592, 1652, 1653, 1652, 1713, 1653, 1653, 1713, 1714, - 1713, 1774, 1714, 1714, 1774, 1775, 1774, 1835, 1775, 1775, 1835, 1836, - 1835, 1896, 1836, 1836, 1896, 1897, 1896, 1957, 1897, 1897, 1957, 1958, - 1957, 2018, 1958, 1958, 2018, 2019, 2018, 2079, 2019, 2019, 2079, 2080, - 2079, 2140, 2080, 2080, 2140, 2141, 2140, 2201, 2141, 2141, 2201, 2202, - 2201, 2262, 2202, 2202, 2262, 2263, 2262, 2323, 2263, 2263, 2323, 2324, - 2323, 2384, 2324, 2324, 2384, 2385, 2384, 2445, 2385, 2385, 2445, 2446, - 2445, 2506, 2446, 2446, 2506, 2507, 2506, 2567, 2507, 2507, 2567, 2568, - 2567, 2628, 2568, 2568, 2628, 2629, 2628, 2689, 2629, 2629, 2689, 2690, - 2689, 2750, 2690, 2690, 2750, 2751, 2750, 2811, 2751, 2751, 2811, 2812, - 2811, 2872, 2812, 2812, 2872, 2873, 2872, 2933, 2873, 2873, 2933, 2934, - 2933, 2994, 2934, 2934, 2994, 2995, 2994, 3055, 2995, 2995, 3055, 3056, - 3055, 3116, 3056, 3056, 3116, 3117, 3116, 3177, 3117, 3117, 3177, 3178, - 3177, 3238, 3178, 3178, 3238, 3239, 3238, 3299, 3239, 3239, 3299, 3300, - 3299, 3360, 3300, 3300, 3360, 3361, 3360, 3421, 3361, 3361, 3421, 3422, - 3421, 3482, 3422, 3422, 3482, 3483, 3482, 3543, 3483, 3483, 3543, 3544, - 3543, 3604, 3544, 3544, 3604, 3605, 3604, 3665, 3605, 3605, 3665, 3666, - 6, 67, 7, 7, 67, 68, 67, 128, 68, 68, 128, 129, - 128, 189, 129, 129, 189, 190, 189, 250, 190, 190, 250, 251, - 250, 311, 251, 251, 311, 312, 311, 372, 312, 312, 372, 373, - 372, 433, 373, 373, 433, 434, 433, 494, 434, 434, 494, 495, - 494, 555, 495, 495, 555, 556, 555, 616, 556, 556, 616, 617, - 616, 677, 617, 617, 677, 678, 677, 738, 678, 678, 738, 739, - 738, 799, 739, 739, 799, 800, 799, 860, 800, 800, 860, 861, - 860, 921, 861, 861, 921, 922, 921, 982, 922, 922, 982, 983, - 982, 1043, 983, 983, 1043, 1044, 1043, 1104, 1044, 1044, 1104, 1105, - 1104, 1165, 1105, 1105, 1165, 1166, 1165, 1226, 1166, 1166, 1226, 1227, - 1226, 1287, 1227, 1227, 1287, 1288, 1287, 1348, 1288, 1288, 1348, 1349, - 1348, 1409, 1349, 1349, 1409, 1410, 1409, 1470, 1410, 1410, 1470, 1471, - 1470, 1531, 1471, 1471, 1531, 1532, 1531, 1592, 1532, 1532, 1592, 1593, - 1592, 1653, 1593, 1593, 1653, 1654, 1653, 1714, 1654, 1654, 1714, 1715, - 1714, 1775, 1715, 1715, 1775, 1776, 1775, 1836, 1776, 1776, 1836, 1837, - 1836, 1897, 1837, 1837, 1897, 1898, 1897, 1958, 1898, 1898, 1958, 1959, - 1958, 2019, 1959, 1959, 2019, 2020, 2019, 2080, 2020, 2020, 2080, 2081, - 2080, 2141, 2081, 2081, 2141, 2142, 2141, 2202, 2142, 2142, 2202, 2203, - 2202, 2263, 2203, 2203, 2263, 2264, 2263, 2324, 2264, 2264, 2324, 2325, - 2324, 2385, 2325, 2325, 2385, 2386, 2385, 2446, 2386, 2386, 2446, 2447, - 2446, 2507, 2447, 2447, 2507, 2508, 2507, 2568, 2508, 2508, 2568, 2569, - 2568, 2629, 2569, 2569, 2629, 2630, 2629, 2690, 2630, 2630, 2690, 2691, - 2690, 2751, 2691, 2691, 2751, 2752, 2751, 2812, 2752, 2752, 2812, 2813, - 2812, 2873, 2813, 2813, 2873, 2874, 2873, 2934, 2874, 2874, 2934, 2935, - 2934, 2995, 2935, 2935, 2995, 2996, 2995, 3056, 2996, 2996, 3056, 3057, - 3056, 3117, 3057, 3057, 3117, 3118, 3117, 3178, 3118, 3118, 3178, 3179, - 3178, 3239, 3179, 3179, 3239, 3240, 3239, 3300, 3240, 3240, 3300, 3301, - 3300, 3361, 3301, 3301, 3361, 3362, 3361, 3422, 3362, 3362, 3422, 3423, - 3422, 3483, 3423, 3423, 3483, 3484, 3483, 3544, 3484, 3484, 3544, 3545, - 3544, 3605, 3545, 3545, 3605, 3606, 3605, 3666, 3606, 3606, 3666, 3667, - 7, 68, 8, 8, 68, 69, 68, 129, 69, 69, 129, 130, - 129, 190, 130, 130, 190, 191, 190, 251, 191, 191, 251, 252, - 251, 312, 252, 252, 312, 313, 312, 373, 313, 313, 373, 374, - 373, 434, 374, 374, 434, 435, 434, 495, 435, 435, 495, 496, - 495, 556, 496, 496, 556, 557, 556, 617, 557, 557, 617, 618, - 617, 678, 618, 618, 678, 679, 678, 739, 679, 679, 739, 740, - 739, 800, 740, 740, 800, 801, 800, 861, 801, 801, 861, 862, - 861, 922, 862, 862, 922, 923, 922, 983, 923, 923, 983, 984, - 983, 1044, 984, 984, 1044, 1045, 1044, 1105, 1045, 1045, 1105, 1106, - 1105, 1166, 1106, 1106, 1166, 1167, 1166, 1227, 1167, 1167, 1227, 1228, - 1227, 1288, 1228, 1228, 1288, 1289, 1288, 1349, 1289, 1289, 1349, 1350, - 1349, 1410, 1350, 1350, 1410, 1411, 1410, 1471, 1411, 1411, 1471, 1472, - 1471, 1532, 1472, 1472, 1532, 1533, 1532, 1593, 1533, 1533, 1593, 1594, - 1593, 1654, 1594, 1594, 1654, 1655, 1654, 1715, 1655, 1655, 1715, 1716, - 1715, 1776, 1716, 1716, 1776, 1777, 1776, 1837, 1777, 1777, 1837, 1838, - 1837, 1898, 1838, 1838, 1898, 1899, 1898, 1959, 1899, 1899, 1959, 1960, - 1959, 2020, 1960, 1960, 2020, 2021, 2020, 2081, 2021, 2021, 2081, 2082, - 2081, 2142, 2082, 2082, 2142, 2143, 2142, 2203, 2143, 2143, 2203, 2204, - 2203, 2264, 2204, 2204, 2264, 2265, 2264, 2325, 2265, 2265, 2325, 2326, - 2325, 2386, 2326, 2326, 2386, 2387, 2386, 2447, 2387, 2387, 2447, 2448, - 2447, 2508, 2448, 2448, 2508, 2509, 2508, 2569, 2509, 2509, 2569, 2570, - 2569, 2630, 2570, 2570, 2630, 2631, 2630, 2691, 2631, 2631, 2691, 2692, - 2691, 2752, 2692, 2692, 2752, 2753, 2752, 2813, 2753, 2753, 2813, 2814, - 2813, 2874, 2814, 2814, 2874, 2875, 2874, 2935, 2875, 2875, 2935, 2936, - 2935, 2996, 2936, 2936, 2996, 2997, 2996, 3057, 2997, 2997, 3057, 3058, - 3057, 3118, 3058, 3058, 3118, 3119, 3118, 3179, 3119, 3119, 3179, 3180, - 3179, 3240, 3180, 3180, 3240, 3241, 3240, 3301, 3241, 3241, 3301, 3302, - 3301, 3362, 3302, 3302, 3362, 3363, 3362, 3423, 3363, 3363, 3423, 3424, - 3423, 3484, 3424, 3424, 3484, 3485, 3484, 3545, 3485, 3485, 3545, 3546, - 3545, 3606, 3546, 3546, 3606, 3607, 3606, 3667, 3607, 3607, 3667, 3668, - 8, 69, 9, 9, 69, 70, 69, 130, 70, 70, 130, 131, - 130, 191, 131, 131, 191, 192, 191, 252, 192, 192, 252, 253, - 252, 313, 253, 253, 313, 314, 313, 374, 314, 314, 374, 375, - 374, 435, 375, 375, 435, 436, 435, 496, 436, 436, 496, 497, - 496, 557, 497, 497, 557, 558, 557, 618, 558, 558, 618, 619, - 618, 679, 619, 619, 679, 680, 679, 740, 680, 680, 740, 741, - 740, 801, 741, 741, 801, 802, 801, 862, 802, 802, 862, 863, - 862, 923, 863, 863, 923, 924, 923, 984, 924, 924, 984, 985, - 984, 1045, 985, 985, 1045, 1046, 1045, 1106, 1046, 1046, 1106, 1107, - 1106, 1167, 1107, 1107, 1167, 1168, 1167, 1228, 1168, 1168, 1228, 1229, - 1228, 1289, 1229, 1229, 1289, 1290, 1289, 1350, 1290, 1290, 1350, 1351, - 1350, 1411, 1351, 1351, 1411, 1412, 1411, 1472, 1412, 1412, 1472, 1473, - 1472, 1533, 1473, 1473, 1533, 1534, 1533, 1594, 1534, 1534, 1594, 1595, - 1594, 1655, 1595, 1595, 1655, 1656, 1655, 1716, 1656, 1656, 1716, 1717, - 1716, 1777, 1717, 1717, 1777, 1778, 1777, 1838, 1778, 1778, 1838, 1839, - 1838, 1899, 1839, 1839, 1899, 1900, 1899, 1960, 1900, 1900, 1960, 1961, - 1960, 2021, 1961, 1961, 2021, 2022, 2021, 2082, 2022, 2022, 2082, 2083, - 2082, 2143, 2083, 2083, 2143, 2144, 2143, 2204, 2144, 2144, 2204, 2205, - 2204, 2265, 2205, 2205, 2265, 2266, 2265, 2326, 2266, 2266, 2326, 2327, - 2326, 2387, 2327, 2327, 2387, 2388, 2387, 2448, 2388, 2388, 2448, 2449, - 2448, 2509, 2449, 2449, 2509, 2510, 2509, 2570, 2510, 2510, 2570, 2571, - 2570, 2631, 2571, 2571, 2631, 2632, 2631, 2692, 2632, 2632, 2692, 2693, - 2692, 2753, 2693, 2693, 2753, 2754, 2753, 2814, 2754, 2754, 2814, 2815, - 2814, 2875, 2815, 2815, 2875, 2876, 2875, 2936, 2876, 2876, 2936, 2937, - 2936, 2997, 2937, 2937, 2997, 2998, 2997, 3058, 2998, 2998, 3058, 3059, - 3058, 3119, 3059, 3059, 3119, 3120, 3119, 3180, 3120, 3120, 3180, 3181, - 3180, 3241, 3181, 3181, 3241, 3242, 3241, 3302, 3242, 3242, 3302, 3303, - 3302, 3363, 3303, 3303, 3363, 3364, 3363, 3424, 3364, 3364, 3424, 3425, - 3424, 3485, 3425, 3425, 3485, 3486, 3485, 3546, 3486, 3486, 3546, 3547, - 3546, 3607, 3547, 3547, 3607, 3608, 3607, 3668, 3608, 3608, 3668, 3669, - 9, 70, 10, 10, 70, 71, 70, 131, 71, 71, 131, 132, - 131, 192, 132, 132, 192, 193, 192, 253, 193, 193, 253, 254, - 253, 314, 254, 254, 314, 315, 314, 375, 315, 315, 375, 376, - 375, 436, 376, 376, 436, 437, 436, 497, 437, 437, 497, 498, - 497, 558, 498, 498, 558, 559, 558, 619, 559, 559, 619, 620, - 619, 680, 620, 620, 680, 681, 680, 741, 681, 681, 741, 742, - 741, 802, 742, 742, 802, 803, 802, 863, 803, 803, 863, 864, - 863, 924, 864, 864, 924, 925, 924, 985, 925, 925, 985, 986, - 985, 1046, 986, 986, 1046, 1047, 1046, 1107, 1047, 1047, 1107, 1108, - 1107, 1168, 1108, 1108, 1168, 1169, 1168, 1229, 1169, 1169, 1229, 1230, - 1229, 1290, 1230, 1230, 1290, 1291, 1290, 1351, 1291, 1291, 1351, 1352, - 1351, 1412, 1352, 1352, 1412, 1413, 1412, 1473, 1413, 1413, 1473, 1474, - 1473, 1534, 1474, 1474, 1534, 1535, 1534, 1595, 1535, 1535, 1595, 1596, - 1595, 1656, 1596, 1596, 1656, 1657, 1656, 1717, 1657, 1657, 1717, 1718, - 1717, 1778, 1718, 1718, 1778, 1779, 1778, 1839, 1779, 1779, 1839, 1840, - 1839, 1900, 1840, 1840, 1900, 1901, 1900, 1961, 1901, 1901, 1961, 1962, - 1961, 2022, 1962, 1962, 2022, 2023, 2022, 2083, 2023, 2023, 2083, 2084, - 2083, 2144, 2084, 2084, 2144, 2145, 2144, 2205, 2145, 2145, 2205, 2206, - 2205, 2266, 2206, 2206, 2266, 2267, 2266, 2327, 2267, 2267, 2327, 2328, - 2327, 2388, 2328, 2328, 2388, 2389, 2388, 2449, 2389, 2389, 2449, 2450, - 2449, 2510, 2450, 2450, 2510, 2511, 2510, 2571, 2511, 2511, 2571, 2572, - 2571, 2632, 2572, 2572, 2632, 2633, 2632, 2693, 2633, 2633, 2693, 2694, - 2693, 2754, 2694, 2694, 2754, 2755, 2754, 2815, 2755, 2755, 2815, 2816, - 2815, 2876, 2816, 2816, 2876, 2877, 2876, 2937, 2877, 2877, 2937, 2938, - 2937, 2998, 2938, 2938, 2998, 2999, 2998, 3059, 2999, 2999, 3059, 3060, - 3059, 3120, 3060, 3060, 3120, 3121, 3120, 3181, 3121, 3121, 3181, 3182, - 3181, 3242, 3182, 3182, 3242, 3243, 3242, 3303, 3243, 3243, 3303, 3304, - 3303, 3364, 3304, 3304, 3364, 3365, 3364, 3425, 3365, 3365, 3425, 3426, - 3425, 3486, 3426, 3426, 3486, 3487, 3486, 3547, 3487, 3487, 3547, 3548, - 3547, 3608, 3548, 3548, 3608, 3609, 3608, 3669, 3609, 3609, 3669, 3670, - 10, 71, 11, 11, 71, 72, 71, 132, 72, 72, 132, 133, - 132, 193, 133, 133, 193, 194, 193, 254, 194, 194, 254, 255, - 254, 315, 255, 255, 315, 316, 315, 376, 316, 316, 376, 377, - 376, 437, 377, 377, 437, 438, 437, 498, 438, 438, 498, 499, - 498, 559, 499, 499, 559, 560, 559, 620, 560, 560, 620, 621, - 620, 681, 621, 621, 681, 682, 681, 742, 682, 682, 742, 743, - 742, 803, 743, 743, 803, 804, 803, 864, 804, 804, 864, 865, - 864, 925, 865, 865, 925, 926, 925, 986, 926, 926, 986, 987, - 986, 1047, 987, 987, 1047, 1048, 1047, 1108, 1048, 1048, 1108, 1109, - 1108, 1169, 1109, 1109, 1169, 1170, 1169, 1230, 1170, 1170, 1230, 1231, - 1230, 1291, 1231, 1231, 1291, 1292, 1291, 1352, 1292, 1292, 1352, 1353, - 1352, 1413, 1353, 1353, 1413, 1414, 1413, 1474, 1414, 1414, 1474, 1475, - 1474, 1535, 1475, 1475, 1535, 1536, 1535, 1596, 1536, 1536, 1596, 1597, - 1596, 1657, 1597, 1597, 1657, 1658, 1657, 1718, 1658, 1658, 1718, 1719, - 1718, 1779, 1719, 1719, 1779, 1780, 1779, 1840, 1780, 1780, 1840, 1841, - 1840, 1901, 1841, 1841, 1901, 1902, 1901, 1962, 1902, 1902, 1962, 1963, - 1962, 2023, 1963, 1963, 2023, 2024, 2023, 2084, 2024, 2024, 2084, 2085, - 2084, 2145, 2085, 2085, 2145, 2146, 2145, 2206, 2146, 2146, 2206, 2207, - 2206, 2267, 2207, 2207, 2267, 2268, 2267, 2328, 2268, 2268, 2328, 2329, - 2328, 2389, 2329, 2329, 2389, 2390, 2389, 2450, 2390, 2390, 2450, 2451, - 2450, 2511, 2451, 2451, 2511, 2512, 2511, 2572, 2512, 2512, 2572, 2573, - 2572, 2633, 2573, 2573, 2633, 2634, 2633, 2694, 2634, 2634, 2694, 2695, - 2694, 2755, 2695, 2695, 2755, 2756, 2755, 2816, 2756, 2756, 2816, 2817, - 2816, 2877, 2817, 2817, 2877, 2878, 2877, 2938, 2878, 2878, 2938, 2939, - 2938, 2999, 2939, 2939, 2999, 3000, 2999, 3060, 3000, 3000, 3060, 3061, - 3060, 3121, 3061, 3061, 3121, 3122, 3121, 3182, 3122, 3122, 3182, 3183, - 3182, 3243, 3183, 3183, 3243, 3244, 3243, 3304, 3244, 3244, 3304, 3305, - 3304, 3365, 3305, 3305, 3365, 3366, 3365, 3426, 3366, 3366, 3426, 3427, - 3426, 3487, 3427, 3427, 3487, 3488, 3487, 3548, 3488, 3488, 3548, 3549, - 3548, 3609, 3549, 3549, 3609, 3610, 3609, 3670, 3610, 3610, 3670, 3671, - 11, 72, 12, 12, 72, 73, 72, 133, 73, 73, 133, 134, - 133, 194, 134, 134, 194, 195, 194, 255, 195, 195, 255, 256, - 255, 316, 256, 256, 316, 317, 316, 377, 317, 317, 377, 378, - 377, 438, 378, 378, 438, 439, 438, 499, 439, 439, 499, 500, - 499, 560, 500, 500, 560, 561, 560, 621, 561, 561, 621, 622, - 621, 682, 622, 622, 682, 683, 682, 743, 683, 683, 743, 744, - 743, 804, 744, 744, 804, 805, 804, 865, 805, 805, 865, 866, - 865, 926, 866, 866, 926, 927, 926, 987, 927, 927, 987, 988, - 987, 1048, 988, 988, 1048, 1049, 1048, 1109, 1049, 1049, 1109, 1110, - 1109, 1170, 1110, 1110, 1170, 1171, 1170, 1231, 1171, 1171, 1231, 1232, - 1231, 1292, 1232, 1232, 1292, 1293, 1292, 1353, 1293, 1293, 1353, 1354, - 1353, 1414, 1354, 1354, 1414, 1415, 1414, 1475, 1415, 1415, 1475, 1476, - 1475, 1536, 1476, 1476, 1536, 1537, 1536, 1597, 1537, 1537, 1597, 1598, - 1597, 1658, 1598, 1598, 1658, 1659, 1658, 1719, 1659, 1659, 1719, 1720, - 1719, 1780, 1720, 1720, 1780, 1781, 1780, 1841, 1781, 1781, 1841, 1842, - 1841, 1902, 1842, 1842, 1902, 1903, 1902, 1963, 1903, 1903, 1963, 1964, - 1963, 2024, 1964, 1964, 2024, 2025, 2024, 2085, 2025, 2025, 2085, 2086, - 2085, 2146, 2086, 2086, 2146, 2147, 2146, 2207, 2147, 2147, 2207, 2208, - 2207, 2268, 2208, 2208, 2268, 2269, 2268, 2329, 2269, 2269, 2329, 2330, - 2329, 2390, 2330, 2330, 2390, 2391, 2390, 2451, 2391, 2391, 2451, 2452, - 2451, 2512, 2452, 2452, 2512, 2513, 2512, 2573, 2513, 2513, 2573, 2574, - 2573, 2634, 2574, 2574, 2634, 2635, 2634, 2695, 2635, 2635, 2695, 2696, - 2695, 2756, 2696, 2696, 2756, 2757, 2756, 2817, 2757, 2757, 2817, 2818, - 2817, 2878, 2818, 2818, 2878, 2879, 2878, 2939, 2879, 2879, 2939, 2940, - 2939, 3000, 2940, 2940, 3000, 3001, 3000, 3061, 3001, 3001, 3061, 3062, - 3061, 3122, 3062, 3062, 3122, 3123, 3122, 3183, 3123, 3123, 3183, 3184, - 3183, 3244, 3184, 3184, 3244, 3245, 3244, 3305, 3245, 3245, 3305, 3306, - 3305, 3366, 3306, 3306, 3366, 3367, 3366, 3427, 3367, 3367, 3427, 3428, - 3427, 3488, 3428, 3428, 3488, 3489, 3488, 3549, 3489, 3489, 3549, 3550, - 3549, 3610, 3550, 3550, 3610, 3611, 3610, 3671, 3611, 3611, 3671, 3672, - 12, 73, 13, 13, 73, 74, 73, 134, 74, 74, 134, 135, - 134, 195, 135, 135, 195, 196, 195, 256, 196, 196, 256, 257, - 256, 317, 257, 257, 317, 318, 317, 378, 318, 318, 378, 379, - 378, 439, 379, 379, 439, 440, 439, 500, 440, 440, 500, 501, - 500, 561, 501, 501, 561, 562, 561, 622, 562, 562, 622, 623, - 622, 683, 623, 623, 683, 684, 683, 744, 684, 684, 744, 745, - 744, 805, 745, 745, 805, 806, 805, 866, 806, 806, 866, 867, - 866, 927, 867, 867, 927, 928, 927, 988, 928, 928, 988, 989, - 988, 1049, 989, 989, 1049, 1050, 1049, 1110, 1050, 1050, 1110, 1111, - 1110, 1171, 1111, 1111, 1171, 1172, 1171, 1232, 1172, 1172, 1232, 1233, - 1232, 1293, 1233, 1233, 1293, 1294, 1293, 1354, 1294, 1294, 1354, 1355, - 1354, 1415, 1355, 1355, 1415, 1416, 1415, 1476, 1416, 1416, 1476, 1477, - 1476, 1537, 1477, 1477, 1537, 1538, 1537, 1598, 1538, 1538, 1598, 1599, - 1598, 1659, 1599, 1599, 1659, 1660, 1659, 1720, 1660, 1660, 1720, 1721, - 1720, 1781, 1721, 1721, 1781, 1782, 1781, 1842, 1782, 1782, 1842, 1843, - 1842, 1903, 1843, 1843, 1903, 1904, 1903, 1964, 1904, 1904, 1964, 1965, - 1964, 2025, 1965, 1965, 2025, 2026, 2025, 2086, 2026, 2026, 2086, 2087, - 2086, 2147, 2087, 2087, 2147, 2148, 2147, 2208, 2148, 2148, 2208, 2209, - 2208, 2269, 2209, 2209, 2269, 2270, 2269, 2330, 2270, 2270, 2330, 2331, - 2330, 2391, 2331, 2331, 2391, 2392, 2391, 2452, 2392, 2392, 2452, 2453, - 2452, 2513, 2453, 2453, 2513, 2514, 2513, 2574, 2514, 2514, 2574, 2575, - 2574, 2635, 2575, 2575, 2635, 2636, 2635, 2696, 2636, 2636, 2696, 2697, - 2696, 2757, 2697, 2697, 2757, 2758, 2757, 2818, 2758, 2758, 2818, 2819, - 2818, 2879, 2819, 2819, 2879, 2880, 2879, 2940, 2880, 2880, 2940, 2941, - 2940, 3001, 2941, 2941, 3001, 3002, 3001, 3062, 3002, 3002, 3062, 3063, - 3062, 3123, 3063, 3063, 3123, 3124, 3123, 3184, 3124, 3124, 3184, 3185, - 3184, 3245, 3185, 3185, 3245, 3246, 3245, 3306, 3246, 3246, 3306, 3307, - 3306, 3367, 3307, 3307, 3367, 3368, 3367, 3428, 3368, 3368, 3428, 3429, - 3428, 3489, 3429, 3429, 3489, 3490, 3489, 3550, 3490, 3490, 3550, 3551, - 3550, 3611, 3551, 3551, 3611, 3612, 3611, 3672, 3612, 3612, 3672, 3673, - 13, 74, 14, 14, 74, 75, 74, 135, 75, 75, 135, 136, - 135, 196, 136, 136, 196, 197, 196, 257, 197, 197, 257, 258, - 257, 318, 258, 258, 318, 319, 318, 379, 319, 319, 379, 380, - 379, 440, 380, 380, 440, 441, 440, 501, 441, 441, 501, 502, - 501, 562, 502, 502, 562, 563, 562, 623, 563, 563, 623, 624, - 623, 684, 624, 624, 684, 685, 684, 745, 685, 685, 745, 746, - 745, 806, 746, 746, 806, 807, 806, 867, 807, 807, 867, 868, - 867, 928, 868, 868, 928, 929, 928, 989, 929, 929, 989, 990, - 989, 1050, 990, 990, 1050, 1051, 1050, 1111, 1051, 1051, 1111, 1112, - 1111, 1172, 1112, 1112, 1172, 1173, 1172, 1233, 1173, 1173, 1233, 1234, - 1233, 1294, 1234, 1234, 1294, 1295, 1294, 1355, 1295, 1295, 1355, 1356, - 1355, 1416, 1356, 1356, 1416, 1417, 1416, 1477, 1417, 1417, 1477, 1478, - 1477, 1538, 1478, 1478, 1538, 1539, 1538, 1599, 1539, 1539, 1599, 1600, - 1599, 1660, 1600, 1600, 1660, 1661, 1660, 1721, 1661, 1661, 1721, 1722, - 1721, 1782, 1722, 1722, 1782, 1783, 1782, 1843, 1783, 1783, 1843, 1844, - 1843, 1904, 1844, 1844, 1904, 1905, 1904, 1965, 1905, 1905, 1965, 1966, - 1965, 2026, 1966, 1966, 2026, 2027, 2026, 2087, 2027, 2027, 2087, 2088, - 2087, 2148, 2088, 2088, 2148, 2149, 2148, 2209, 2149, 2149, 2209, 2210, - 2209, 2270, 2210, 2210, 2270, 2271, 2270, 2331, 2271, 2271, 2331, 2332, - 2331, 2392, 2332, 2332, 2392, 2393, 2392, 2453, 2393, 2393, 2453, 2454, - 2453, 2514, 2454, 2454, 2514, 2515, 2514, 2575, 2515, 2515, 2575, 2576, - 2575, 2636, 2576, 2576, 2636, 2637, 2636, 2697, 2637, 2637, 2697, 2698, - 2697, 2758, 2698, 2698, 2758, 2759, 2758, 2819, 2759, 2759, 2819, 2820, - 2819, 2880, 2820, 2820, 2880, 2881, 2880, 2941, 2881, 2881, 2941, 2942, - 2941, 3002, 2942, 2942, 3002, 3003, 3002, 3063, 3003, 3003, 3063, 3064, - 3063, 3124, 3064, 3064, 3124, 3125, 3124, 3185, 3125, 3125, 3185, 3186, - 3185, 3246, 3186, 3186, 3246, 3247, 3246, 3307, 3247, 3247, 3307, 3308, - 3307, 3368, 3308, 3308, 3368, 3369, 3368, 3429, 3369, 3369, 3429, 3430, - 3429, 3490, 3430, 3430, 3490, 3491, 3490, 3551, 3491, 3491, 3551, 3552, - 3551, 3612, 3552, 3552, 3612, 3613, 3612, 3673, 3613, 3613, 3673, 3674, - 14, 75, 15, 15, 75, 76, 75, 136, 76, 76, 136, 137, - 136, 197, 137, 137, 197, 198, 197, 258, 198, 198, 258, 259, - 258, 319, 259, 259, 319, 320, 319, 380, 320, 320, 380, 381, - 380, 441, 381, 381, 441, 442, 441, 502, 442, 442, 502, 503, - 502, 563, 503, 503, 563, 564, 563, 624, 564, 564, 624, 625, - 624, 685, 625, 625, 685, 686, 685, 746, 686, 686, 746, 747, - 746, 807, 747, 747, 807, 808, 807, 868, 808, 808, 868, 869, - 868, 929, 869, 869, 929, 930, 929, 990, 930, 930, 990, 991, - 990, 1051, 991, 991, 1051, 1052, 1051, 1112, 1052, 1052, 1112, 1113, - 1112, 1173, 1113, 1113, 1173, 1174, 1173, 1234, 1174, 1174, 1234, 1235, - 1234, 1295, 1235, 1235, 1295, 1296, 1295, 1356, 1296, 1296, 1356, 1357, - 1356, 1417, 1357, 1357, 1417, 1418, 1417, 1478, 1418, 1418, 1478, 1479, - 1478, 1539, 1479, 1479, 1539, 1540, 1539, 1600, 1540, 1540, 1600, 1601, - 1600, 1661, 1601, 1601, 1661, 1662, 1661, 1722, 1662, 1662, 1722, 1723, - 1722, 1783, 1723, 1723, 1783, 1784, 1783, 1844, 1784, 1784, 1844, 1845, - 1844, 1905, 1845, 1845, 1905, 1906, 1905, 1966, 1906, 1906, 1966, 1967, - 1966, 2027, 1967, 1967, 2027, 2028, 2027, 2088, 2028, 2028, 2088, 2089, - 2088, 2149, 2089, 2089, 2149, 2150, 2149, 2210, 2150, 2150, 2210, 2211, - 2210, 2271, 2211, 2211, 2271, 2272, 2271, 2332, 2272, 2272, 2332, 2333, - 2332, 2393, 2333, 2333, 2393, 2394, 2393, 2454, 2394, 2394, 2454, 2455, - 2454, 2515, 2455, 2455, 2515, 2516, 2515, 2576, 2516, 2516, 2576, 2577, - 2576, 2637, 2577, 2577, 2637, 2638, 2637, 2698, 2638, 2638, 2698, 2699, - 2698, 2759, 2699, 2699, 2759, 2760, 2759, 2820, 2760, 2760, 2820, 2821, - 2820, 2881, 2821, 2821, 2881, 2882, 2881, 2942, 2882, 2882, 2942, 2943, - 2942, 3003, 2943, 2943, 3003, 3004, 3003, 3064, 3004, 3004, 3064, 3065, - 3064, 3125, 3065, 3065, 3125, 3126, 3125, 3186, 3126, 3126, 3186, 3187, - 3186, 3247, 3187, 3187, 3247, 3248, 3247, 3308, 3248, 3248, 3308, 3309, - 3308, 3369, 3309, 3309, 3369, 3370, 3369, 3430, 3370, 3370, 3430, 3431, - 3430, 3491, 3431, 3431, 3491, 3492, 3491, 3552, 3492, 3492, 3552, 3553, - 3552, 3613, 3553, 3553, 3613, 3614, 3613, 3674, 3614, 3614, 3674, 3675, - 15, 76, 16, 16, 76, 77, 76, 137, 77, 77, 137, 138, - 137, 198, 138, 138, 198, 199, 198, 259, 199, 199, 259, 260, - 259, 320, 260, 260, 320, 321, 320, 381, 321, 321, 381, 382, - 381, 442, 382, 382, 442, 443, 442, 503, 443, 443, 503, 504, - 503, 564, 504, 504, 564, 565, 564, 625, 565, 565, 625, 626, - 625, 686, 626, 626, 686, 687, 686, 747, 687, 687, 747, 748, - 747, 808, 748, 748, 808, 809, 808, 869, 809, 809, 869, 870, - 869, 930, 870, 870, 930, 931, 930, 991, 931, 931, 991, 992, - 991, 1052, 992, 992, 1052, 1053, 1052, 1113, 1053, 1053, 1113, 1114, - 1113, 1174, 1114, 1114, 1174, 1175, 1174, 1235, 1175, 1175, 1235, 1236, - 1235, 1296, 1236, 1236, 1296, 1297, 1296, 1357, 1297, 1297, 1357, 1358, - 1357, 1418, 1358, 1358, 1418, 1419, 1418, 1479, 1419, 1419, 1479, 1480, - 1479, 1540, 1480, 1480, 1540, 1541, 1540, 1601, 1541, 1541, 1601, 1602, - 1601, 1662, 1602, 1602, 1662, 1663, 1662, 1723, 1663, 1663, 1723, 1724, - 1723, 1784, 1724, 1724, 1784, 1785, 1784, 1845, 1785, 1785, 1845, 1846, - 1845, 1906, 1846, 1846, 1906, 1907, 1906, 1967, 1907, 1907, 1967, 1968, - 1967, 2028, 1968, 1968, 2028, 2029, 2028, 2089, 2029, 2029, 2089, 2090, - 2089, 2150, 2090, 2090, 2150, 2151, 2150, 2211, 2151, 2151, 2211, 2212, - 2211, 2272, 2212, 2212, 2272, 2273, 2272, 2333, 2273, 2273, 2333, 2334, - 2333, 2394, 2334, 2334, 2394, 2395, 2394, 2455, 2395, 2395, 2455, 2456, - 2455, 2516, 2456, 2456, 2516, 2517, 2516, 2577, 2517, 2517, 2577, 2578, - 2577, 2638, 2578, 2578, 2638, 2639, 2638, 2699, 2639, 2639, 2699, 2700, - 2699, 2760, 2700, 2700, 2760, 2761, 2760, 2821, 2761, 2761, 2821, 2822, - 2821, 2882, 2822, 2822, 2882, 2883, 2882, 2943, 2883, 2883, 2943, 2944, - 2943, 3004, 2944, 2944, 3004, 3005, 3004, 3065, 3005, 3005, 3065, 3066, - 3065, 3126, 3066, 3066, 3126, 3127, 3126, 3187, 3127, 3127, 3187, 3188, - 3187, 3248, 3188, 3188, 3248, 3249, 3248, 3309, 3249, 3249, 3309, 3310, - 3309, 3370, 3310, 3310, 3370, 3371, 3370, 3431, 3371, 3371, 3431, 3432, - 3431, 3492, 3432, 3432, 3492, 3493, 3492, 3553, 3493, 3493, 3553, 3554, - 3553, 3614, 3554, 3554, 3614, 3615, 3614, 3675, 3615, 3615, 3675, 3676, - 16, 77, 17, 17, 77, 78, 77, 138, 78, 78, 138, 139, - 138, 199, 139, 139, 199, 200, 199, 260, 200, 200, 260, 261, - 260, 321, 261, 261, 321, 322, 321, 382, 322, 322, 382, 383, - 382, 443, 383, 383, 443, 444, 443, 504, 444, 444, 504, 505, - 504, 565, 505, 505, 565, 566, 565, 626, 566, 566, 626, 627, - 626, 687, 627, 627, 687, 688, 687, 748, 688, 688, 748, 749, - 748, 809, 749, 749, 809, 810, 809, 870, 810, 810, 870, 871, - 870, 931, 871, 871, 931, 932, 931, 992, 932, 932, 992, 993, - 992, 1053, 993, 993, 1053, 1054, 1053, 1114, 1054, 1054, 1114, 1115, - 1114, 1175, 1115, 1115, 1175, 1176, 1175, 1236, 1176, 1176, 1236, 1237, - 1236, 1297, 1237, 1237, 1297, 1298, 1297, 1358, 1298, 1298, 1358, 1359, - 1358, 1419, 1359, 1359, 1419, 1420, 1419, 1480, 1420, 1420, 1480, 1481, - 1480, 1541, 1481, 1481, 1541, 1542, 1541, 1602, 1542, 1542, 1602, 1603, - 1602, 1663, 1603, 1603, 1663, 1664, 1663, 1724, 1664, 1664, 1724, 1725, - 1724, 1785, 1725, 1725, 1785, 1786, 1785, 1846, 1786, 1786, 1846, 1847, - 1846, 1907, 1847, 1847, 1907, 1908, 1907, 1968, 1908, 1908, 1968, 1969, - 1968, 2029, 1969, 1969, 2029, 2030, 2029, 2090, 2030, 2030, 2090, 2091, - 2090, 2151, 2091, 2091, 2151, 2152, 2151, 2212, 2152, 2152, 2212, 2213, - 2212, 2273, 2213, 2213, 2273, 2274, 2273, 2334, 2274, 2274, 2334, 2335, - 2334, 2395, 2335, 2335, 2395, 2396, 2395, 2456, 2396, 2396, 2456, 2457, - 2456, 2517, 2457, 2457, 2517, 2518, 2517, 2578, 2518, 2518, 2578, 2579, - 2578, 2639, 2579, 2579, 2639, 2640, 2639, 2700, 2640, 2640, 2700, 2701, - 2700, 2761, 2701, 2701, 2761, 2762, 2761, 2822, 2762, 2762, 2822, 2823, - 2822, 2883, 2823, 2823, 2883, 2884, 2883, 2944, 2884, 2884, 2944, 2945, - 2944, 3005, 2945, 2945, 3005, 3006, 3005, 3066, 3006, 3006, 3066, 3067, - 3066, 3127, 3067, 3067, 3127, 3128, 3127, 3188, 3128, 3128, 3188, 3189, - 3188, 3249, 3189, 3189, 3249, 3250, 3249, 3310, 3250, 3250, 3310, 3311, - 3310, 3371, 3311, 3311, 3371, 3372, 3371, 3432, 3372, 3372, 3432, 3433, - 3432, 3493, 3433, 3433, 3493, 3494, 3493, 3554, 3494, 3494, 3554, 3555, - 3554, 3615, 3555, 3555, 3615, 3616, 3615, 3676, 3616, 3616, 3676, 3677, - 17, 78, 18, 18, 78, 79, 78, 139, 79, 79, 139, 140, - 139, 200, 140, 140, 200, 201, 200, 261, 201, 201, 261, 262, - 261, 322, 262, 262, 322, 323, 322, 383, 323, 323, 383, 384, - 383, 444, 384, 384, 444, 445, 444, 505, 445, 445, 505, 506, - 505, 566, 506, 506, 566, 567, 566, 627, 567, 567, 627, 628, - 627, 688, 628, 628, 688, 689, 688, 749, 689, 689, 749, 750, - 749, 810, 750, 750, 810, 811, 810, 871, 811, 811, 871, 872, - 871, 932, 872, 872, 932, 933, 932, 993, 933, 933, 993, 994, - 993, 1054, 994, 994, 1054, 1055, 1054, 1115, 1055, 1055, 1115, 1116, - 1115, 1176, 1116, 1116, 1176, 1177, 1176, 1237, 1177, 1177, 1237, 1238, - 1237, 1298, 1238, 1238, 1298, 1299, 1298, 1359, 1299, 1299, 1359, 1360, - 1359, 1420, 1360, 1360, 1420, 1421, 1420, 1481, 1421, 1421, 1481, 1482, - 1481, 1542, 1482, 1482, 1542, 1543, 1542, 1603, 1543, 1543, 1603, 1604, - 1603, 1664, 1604, 1604, 1664, 1665, 1664, 1725, 1665, 1665, 1725, 1726, - 1725, 1786, 1726, 1726, 1786, 1787, 1786, 1847, 1787, 1787, 1847, 1848, - 1847, 1908, 1848, 1848, 1908, 1909, 1908, 1969, 1909, 1909, 1969, 1970, - 1969, 2030, 1970, 1970, 2030, 2031, 2030, 2091, 2031, 2031, 2091, 2092, - 2091, 2152, 2092, 2092, 2152, 2153, 2152, 2213, 2153, 2153, 2213, 2214, - 2213, 2274, 2214, 2214, 2274, 2275, 2274, 2335, 2275, 2275, 2335, 2336, - 2335, 2396, 2336, 2336, 2396, 2397, 2396, 2457, 2397, 2397, 2457, 2458, - 2457, 2518, 2458, 2458, 2518, 2519, 2518, 2579, 2519, 2519, 2579, 2580, - 2579, 2640, 2580, 2580, 2640, 2641, 2640, 2701, 2641, 2641, 2701, 2702, - 2701, 2762, 2702, 2702, 2762, 2763, 2762, 2823, 2763, 2763, 2823, 2824, - 2823, 2884, 2824, 2824, 2884, 2885, 2884, 2945, 2885, 2885, 2945, 2946, - 2945, 3006, 2946, 2946, 3006, 3007, 3006, 3067, 3007, 3007, 3067, 3068, - 3067, 3128, 3068, 3068, 3128, 3129, 3128, 3189, 3129, 3129, 3189, 3190, - 3189, 3250, 3190, 3190, 3250, 3251, 3250, 3311, 3251, 3251, 3311, 3312, - 3311, 3372, 3312, 3312, 3372, 3373, 3372, 3433, 3373, 3373, 3433, 3434, - 3433, 3494, 3434, 3434, 3494, 3495, 3494, 3555, 3495, 3495, 3555, 3556, - 3555, 3616, 3556, 3556, 3616, 3617, 3616, 3677, 3617, 3617, 3677, 3678, - 18, 79, 19, 19, 79, 80, 79, 140, 80, 80, 140, 141, - 140, 201, 141, 141, 201, 202, 201, 262, 202, 202, 262, 263, - 262, 323, 263, 263, 323, 324, 323, 384, 324, 324, 384, 385, - 384, 445, 385, 385, 445, 446, 445, 506, 446, 446, 506, 507, - 506, 567, 507, 507, 567, 568, 567, 628, 568, 568, 628, 629, - 628, 689, 629, 629, 689, 690, 689, 750, 690, 690, 750, 751, - 750, 811, 751, 751, 811, 812, 811, 872, 812, 812, 872, 873, - 872, 933, 873, 873, 933, 934, 933, 994, 934, 934, 994, 995, - 994, 1055, 995, 995, 1055, 1056, 1055, 1116, 1056, 1056, 1116, 1117, - 1116, 1177, 1117, 1117, 1177, 1178, 1177, 1238, 1178, 1178, 1238, 1239, - 1238, 1299, 1239, 1239, 1299, 1300, 1299, 1360, 1300, 1300, 1360, 1361, - 1360, 1421, 1361, 1361, 1421, 1422, 1421, 1482, 1422, 1422, 1482, 1483, - 1482, 1543, 1483, 1483, 1543, 1544, 1543, 1604, 1544, 1544, 1604, 1605, - 1604, 1665, 1605, 1605, 1665, 1666, 1665, 1726, 1666, 1666, 1726, 1727, - 1726, 1787, 1727, 1727, 1787, 1788, 1787, 1848, 1788, 1788, 1848, 1849, - 1848, 1909, 1849, 1849, 1909, 1910, 1909, 1970, 1910, 1910, 1970, 1971, - 1970, 2031, 1971, 1971, 2031, 2032, 2031, 2092, 2032, 2032, 2092, 2093, - 2092, 2153, 2093, 2093, 2153, 2154, 2153, 2214, 2154, 2154, 2214, 2215, - 2214, 2275, 2215, 2215, 2275, 2276, 2275, 2336, 2276, 2276, 2336, 2337, - 2336, 2397, 2337, 2337, 2397, 2398, 2397, 2458, 2398, 2398, 2458, 2459, - 2458, 2519, 2459, 2459, 2519, 2520, 2519, 2580, 2520, 2520, 2580, 2581, - 2580, 2641, 2581, 2581, 2641, 2642, 2641, 2702, 2642, 2642, 2702, 2703, - 2702, 2763, 2703, 2703, 2763, 2764, 2763, 2824, 2764, 2764, 2824, 2825, - 2824, 2885, 2825, 2825, 2885, 2886, 2885, 2946, 2886, 2886, 2946, 2947, - 2946, 3007, 2947, 2947, 3007, 3008, 3007, 3068, 3008, 3008, 3068, 3069, - 3068, 3129, 3069, 3069, 3129, 3130, 3129, 3190, 3130, 3130, 3190, 3191, - 3190, 3251, 3191, 3191, 3251, 3252, 3251, 3312, 3252, 3252, 3312, 3313, - 3312, 3373, 3313, 3313, 3373, 3374, 3373, 3434, 3374, 3374, 3434, 3435, - 3434, 3495, 3435, 3435, 3495, 3496, 3495, 3556, 3496, 3496, 3556, 3557, - 3556, 3617, 3557, 3557, 3617, 3618, 3617, 3678, 3618, 3618, 3678, 3679, - 19, 80, 20, 20, 80, 81, 80, 141, 81, 81, 141, 142, - 141, 202, 142, 142, 202, 203, 202, 263, 203, 203, 263, 264, - 263, 324, 264, 264, 324, 325, 324, 385, 325, 325, 385, 386, - 385, 446, 386, 386, 446, 447, 446, 507, 447, 447, 507, 508, - 507, 568, 508, 508, 568, 569, 568, 629, 569, 569, 629, 630, - 629, 690, 630, 630, 690, 691, 690, 751, 691, 691, 751, 752, - 751, 812, 752, 752, 812, 813, 812, 873, 813, 813, 873, 874, - 873, 934, 874, 874, 934, 935, 934, 995, 935, 935, 995, 996, - 995, 1056, 996, 996, 1056, 1057, 1056, 1117, 1057, 1057, 1117, 1118, - 1117, 1178, 1118, 1118, 1178, 1179, 1178, 1239, 1179, 1179, 1239, 1240, - 1239, 1300, 1240, 1240, 1300, 1301, 1300, 1361, 1301, 1301, 1361, 1362, - 1361, 1422, 1362, 1362, 1422, 1423, 1422, 1483, 1423, 1423, 1483, 1484, - 1483, 1544, 1484, 1484, 1544, 1545, 1544, 1605, 1545, 1545, 1605, 1606, - 1605, 1666, 1606, 1606, 1666, 1667, 1666, 1727, 1667, 1667, 1727, 1728, - 1727, 1788, 1728, 1728, 1788, 1789, 1788, 1849, 1789, 1789, 1849, 1850, - 1849, 1910, 1850, 1850, 1910, 1911, 1910, 1971, 1911, 1911, 1971, 1972, - 1971, 2032, 1972, 1972, 2032, 2033, 2032, 2093, 2033, 2033, 2093, 2094, - 2093, 2154, 2094, 2094, 2154, 2155, 2154, 2215, 2155, 2155, 2215, 2216, - 2215, 2276, 2216, 2216, 2276, 2277, 2276, 2337, 2277, 2277, 2337, 2338, - 2337, 2398, 2338, 2338, 2398, 2399, 2398, 2459, 2399, 2399, 2459, 2460, - 2459, 2520, 2460, 2460, 2520, 2521, 2520, 2581, 2521, 2521, 2581, 2582, - 2581, 2642, 2582, 2582, 2642, 2643, 2642, 2703, 2643, 2643, 2703, 2704, - 2703, 2764, 2704, 2704, 2764, 2765, 2764, 2825, 2765, 2765, 2825, 2826, - 2825, 2886, 2826, 2826, 2886, 2887, 2886, 2947, 2887, 2887, 2947, 2948, - 2947, 3008, 2948, 2948, 3008, 3009, 3008, 3069, 3009, 3009, 3069, 3070, - 3069, 3130, 3070, 3070, 3130, 3131, 3130, 3191, 3131, 3131, 3191, 3192, - 3191, 3252, 3192, 3192, 3252, 3253, 3252, 3313, 3253, 3253, 3313, 3314, - 3313, 3374, 3314, 3314, 3374, 3375, 3374, 3435, 3375, 3375, 3435, 3436, - 3435, 3496, 3436, 3436, 3496, 3497, 3496, 3557, 3497, 3497, 3557, 3558, - 3557, 3618, 3558, 3558, 3618, 3619, 3618, 3679, 3619, 3619, 3679, 3680, - 20, 81, 21, 21, 81, 82, 81, 142, 82, 82, 142, 143, - 142, 203, 143, 143, 203, 204, 203, 264, 204, 204, 264, 265, - 264, 325, 265, 265, 325, 326, 325, 386, 326, 326, 386, 387, - 386, 447, 387, 387, 447, 448, 447, 508, 448, 448, 508, 509, - 508, 569, 509, 509, 569, 570, 569, 630, 570, 570, 630, 631, - 630, 691, 631, 631, 691, 692, 691, 752, 692, 692, 752, 753, - 752, 813, 753, 753, 813, 814, 813, 874, 814, 814, 874, 875, - 874, 935, 875, 875, 935, 936, 935, 996, 936, 936, 996, 997, - 996, 1057, 997, 997, 1057, 1058, 1057, 1118, 1058, 1058, 1118, 1119, - 1118, 1179, 1119, 1119, 1179, 1180, 1179, 1240, 1180, 1180, 1240, 1241, - 1240, 1301, 1241, 1241, 1301, 1302, 1301, 1362, 1302, 1302, 1362, 1363, - 1362, 1423, 1363, 1363, 1423, 1424, 1423, 1484, 1424, 1424, 1484, 1485, - 1484, 1545, 1485, 1485, 1545, 1546, 1545, 1606, 1546, 1546, 1606, 1607, - 1606, 1667, 1607, 1607, 1667, 1668, 1667, 1728, 1668, 1668, 1728, 1729, - 1728, 1789, 1729, 1729, 1789, 1790, 1789, 1850, 1790, 1790, 1850, 1851, - 1850, 1911, 1851, 1851, 1911, 1912, 1911, 1972, 1912, 1912, 1972, 1973, - 1972, 2033, 1973, 1973, 2033, 2034, 2033, 2094, 2034, 2034, 2094, 2095, - 2094, 2155, 2095, 2095, 2155, 2156, 2155, 2216, 2156, 2156, 2216, 2217, - 2216, 2277, 2217, 2217, 2277, 2278, 2277, 2338, 2278, 2278, 2338, 2339, - 2338, 2399, 2339, 2339, 2399, 2400, 2399, 2460, 2400, 2400, 2460, 2461, - 2460, 2521, 2461, 2461, 2521, 2522, 2521, 2582, 2522, 2522, 2582, 2583, - 2582, 2643, 2583, 2583, 2643, 2644, 2643, 2704, 2644, 2644, 2704, 2705, - 2704, 2765, 2705, 2705, 2765, 2766, 2765, 2826, 2766, 2766, 2826, 2827, - 2826, 2887, 2827, 2827, 2887, 2888, 2887, 2948, 2888, 2888, 2948, 2949, - 2948, 3009, 2949, 2949, 3009, 3010, 3009, 3070, 3010, 3010, 3070, 3071, - 3070, 3131, 3071, 3071, 3131, 3132, 3131, 3192, 3132, 3132, 3192, 3193, - 3192, 3253, 3193, 3193, 3253, 3254, 3253, 3314, 3254, 3254, 3314, 3315, - 3314, 3375, 3315, 3315, 3375, 3376, 3375, 3436, 3376, 3376, 3436, 3437, - 3436, 3497, 3437, 3437, 3497, 3498, 3497, 3558, 3498, 3498, 3558, 3559, - 3558, 3619, 3559, 3559, 3619, 3620, 3619, 3680, 3620, 3620, 3680, 3681, - 21, 82, 22, 22, 82, 83, 82, 143, 83, 83, 143, 144, - 143, 204, 144, 144, 204, 205, 204, 265, 205, 205, 265, 266, - 265, 326, 266, 266, 326, 327, 326, 387, 327, 327, 387, 388, - 387, 448, 388, 388, 448, 449, 448, 509, 449, 449, 509, 510, - 509, 570, 510, 510, 570, 571, 570, 631, 571, 571, 631, 632, - 631, 692, 632, 632, 692, 693, 692, 753, 693, 693, 753, 754, - 753, 814, 754, 754, 814, 815, 814, 875, 815, 815, 875, 876, - 875, 936, 876, 876, 936, 937, 936, 997, 937, 937, 997, 998, - 997, 1058, 998, 998, 1058, 1059, 1058, 1119, 1059, 1059, 1119, 1120, - 1119, 1180, 1120, 1120, 1180, 1181, 1180, 1241, 1181, 1181, 1241, 1242, - 1241, 1302, 1242, 1242, 1302, 1303, 1302, 1363, 1303, 1303, 1363, 1364, - 1363, 1424, 1364, 1364, 1424, 1425, 1424, 1485, 1425, 1425, 1485, 1486, - 1485, 1546, 1486, 1486, 1546, 1547, 1546, 1607, 1547, 1547, 1607, 1608, - 1607, 1668, 1608, 1608, 1668, 1669, 1668, 1729, 1669, 1669, 1729, 1730, - 1729, 1790, 1730, 1730, 1790, 1791, 1790, 1851, 1791, 1791, 1851, 1852, - 1851, 1912, 1852, 1852, 1912, 1913, 1912, 1973, 1913, 1913, 1973, 1974, - 1973, 2034, 1974, 1974, 2034, 2035, 2034, 2095, 2035, 2035, 2095, 2096, - 2095, 2156, 2096, 2096, 2156, 2157, 2156, 2217, 2157, 2157, 2217, 2218, - 2217, 2278, 2218, 2218, 2278, 2279, 2278, 2339, 2279, 2279, 2339, 2340, - 2339, 2400, 2340, 2340, 2400, 2401, 2400, 2461, 2401, 2401, 2461, 2462, - 2461, 2522, 2462, 2462, 2522, 2523, 2522, 2583, 2523, 2523, 2583, 2584, - 2583, 2644, 2584, 2584, 2644, 2645, 2644, 2705, 2645, 2645, 2705, 2706, - 2705, 2766, 2706, 2706, 2766, 2767, 2766, 2827, 2767, 2767, 2827, 2828, - 2827, 2888, 2828, 2828, 2888, 2889, 2888, 2949, 2889, 2889, 2949, 2950, - 2949, 3010, 2950, 2950, 3010, 3011, 3010, 3071, 3011, 3011, 3071, 3072, - 3071, 3132, 3072, 3072, 3132, 3133, 3132, 3193, 3133, 3133, 3193, 3194, - 3193, 3254, 3194, 3194, 3254, 3255, 3254, 3315, 3255, 3255, 3315, 3316, - 3315, 3376, 3316, 3316, 3376, 3377, 3376, 3437, 3377, 3377, 3437, 3438, - 3437, 3498, 3438, 3438, 3498, 3499, 3498, 3559, 3499, 3499, 3559, 3560, - 3559, 3620, 3560, 3560, 3620, 3621, 3620, 3681, 3621, 3621, 3681, 3682, - 22, 83, 23, 23, 83, 84, 83, 144, 84, 84, 144, 145, - 144, 205, 145, 145, 205, 206, 205, 266, 206, 206, 266, 267, - 266, 327, 267, 267, 327, 328, 327, 388, 328, 328, 388, 389, - 388, 449, 389, 389, 449, 450, 449, 510, 450, 450, 510, 511, - 510, 571, 511, 511, 571, 572, 571, 632, 572, 572, 632, 633, - 632, 693, 633, 633, 693, 694, 693, 754, 694, 694, 754, 755, - 754, 815, 755, 755, 815, 816, 815, 876, 816, 816, 876, 877, - 876, 937, 877, 877, 937, 938, 937, 998, 938, 938, 998, 999, - 998, 1059, 999, 999, 1059, 1060, 1059, 1120, 1060, 1060, 1120, 1121, - 1120, 1181, 1121, 1121, 1181, 1182, 1181, 1242, 1182, 1182, 1242, 1243, - 1242, 1303, 1243, 1243, 1303, 1304, 1303, 1364, 1304, 1304, 1364, 1365, - 1364, 1425, 1365, 1365, 1425, 1426, 1425, 1486, 1426, 1426, 1486, 1487, - 1486, 1547, 1487, 1487, 1547, 1548, 1547, 1608, 1548, 1548, 1608, 1609, - 1608, 1669, 1609, 1609, 1669, 1670, 1669, 1730, 1670, 1670, 1730, 1731, - 1730, 1791, 1731, 1731, 1791, 1792, 1791, 1852, 1792, 1792, 1852, 1853, - 1852, 1913, 1853, 1853, 1913, 1914, 1913, 1974, 1914, 1914, 1974, 1975, - 1974, 2035, 1975, 1975, 2035, 2036, 2035, 2096, 2036, 2036, 2096, 2097, - 2096, 2157, 2097, 2097, 2157, 2158, 2157, 2218, 2158, 2158, 2218, 2219, - 2218, 2279, 2219, 2219, 2279, 2280, 2279, 2340, 2280, 2280, 2340, 2341, - 2340, 2401, 2341, 2341, 2401, 2402, 2401, 2462, 2402, 2402, 2462, 2463, - 2462, 2523, 2463, 2463, 2523, 2524, 2523, 2584, 2524, 2524, 2584, 2585, - 2584, 2645, 2585, 2585, 2645, 2646, 2645, 2706, 2646, 2646, 2706, 2707, - 2706, 2767, 2707, 2707, 2767, 2768, 2767, 2828, 2768, 2768, 2828, 2829, - 2828, 2889, 2829, 2829, 2889, 2890, 2889, 2950, 2890, 2890, 2950, 2951, - 2950, 3011, 2951, 2951, 3011, 3012, 3011, 3072, 3012, 3012, 3072, 3073, - 3072, 3133, 3073, 3073, 3133, 3134, 3133, 3194, 3134, 3134, 3194, 3195, - 3194, 3255, 3195, 3195, 3255, 3256, 3255, 3316, 3256, 3256, 3316, 3317, - 3316, 3377, 3317, 3317, 3377, 3378, 3377, 3438, 3378, 3378, 3438, 3439, - 3438, 3499, 3439, 3439, 3499, 3500, 3499, 3560, 3500, 3500, 3560, 3561, - 3560, 3621, 3561, 3561, 3621, 3622, 3621, 3682, 3622, 3622, 3682, 3683, - 23, 84, 24, 24, 84, 85, 84, 145, 85, 85, 145, 146, - 145, 206, 146, 146, 206, 207, 206, 267, 207, 207, 267, 268, - 267, 328, 268, 268, 328, 329, 328, 389, 329, 329, 389, 390, - 389, 450, 390, 390, 450, 451, 450, 511, 451, 451, 511, 512, - 511, 572, 512, 512, 572, 573, 572, 633, 573, 573, 633, 634, - 633, 694, 634, 634, 694, 695, 694, 755, 695, 695, 755, 756, - 755, 816, 756, 756, 816, 817, 816, 877, 817, 817, 877, 878, - 877, 938, 878, 878, 938, 939, 938, 999, 939, 939, 999, 1000, - 999, 1060, 1000, 1000, 1060, 1061, 1060, 1121, 1061, 1061, 1121, 1122, - 1121, 1182, 1122, 1122, 1182, 1183, 1182, 1243, 1183, 1183, 1243, 1244, - 1243, 1304, 1244, 1244, 1304, 1305, 1304, 1365, 1305, 1305, 1365, 1366, - 1365, 1426, 1366, 1366, 1426, 1427, 1426, 1487, 1427, 1427, 1487, 1488, - 1487, 1548, 1488, 1488, 1548, 1549, 1548, 1609, 1549, 1549, 1609, 1610, - 1609, 1670, 1610, 1610, 1670, 1671, 1670, 1731, 1671, 1671, 1731, 1732, - 1731, 1792, 1732, 1732, 1792, 1793, 1792, 1853, 1793, 1793, 1853, 1854, - 1853, 1914, 1854, 1854, 1914, 1915, 1914, 1975, 1915, 1915, 1975, 1976, - 1975, 2036, 1976, 1976, 2036, 2037, 2036, 2097, 2037, 2037, 2097, 2098, - 2097, 2158, 2098, 2098, 2158, 2159, 2158, 2219, 2159, 2159, 2219, 2220, - 2219, 2280, 2220, 2220, 2280, 2281, 2280, 2341, 2281, 2281, 2341, 2342, - 2341, 2402, 2342, 2342, 2402, 2403, 2402, 2463, 2403, 2403, 2463, 2464, - 2463, 2524, 2464, 2464, 2524, 2525, 2524, 2585, 2525, 2525, 2585, 2586, - 2585, 2646, 2586, 2586, 2646, 2647, 2646, 2707, 2647, 2647, 2707, 2708, - 2707, 2768, 2708, 2708, 2768, 2769, 2768, 2829, 2769, 2769, 2829, 2830, - 2829, 2890, 2830, 2830, 2890, 2891, 2890, 2951, 2891, 2891, 2951, 2952, - 2951, 3012, 2952, 2952, 3012, 3013, 3012, 3073, 3013, 3013, 3073, 3074, - 3073, 3134, 3074, 3074, 3134, 3135, 3134, 3195, 3135, 3135, 3195, 3196, - 3195, 3256, 3196, 3196, 3256, 3257, 3256, 3317, 3257, 3257, 3317, 3318, - 3317, 3378, 3318, 3318, 3378, 3379, 3378, 3439, 3379, 3379, 3439, 3440, - 3439, 3500, 3440, 3440, 3500, 3501, 3500, 3561, 3501, 3501, 3561, 3562, - 3561, 3622, 3562, 3562, 3622, 3623, 3622, 3683, 3623, 3623, 3683, 3684, - 24, 85, 25, 25, 85, 86, 85, 146, 86, 86, 146, 147, - 146, 207, 147, 147, 207, 208, 207, 268, 208, 208, 268, 269, - 268, 329, 269, 269, 329, 330, 329, 390, 330, 330, 390, 391, - 390, 451, 391, 391, 451, 452, 451, 512, 452, 452, 512, 513, - 512, 573, 513, 513, 573, 574, 573, 634, 574, 574, 634, 635, - 634, 695, 635, 635, 695, 696, 695, 756, 696, 696, 756, 757, - 756, 817, 757, 757, 817, 818, 817, 878, 818, 818, 878, 879, - 878, 939, 879, 879, 939, 940, 939, 1000, 940, 940, 1000, 1001, - 1000, 1061, 1001, 1001, 1061, 1062, 1061, 1122, 1062, 1062, 1122, 1123, - 1122, 1183, 1123, 1123, 1183, 1184, 1183, 1244, 1184, 1184, 1244, 1245, - 1244, 1305, 1245, 1245, 1305, 1306, 1305, 1366, 1306, 1306, 1366, 1367, - 1366, 1427, 1367, 1367, 1427, 1428, 1427, 1488, 1428, 1428, 1488, 1489, - 1488, 1549, 1489, 1489, 1549, 1550, 1549, 1610, 1550, 1550, 1610, 1611, - 1610, 1671, 1611, 1611, 1671, 1672, 1671, 1732, 1672, 1672, 1732, 1733, - 1732, 1793, 1733, 1733, 1793, 1794, 1793, 1854, 1794, 1794, 1854, 1855, - 1854, 1915, 1855, 1855, 1915, 1916, 1915, 1976, 1916, 1916, 1976, 1977, - 1976, 2037, 1977, 1977, 2037, 2038, 2037, 2098, 2038, 2038, 2098, 2099, - 2098, 2159, 2099, 2099, 2159, 2160, 2159, 2220, 2160, 2160, 2220, 2221, - 2220, 2281, 2221, 2221, 2281, 2282, 2281, 2342, 2282, 2282, 2342, 2343, - 2342, 2403, 2343, 2343, 2403, 2404, 2403, 2464, 2404, 2404, 2464, 2465, - 2464, 2525, 2465, 2465, 2525, 2526, 2525, 2586, 2526, 2526, 2586, 2587, - 2586, 2647, 2587, 2587, 2647, 2648, 2647, 2708, 2648, 2648, 2708, 2709, - 2708, 2769, 2709, 2709, 2769, 2770, 2769, 2830, 2770, 2770, 2830, 2831, - 2830, 2891, 2831, 2831, 2891, 2892, 2891, 2952, 2892, 2892, 2952, 2953, - 2952, 3013, 2953, 2953, 3013, 3014, 3013, 3074, 3014, 3014, 3074, 3075, - 3074, 3135, 3075, 3075, 3135, 3136, 3135, 3196, 3136, 3136, 3196, 3197, - 3196, 3257, 3197, 3197, 3257, 3258, 3257, 3318, 3258, 3258, 3318, 3319, - 3318, 3379, 3319, 3319, 3379, 3380, 3379, 3440, 3380, 3380, 3440, 3441, - 3440, 3501, 3441, 3441, 3501, 3502, 3501, 3562, 3502, 3502, 3562, 3563, - 3562, 3623, 3563, 3563, 3623, 3624, 3623, 3684, 3624, 3624, 3684, 3685, - 25, 86, 26, 26, 86, 87, 86, 147, 87, 87, 147, 148, - 147, 208, 148, 148, 208, 209, 208, 269, 209, 209, 269, 270, - 269, 330, 270, 270, 330, 331, 330, 391, 331, 331, 391, 392, - 391, 452, 392, 392, 452, 453, 452, 513, 453, 453, 513, 514, - 513, 574, 514, 514, 574, 575, 574, 635, 575, 575, 635, 636, - 635, 696, 636, 636, 696, 697, 696, 757, 697, 697, 757, 758, - 757, 818, 758, 758, 818, 819, 818, 879, 819, 819, 879, 880, - 879, 940, 880, 880, 940, 941, 940, 1001, 941, 941, 1001, 1002, - 1001, 1062, 1002, 1002, 1062, 1063, 1062, 1123, 1063, 1063, 1123, 1124, - 1123, 1184, 1124, 1124, 1184, 1185, 1184, 1245, 1185, 1185, 1245, 1246, - 1245, 1306, 1246, 1246, 1306, 1307, 1306, 1367, 1307, 1307, 1367, 1368, - 1367, 1428, 1368, 1368, 1428, 1429, 1428, 1489, 1429, 1429, 1489, 1490, - 1489, 1550, 1490, 1490, 1550, 1551, 1550, 1611, 1551, 1551, 1611, 1612, - 1611, 1672, 1612, 1612, 1672, 1673, 1672, 1733, 1673, 1673, 1733, 1734, - 1733, 1794, 1734, 1734, 1794, 1795, 1794, 1855, 1795, 1795, 1855, 1856, - 1855, 1916, 1856, 1856, 1916, 1917, 1916, 1977, 1917, 1917, 1977, 1978, - 1977, 2038, 1978, 1978, 2038, 2039, 2038, 2099, 2039, 2039, 2099, 2100, - 2099, 2160, 2100, 2100, 2160, 2161, 2160, 2221, 2161, 2161, 2221, 2222, - 2221, 2282, 2222, 2222, 2282, 2283, 2282, 2343, 2283, 2283, 2343, 2344, - 2343, 2404, 2344, 2344, 2404, 2405, 2404, 2465, 2405, 2405, 2465, 2466, - 2465, 2526, 2466, 2466, 2526, 2527, 2526, 2587, 2527, 2527, 2587, 2588, - 2587, 2648, 2588, 2588, 2648, 2649, 2648, 2709, 2649, 2649, 2709, 2710, - 2709, 2770, 2710, 2710, 2770, 2771, 2770, 2831, 2771, 2771, 2831, 2832, - 2831, 2892, 2832, 2832, 2892, 2893, 2892, 2953, 2893, 2893, 2953, 2954, - 2953, 3014, 2954, 2954, 3014, 3015, 3014, 3075, 3015, 3015, 3075, 3076, - 3075, 3136, 3076, 3076, 3136, 3137, 3136, 3197, 3137, 3137, 3197, 3198, - 3197, 3258, 3198, 3198, 3258, 3259, 3258, 3319, 3259, 3259, 3319, 3320, - 3319, 3380, 3320, 3320, 3380, 3381, 3380, 3441, 3381, 3381, 3441, 3442, - 3441, 3502, 3442, 3442, 3502, 3503, 3502, 3563, 3503, 3503, 3563, 3564, - 3563, 3624, 3564, 3564, 3624, 3625, 3624, 3685, 3625, 3625, 3685, 3686, - 26, 87, 27, 27, 87, 88, 87, 148, 88, 88, 148, 149, - 148, 209, 149, 149, 209, 210, 209, 270, 210, 210, 270, 271, - 270, 331, 271, 271, 331, 332, 331, 392, 332, 332, 392, 393, - 392, 453, 393, 393, 453, 454, 453, 514, 454, 454, 514, 515, - 514, 575, 515, 515, 575, 576, 575, 636, 576, 576, 636, 637, - 636, 697, 637, 637, 697, 698, 697, 758, 698, 698, 758, 759, - 758, 819, 759, 759, 819, 820, 819, 880, 820, 820, 880, 881, - 880, 941, 881, 881, 941, 942, 941, 1002, 942, 942, 1002, 1003, - 1002, 1063, 1003, 1003, 1063, 1064, 1063, 1124, 1064, 1064, 1124, 1125, - 1124, 1185, 1125, 1125, 1185, 1186, 1185, 1246, 1186, 1186, 1246, 1247, - 1246, 1307, 1247, 1247, 1307, 1308, 1307, 1368, 1308, 1308, 1368, 1369, - 1368, 1429, 1369, 1369, 1429, 1430, 1429, 1490, 1430, 1430, 1490, 1491, - 1490, 1551, 1491, 1491, 1551, 1552, 1551, 1612, 1552, 1552, 1612, 1613, - 1612, 1673, 1613, 1613, 1673, 1674, 1673, 1734, 1674, 1674, 1734, 1735, - 1734, 1795, 1735, 1735, 1795, 1796, 1795, 1856, 1796, 1796, 1856, 1857, - 1856, 1917, 1857, 1857, 1917, 1918, 1917, 1978, 1918, 1918, 1978, 1979, - 1978, 2039, 1979, 1979, 2039, 2040, 2039, 2100, 2040, 2040, 2100, 2101, - 2100, 2161, 2101, 2101, 2161, 2162, 2161, 2222, 2162, 2162, 2222, 2223, - 2222, 2283, 2223, 2223, 2283, 2284, 2283, 2344, 2284, 2284, 2344, 2345, - 2344, 2405, 2345, 2345, 2405, 2406, 2405, 2466, 2406, 2406, 2466, 2467, - 2466, 2527, 2467, 2467, 2527, 2528, 2527, 2588, 2528, 2528, 2588, 2589, - 2588, 2649, 2589, 2589, 2649, 2650, 2649, 2710, 2650, 2650, 2710, 2711, - 2710, 2771, 2711, 2711, 2771, 2772, 2771, 2832, 2772, 2772, 2832, 2833, - 2832, 2893, 2833, 2833, 2893, 2894, 2893, 2954, 2894, 2894, 2954, 2955, - 2954, 3015, 2955, 2955, 3015, 3016, 3015, 3076, 3016, 3016, 3076, 3077, - 3076, 3137, 3077, 3077, 3137, 3138, 3137, 3198, 3138, 3138, 3198, 3199, - 3198, 3259, 3199, 3199, 3259, 3260, 3259, 3320, 3260, 3260, 3320, 3321, - 3320, 3381, 3321, 3321, 3381, 3382, 3381, 3442, 3382, 3382, 3442, 3443, - 3442, 3503, 3443, 3443, 3503, 3504, 3503, 3564, 3504, 3504, 3564, 3565, - 3564, 3625, 3565, 3565, 3625, 3626, 3625, 3686, 3626, 3626, 3686, 3687, - 27, 88, 28, 28, 88, 89, 88, 149, 89, 89, 149, 150, - 149, 210, 150, 150, 210, 211, 210, 271, 211, 211, 271, 272, - 271, 332, 272, 272, 332, 333, 332, 393, 333, 333, 393, 394, - 393, 454, 394, 394, 454, 455, 454, 515, 455, 455, 515, 516, - 515, 576, 516, 516, 576, 577, 576, 637, 577, 577, 637, 638, - 637, 698, 638, 638, 698, 699, 698, 759, 699, 699, 759, 760, - 759, 820, 760, 760, 820, 821, 820, 881, 821, 821, 881, 882, - 881, 942, 882, 882, 942, 943, 942, 1003, 943, 943, 1003, 1004, - 1003, 1064, 1004, 1004, 1064, 1065, 1064, 1125, 1065, 1065, 1125, 1126, - 1125, 1186, 1126, 1126, 1186, 1187, 1186, 1247, 1187, 1187, 1247, 1248, - 1247, 1308, 1248, 1248, 1308, 1309, 1308, 1369, 1309, 1309, 1369, 1370, - 1369, 1430, 1370, 1370, 1430, 1431, 1430, 1491, 1431, 1431, 1491, 1492, - 1491, 1552, 1492, 1492, 1552, 1553, 1552, 1613, 1553, 1553, 1613, 1614, - 1613, 1674, 1614, 1614, 1674, 1675, 1674, 1735, 1675, 1675, 1735, 1736, - 1735, 1796, 1736, 1736, 1796, 1797, 1796, 1857, 1797, 1797, 1857, 1858, - 1857, 1918, 1858, 1858, 1918, 1919, 1918, 1979, 1919, 1919, 1979, 1980, - 1979, 2040, 1980, 1980, 2040, 2041, 2040, 2101, 2041, 2041, 2101, 2102, - 2101, 2162, 2102, 2102, 2162, 2163, 2162, 2223, 2163, 2163, 2223, 2224, - 2223, 2284, 2224, 2224, 2284, 2285, 2284, 2345, 2285, 2285, 2345, 2346, - 2345, 2406, 2346, 2346, 2406, 2407, 2406, 2467, 2407, 2407, 2467, 2468, - 2467, 2528, 2468, 2468, 2528, 2529, 2528, 2589, 2529, 2529, 2589, 2590, - 2589, 2650, 2590, 2590, 2650, 2651, 2650, 2711, 2651, 2651, 2711, 2712, - 2711, 2772, 2712, 2712, 2772, 2773, 2772, 2833, 2773, 2773, 2833, 2834, - 2833, 2894, 2834, 2834, 2894, 2895, 2894, 2955, 2895, 2895, 2955, 2956, - 2955, 3016, 2956, 2956, 3016, 3017, 3016, 3077, 3017, 3017, 3077, 3078, - 3077, 3138, 3078, 3078, 3138, 3139, 3138, 3199, 3139, 3139, 3199, 3200, - 3199, 3260, 3200, 3200, 3260, 3261, 3260, 3321, 3261, 3261, 3321, 3322, - 3321, 3382, 3322, 3322, 3382, 3383, 3382, 3443, 3383, 3383, 3443, 3444, - 3443, 3504, 3444, 3444, 3504, 3505, 3504, 3565, 3505, 3505, 3565, 3566, - 3565, 3626, 3566, 3566, 3626, 3627, 3626, 3687, 3627, 3627, 3687, 3688, - 28, 89, 29, 29, 89, 90, 89, 150, 90, 90, 150, 151, - 150, 211, 151, 151, 211, 212, 211, 272, 212, 212, 272, 273, - 272, 333, 273, 273, 333, 334, 333, 394, 334, 334, 394, 395, - 394, 455, 395, 395, 455, 456, 455, 516, 456, 456, 516, 517, - 516, 577, 517, 517, 577, 578, 577, 638, 578, 578, 638, 639, - 638, 699, 639, 639, 699, 700, 699, 760, 700, 700, 760, 761, - 760, 821, 761, 761, 821, 822, 821, 882, 822, 822, 882, 883, - 882, 943, 883, 883, 943, 944, 943, 1004, 944, 944, 1004, 1005, - 1004, 1065, 1005, 1005, 1065, 1066, 1065, 1126, 1066, 1066, 1126, 1127, - 1126, 1187, 1127, 1127, 1187, 1188, 1187, 1248, 1188, 1188, 1248, 1249, - 1248, 1309, 1249, 1249, 1309, 1310, 1309, 1370, 1310, 1310, 1370, 1371, - 1370, 1431, 1371, 1371, 1431, 1432, 1431, 1492, 1432, 1432, 1492, 1493, - 1492, 1553, 1493, 1493, 1553, 1554, 1553, 1614, 1554, 1554, 1614, 1615, - 1614, 1675, 1615, 1615, 1675, 1676, 1675, 1736, 1676, 1676, 1736, 1737, - 1736, 1797, 1737, 1737, 1797, 1798, 1797, 1858, 1798, 1798, 1858, 1859, - 1858, 1919, 1859, 1859, 1919, 1920, 1919, 1980, 1920, 1920, 1980, 1981, - 1980, 2041, 1981, 1981, 2041, 2042, 2041, 2102, 2042, 2042, 2102, 2103, - 2102, 2163, 2103, 2103, 2163, 2164, 2163, 2224, 2164, 2164, 2224, 2225, - 2224, 2285, 2225, 2225, 2285, 2286, 2285, 2346, 2286, 2286, 2346, 2347, - 2346, 2407, 2347, 2347, 2407, 2408, 2407, 2468, 2408, 2408, 2468, 2469, - 2468, 2529, 2469, 2469, 2529, 2530, 2529, 2590, 2530, 2530, 2590, 2591, - 2590, 2651, 2591, 2591, 2651, 2652, 2651, 2712, 2652, 2652, 2712, 2713, - 2712, 2773, 2713, 2713, 2773, 2774, 2773, 2834, 2774, 2774, 2834, 2835, - 2834, 2895, 2835, 2835, 2895, 2896, 2895, 2956, 2896, 2896, 2956, 2957, - 2956, 3017, 2957, 2957, 3017, 3018, 3017, 3078, 3018, 3018, 3078, 3079, - 3078, 3139, 3079, 3079, 3139, 3140, 3139, 3200, 3140, 3140, 3200, 3201, - 3200, 3261, 3201, 3201, 3261, 3262, 3261, 3322, 3262, 3262, 3322, 3323, - 3322, 3383, 3323, 3323, 3383, 3384, 3383, 3444, 3384, 3384, 3444, 3445, - 3444, 3505, 3445, 3445, 3505, 3506, 3505, 3566, 3506, 3506, 3566, 3567, - 3566, 3627, 3567, 3567, 3627, 3628, 3627, 3688, 3628, 3628, 3688, 3689, - 29, 90, 30, 30, 90, 91, 90, 151, 91, 91, 151, 152, - 151, 212, 152, 152, 212, 213, 212, 273, 213, 213, 273, 274, - 273, 334, 274, 274, 334, 335, 334, 395, 335, 335, 395, 396, - 395, 456, 396, 396, 456, 457, 456, 517, 457, 457, 517, 518, - 517, 578, 518, 518, 578, 579, 578, 639, 579, 579, 639, 640, - 639, 700, 640, 640, 700, 701, 700, 761, 701, 701, 761, 762, - 761, 822, 762, 762, 822, 823, 822, 883, 823, 823, 883, 884, - 883, 944, 884, 884, 944, 945, 944, 1005, 945, 945, 1005, 1006, - 1005, 1066, 1006, 1006, 1066, 1067, 1066, 1127, 1067, 1067, 1127, 1128, - 1127, 1188, 1128, 1128, 1188, 1189, 1188, 1249, 1189, 1189, 1249, 1250, - 1249, 1310, 1250, 1250, 1310, 1311, 1310, 1371, 1311, 1311, 1371, 1372, - 1371, 1432, 1372, 1372, 1432, 1433, 1432, 1493, 1433, 1433, 1493, 1494, - 1493, 1554, 1494, 1494, 1554, 1555, 1554, 1615, 1555, 1555, 1615, 1616, - 1615, 1676, 1616, 1616, 1676, 1677, 1676, 1737, 1677, 1677, 1737, 1738, - 1737, 1798, 1738, 1738, 1798, 1799, 1798, 1859, 1799, 1799, 1859, 1860, - 1859, 1920, 1860, 1860, 1920, 1921, 1920, 1981, 1921, 1921, 1981, 1982, - 1981, 2042, 1982, 1982, 2042, 2043, 2042, 2103, 2043, 2043, 2103, 2104, - 2103, 2164, 2104, 2104, 2164, 2165, 2164, 2225, 2165, 2165, 2225, 2226, - 2225, 2286, 2226, 2226, 2286, 2287, 2286, 2347, 2287, 2287, 2347, 2348, - 2347, 2408, 2348, 2348, 2408, 2409, 2408, 2469, 2409, 2409, 2469, 2470, - 2469, 2530, 2470, 2470, 2530, 2531, 2530, 2591, 2531, 2531, 2591, 2592, - 2591, 2652, 2592, 2592, 2652, 2653, 2652, 2713, 2653, 2653, 2713, 2714, - 2713, 2774, 2714, 2714, 2774, 2775, 2774, 2835, 2775, 2775, 2835, 2836, - 2835, 2896, 2836, 2836, 2896, 2897, 2896, 2957, 2897, 2897, 2957, 2958, - 2957, 3018, 2958, 2958, 3018, 3019, 3018, 3079, 3019, 3019, 3079, 3080, - 3079, 3140, 3080, 3080, 3140, 3141, 3140, 3201, 3141, 3141, 3201, 3202, - 3201, 3262, 3202, 3202, 3262, 3263, 3262, 3323, 3263, 3263, 3323, 3324, - 3323, 3384, 3324, 3324, 3384, 3385, 3384, 3445, 3385, 3385, 3445, 3446, - 3445, 3506, 3446, 3446, 3506, 3507, 3506, 3567, 3507, 3507, 3567, 3568, - 3567, 3628, 3568, 3568, 3628, 3629, 3628, 3689, 3629, 3629, 3689, 3690, - 30, 91, 31, 31, 91, 92, 91, 152, 92, 92, 152, 153, - 152, 213, 153, 153, 213, 214, 213, 274, 214, 214, 274, 275, - 274, 335, 275, 275, 335, 336, 335, 396, 336, 336, 396, 397, - 396, 457, 397, 397, 457, 458, 457, 518, 458, 458, 518, 519, - 518, 579, 519, 519, 579, 580, 579, 640, 580, 580, 640, 641, - 640, 701, 641, 641, 701, 702, 701, 762, 702, 702, 762, 763, - 762, 823, 763, 763, 823, 824, 823, 884, 824, 824, 884, 885, - 884, 945, 885, 885, 945, 946, 945, 1006, 946, 946, 1006, 1007, - 1006, 1067, 1007, 1007, 1067, 1068, 1067, 1128, 1068, 1068, 1128, 1129, - 1128, 1189, 1129, 1129, 1189, 1190, 1189, 1250, 1190, 1190, 1250, 1251, - 1250, 1311, 1251, 1251, 1311, 1312, 1311, 1372, 1312, 1312, 1372, 1373, - 1372, 1433, 1373, 1373, 1433, 1434, 1433, 1494, 1434, 1434, 1494, 1495, - 1494, 1555, 1495, 1495, 1555, 1556, 1555, 1616, 1556, 1556, 1616, 1617, - 1616, 1677, 1617, 1617, 1677, 1678, 1677, 1738, 1678, 1678, 1738, 1739, - 1738, 1799, 1739, 1739, 1799, 1800, 1799, 1860, 1800, 1800, 1860, 1861, - 1860, 1921, 1861, 1861, 1921, 1922, 1921, 1982, 1922, 1922, 1982, 1983, - 1982, 2043, 1983, 1983, 2043, 2044, 2043, 2104, 2044, 2044, 2104, 2105, - 2104, 2165, 2105, 2105, 2165, 2166, 2165, 2226, 2166, 2166, 2226, 2227, - 2226, 2287, 2227, 2227, 2287, 2288, 2287, 2348, 2288, 2288, 2348, 2349, - 2348, 2409, 2349, 2349, 2409, 2410, 2409, 2470, 2410, 2410, 2470, 2471, - 2470, 2531, 2471, 2471, 2531, 2532, 2531, 2592, 2532, 2532, 2592, 2593, - 2592, 2653, 2593, 2593, 2653, 2654, 2653, 2714, 2654, 2654, 2714, 2715, - 2714, 2775, 2715, 2715, 2775, 2776, 2775, 2836, 2776, 2776, 2836, 2837, - 2836, 2897, 2837, 2837, 2897, 2898, 2897, 2958, 2898, 2898, 2958, 2959, - 2958, 3019, 2959, 2959, 3019, 3020, 3019, 3080, 3020, 3020, 3080, 3081, - 3080, 3141, 3081, 3081, 3141, 3142, 3141, 3202, 3142, 3142, 3202, 3203, - 3202, 3263, 3203, 3203, 3263, 3264, 3263, 3324, 3264, 3264, 3324, 3325, - 3324, 3385, 3325, 3325, 3385, 3386, 3385, 3446, 3386, 3386, 3446, 3447, - 3446, 3507, 3447, 3447, 3507, 3508, 3507, 3568, 3508, 3508, 3568, 3569, - 3568, 3629, 3569, 3569, 3629, 3630, 3629, 3690, 3630, 3630, 3690, 3691, - 31, 92, 32, 32, 92, 93, 92, 153, 93, 93, 153, 154, - 153, 214, 154, 154, 214, 215, 214, 275, 215, 215, 275, 276, - 275, 336, 276, 276, 336, 337, 336, 397, 337, 337, 397, 398, - 397, 458, 398, 398, 458, 459, 458, 519, 459, 459, 519, 520, - 519, 580, 520, 520, 580, 581, 580, 641, 581, 581, 641, 642, - 641, 702, 642, 642, 702, 703, 702, 763, 703, 703, 763, 764, - 763, 824, 764, 764, 824, 825, 824, 885, 825, 825, 885, 886, - 885, 946, 886, 886, 946, 947, 946, 1007, 947, 947, 1007, 1008, - 1007, 1068, 1008, 1008, 1068, 1069, 1068, 1129, 1069, 1069, 1129, 1130, - 1129, 1190, 1130, 1130, 1190, 1191, 1190, 1251, 1191, 1191, 1251, 1252, - 1251, 1312, 1252, 1252, 1312, 1313, 1312, 1373, 1313, 1313, 1373, 1374, - 1373, 1434, 1374, 1374, 1434, 1435, 1434, 1495, 1435, 1435, 1495, 1496, - 1495, 1556, 1496, 1496, 1556, 1557, 1556, 1617, 1557, 1557, 1617, 1618, - 1617, 1678, 1618, 1618, 1678, 1679, 1678, 1739, 1679, 1679, 1739, 1740, - 1739, 1800, 1740, 1740, 1800, 1801, 1800, 1861, 1801, 1801, 1861, 1862, - 1861, 1922, 1862, 1862, 1922, 1923, 1922, 1983, 1923, 1923, 1983, 1984, - 1983, 2044, 1984, 1984, 2044, 2045, 2044, 2105, 2045, 2045, 2105, 2106, - 2105, 2166, 2106, 2106, 2166, 2167, 2166, 2227, 2167, 2167, 2227, 2228, - 2227, 2288, 2228, 2228, 2288, 2289, 2288, 2349, 2289, 2289, 2349, 2350, - 2349, 2410, 2350, 2350, 2410, 2411, 2410, 2471, 2411, 2411, 2471, 2472, - 2471, 2532, 2472, 2472, 2532, 2533, 2532, 2593, 2533, 2533, 2593, 2594, - 2593, 2654, 2594, 2594, 2654, 2655, 2654, 2715, 2655, 2655, 2715, 2716, - 2715, 2776, 2716, 2716, 2776, 2777, 2776, 2837, 2777, 2777, 2837, 2838, - 2837, 2898, 2838, 2838, 2898, 2899, 2898, 2959, 2899, 2899, 2959, 2960, - 2959, 3020, 2960, 2960, 3020, 3021, 3020, 3081, 3021, 3021, 3081, 3082, - 3081, 3142, 3082, 3082, 3142, 3143, 3142, 3203, 3143, 3143, 3203, 3204, - 3203, 3264, 3204, 3204, 3264, 3265, 3264, 3325, 3265, 3265, 3325, 3326, - 3325, 3386, 3326, 3326, 3386, 3387, 3386, 3447, 3387, 3387, 3447, 3448, - 3447, 3508, 3448, 3448, 3508, 3509, 3508, 3569, 3509, 3509, 3569, 3570, - 3569, 3630, 3570, 3570, 3630, 3631, 3630, 3691, 3631, 3631, 3691, 3692, - 32, 93, 33, 33, 93, 94, 93, 154, 94, 94, 154, 155, - 154, 215, 155, 155, 215, 216, 215, 276, 216, 216, 276, 277, - 276, 337, 277, 277, 337, 338, 337, 398, 338, 338, 398, 399, - 398, 459, 399, 399, 459, 460, 459, 520, 460, 460, 520, 521, - 520, 581, 521, 521, 581, 582, 581, 642, 582, 582, 642, 643, - 642, 703, 643, 643, 703, 704, 703, 764, 704, 704, 764, 765, - 764, 825, 765, 765, 825, 826, 825, 886, 826, 826, 886, 887, - 886, 947, 887, 887, 947, 948, 947, 1008, 948, 948, 1008, 1009, - 1008, 1069, 1009, 1009, 1069, 1070, 1069, 1130, 1070, 1070, 1130, 1131, - 1130, 1191, 1131, 1131, 1191, 1192, 1191, 1252, 1192, 1192, 1252, 1253, - 1252, 1313, 1253, 1253, 1313, 1314, 1313, 1374, 1314, 1314, 1374, 1375, - 1374, 1435, 1375, 1375, 1435, 1436, 1435, 1496, 1436, 1436, 1496, 1497, - 1496, 1557, 1497, 1497, 1557, 1558, 1557, 1618, 1558, 1558, 1618, 1619, - 1618, 1679, 1619, 1619, 1679, 1680, 1679, 1740, 1680, 1680, 1740, 1741, - 1740, 1801, 1741, 1741, 1801, 1802, 1801, 1862, 1802, 1802, 1862, 1863, - 1862, 1923, 1863, 1863, 1923, 1924, 1923, 1984, 1924, 1924, 1984, 1985, - 1984, 2045, 1985, 1985, 2045, 2046, 2045, 2106, 2046, 2046, 2106, 2107, - 2106, 2167, 2107, 2107, 2167, 2168, 2167, 2228, 2168, 2168, 2228, 2229, - 2228, 2289, 2229, 2229, 2289, 2290, 2289, 2350, 2290, 2290, 2350, 2351, - 2350, 2411, 2351, 2351, 2411, 2412, 2411, 2472, 2412, 2412, 2472, 2473, - 2472, 2533, 2473, 2473, 2533, 2534, 2533, 2594, 2534, 2534, 2594, 2595, - 2594, 2655, 2595, 2595, 2655, 2656, 2655, 2716, 2656, 2656, 2716, 2717, - 2716, 2777, 2717, 2717, 2777, 2778, 2777, 2838, 2778, 2778, 2838, 2839, - 2838, 2899, 2839, 2839, 2899, 2900, 2899, 2960, 2900, 2900, 2960, 2961, - 2960, 3021, 2961, 2961, 3021, 3022, 3021, 3082, 3022, 3022, 3082, 3083, - 3082, 3143, 3083, 3083, 3143, 3144, 3143, 3204, 3144, 3144, 3204, 3205, - 3204, 3265, 3205, 3205, 3265, 3266, 3265, 3326, 3266, 3266, 3326, 3327, - 3326, 3387, 3327, 3327, 3387, 3388, 3387, 3448, 3388, 3388, 3448, 3449, - 3448, 3509, 3449, 3449, 3509, 3510, 3509, 3570, 3510, 3510, 3570, 3571, - 3570, 3631, 3571, 3571, 3631, 3632, 3631, 3692, 3632, 3632, 3692, 3693, - 33, 94, 34, 34, 94, 95, 94, 155, 95, 95, 155, 156, - 155, 216, 156, 156, 216, 217, 216, 277, 217, 217, 277, 278, - 277, 338, 278, 278, 338, 339, 338, 399, 339, 339, 399, 400, - 399, 460, 400, 400, 460, 461, 460, 521, 461, 461, 521, 522, - 521, 582, 522, 522, 582, 583, 582, 643, 583, 583, 643, 644, - 643, 704, 644, 644, 704, 705, 704, 765, 705, 705, 765, 766, - 765, 826, 766, 766, 826, 827, 826, 887, 827, 827, 887, 888, - 887, 948, 888, 888, 948, 949, 948, 1009, 949, 949, 1009, 1010, - 1009, 1070, 1010, 1010, 1070, 1071, 1070, 1131, 1071, 1071, 1131, 1132, - 1131, 1192, 1132, 1132, 1192, 1193, 1192, 1253, 1193, 1193, 1253, 1254, - 1253, 1314, 1254, 1254, 1314, 1315, 1314, 1375, 1315, 1315, 1375, 1376, - 1375, 1436, 1376, 1376, 1436, 1437, 1436, 1497, 1437, 1437, 1497, 1498, - 1497, 1558, 1498, 1498, 1558, 1559, 1558, 1619, 1559, 1559, 1619, 1620, - 1619, 1680, 1620, 1620, 1680, 1681, 1680, 1741, 1681, 1681, 1741, 1742, - 1741, 1802, 1742, 1742, 1802, 1803, 1802, 1863, 1803, 1803, 1863, 1864, - 1863, 1924, 1864, 1864, 1924, 1925, 1924, 1985, 1925, 1925, 1985, 1986, - 1985, 2046, 1986, 1986, 2046, 2047, 2046, 2107, 2047, 2047, 2107, 2108, - 2107, 2168, 2108, 2108, 2168, 2169, 2168, 2229, 2169, 2169, 2229, 2230, - 2229, 2290, 2230, 2230, 2290, 2291, 2290, 2351, 2291, 2291, 2351, 2352, - 2351, 2412, 2352, 2352, 2412, 2413, 2412, 2473, 2413, 2413, 2473, 2474, - 2473, 2534, 2474, 2474, 2534, 2535, 2534, 2595, 2535, 2535, 2595, 2596, - 2595, 2656, 2596, 2596, 2656, 2657, 2656, 2717, 2657, 2657, 2717, 2718, - 2717, 2778, 2718, 2718, 2778, 2779, 2778, 2839, 2779, 2779, 2839, 2840, - 2839, 2900, 2840, 2840, 2900, 2901, 2900, 2961, 2901, 2901, 2961, 2962, - 2961, 3022, 2962, 2962, 3022, 3023, 3022, 3083, 3023, 3023, 3083, 3084, - 3083, 3144, 3084, 3084, 3144, 3145, 3144, 3205, 3145, 3145, 3205, 3206, - 3205, 3266, 3206, 3206, 3266, 3267, 3266, 3327, 3267, 3267, 3327, 3328, - 3327, 3388, 3328, 3328, 3388, 3389, 3388, 3449, 3389, 3389, 3449, 3450, - 3449, 3510, 3450, 3450, 3510, 3511, 3510, 3571, 3511, 3511, 3571, 3572, - 3571, 3632, 3572, 3572, 3632, 3633, 3632, 3693, 3633, 3633, 3693, 3694, - 34, 95, 35, 35, 95, 96, 95, 156, 96, 96, 156, 157, - 156, 217, 157, 157, 217, 218, 217, 278, 218, 218, 278, 279, - 278, 339, 279, 279, 339, 340, 339, 400, 340, 340, 400, 401, - 400, 461, 401, 401, 461, 462, 461, 522, 462, 462, 522, 523, - 522, 583, 523, 523, 583, 584, 583, 644, 584, 584, 644, 645, - 644, 705, 645, 645, 705, 706, 705, 766, 706, 706, 766, 767, - 766, 827, 767, 767, 827, 828, 827, 888, 828, 828, 888, 889, - 888, 949, 889, 889, 949, 950, 949, 1010, 950, 950, 1010, 1011, - 1010, 1071, 1011, 1011, 1071, 1072, 1071, 1132, 1072, 1072, 1132, 1133, - 1132, 1193, 1133, 1133, 1193, 1194, 1193, 1254, 1194, 1194, 1254, 1255, - 1254, 1315, 1255, 1255, 1315, 1316, 1315, 1376, 1316, 1316, 1376, 1377, - 1376, 1437, 1377, 1377, 1437, 1438, 1437, 1498, 1438, 1438, 1498, 1499, - 1498, 1559, 1499, 1499, 1559, 1560, 1559, 1620, 1560, 1560, 1620, 1621, - 1620, 1681, 1621, 1621, 1681, 1682, 1681, 1742, 1682, 1682, 1742, 1743, - 1742, 1803, 1743, 1743, 1803, 1804, 1803, 1864, 1804, 1804, 1864, 1865, - 1864, 1925, 1865, 1865, 1925, 1926, 1925, 1986, 1926, 1926, 1986, 1987, - 1986, 2047, 1987, 1987, 2047, 2048, 2047, 2108, 2048, 2048, 2108, 2109, - 2108, 2169, 2109, 2109, 2169, 2170, 2169, 2230, 2170, 2170, 2230, 2231, - 2230, 2291, 2231, 2231, 2291, 2292, 2291, 2352, 2292, 2292, 2352, 2353, - 2352, 2413, 2353, 2353, 2413, 2414, 2413, 2474, 2414, 2414, 2474, 2475, - 2474, 2535, 2475, 2475, 2535, 2536, 2535, 2596, 2536, 2536, 2596, 2597, - 2596, 2657, 2597, 2597, 2657, 2658, 2657, 2718, 2658, 2658, 2718, 2719, - 2718, 2779, 2719, 2719, 2779, 2780, 2779, 2840, 2780, 2780, 2840, 2841, - 2840, 2901, 2841, 2841, 2901, 2902, 2901, 2962, 2902, 2902, 2962, 2963, - 2962, 3023, 2963, 2963, 3023, 3024, 3023, 3084, 3024, 3024, 3084, 3085, - 3084, 3145, 3085, 3085, 3145, 3146, 3145, 3206, 3146, 3146, 3206, 3207, - 3206, 3267, 3207, 3207, 3267, 3268, 3267, 3328, 3268, 3268, 3328, 3329, - 3328, 3389, 3329, 3329, 3389, 3390, 3389, 3450, 3390, 3390, 3450, 3451, - 3450, 3511, 3451, 3451, 3511, 3512, 3511, 3572, 3512, 3512, 3572, 3573, - 3572, 3633, 3573, 3573, 3633, 3634, 3633, 3694, 3634, 3634, 3694, 3695, - 35, 96, 36, 36, 96, 97, 96, 157, 97, 97, 157, 158, - 157, 218, 158, 158, 218, 219, 218, 279, 219, 219, 279, 280, - 279, 340, 280, 280, 340, 341, 340, 401, 341, 341, 401, 402, - 401, 462, 402, 402, 462, 463, 462, 523, 463, 463, 523, 524, - 523, 584, 524, 524, 584, 585, 584, 645, 585, 585, 645, 646, - 645, 706, 646, 646, 706, 707, 706, 767, 707, 707, 767, 768, - 767, 828, 768, 768, 828, 829, 828, 889, 829, 829, 889, 890, - 889, 950, 890, 890, 950, 951, 950, 1011, 951, 951, 1011, 1012, - 1011, 1072, 1012, 1012, 1072, 1073, 1072, 1133, 1073, 1073, 1133, 1134, - 1133, 1194, 1134, 1134, 1194, 1195, 1194, 1255, 1195, 1195, 1255, 1256, - 1255, 1316, 1256, 1256, 1316, 1317, 1316, 1377, 1317, 1317, 1377, 1378, - 1377, 1438, 1378, 1378, 1438, 1439, 1438, 1499, 1439, 1439, 1499, 1500, - 1499, 1560, 1500, 1500, 1560, 1561, 1560, 1621, 1561, 1561, 1621, 1622, - 1621, 1682, 1622, 1622, 1682, 1683, 1682, 1743, 1683, 1683, 1743, 1744, - 1743, 1804, 1744, 1744, 1804, 1805, 1804, 1865, 1805, 1805, 1865, 1866, - 1865, 1926, 1866, 1866, 1926, 1927, 1926, 1987, 1927, 1927, 1987, 1988, - 1987, 2048, 1988, 1988, 2048, 2049, 2048, 2109, 2049, 2049, 2109, 2110, - 2109, 2170, 2110, 2110, 2170, 2171, 2170, 2231, 2171, 2171, 2231, 2232, - 2231, 2292, 2232, 2232, 2292, 2293, 2292, 2353, 2293, 2293, 2353, 2354, - 2353, 2414, 2354, 2354, 2414, 2415, 2414, 2475, 2415, 2415, 2475, 2476, - 2475, 2536, 2476, 2476, 2536, 2537, 2536, 2597, 2537, 2537, 2597, 2598, - 2597, 2658, 2598, 2598, 2658, 2659, 2658, 2719, 2659, 2659, 2719, 2720, - 2719, 2780, 2720, 2720, 2780, 2781, 2780, 2841, 2781, 2781, 2841, 2842, - 2841, 2902, 2842, 2842, 2902, 2903, 2902, 2963, 2903, 2903, 2963, 2964, - 2963, 3024, 2964, 2964, 3024, 3025, 3024, 3085, 3025, 3025, 3085, 3086, - 3085, 3146, 3086, 3086, 3146, 3147, 3146, 3207, 3147, 3147, 3207, 3208, - 3207, 3268, 3208, 3208, 3268, 3269, 3268, 3329, 3269, 3269, 3329, 3330, - 3329, 3390, 3330, 3330, 3390, 3391, 3390, 3451, 3391, 3391, 3451, 3452, - 3451, 3512, 3452, 3452, 3512, 3513, 3512, 3573, 3513, 3513, 3573, 3574, - 3573, 3634, 3574, 3574, 3634, 3635, 3634, 3695, 3635, 3635, 3695, 3696, - 36, 97, 37, 37, 97, 98, 97, 158, 98, 98, 158, 159, - 158, 219, 159, 159, 219, 220, 219, 280, 220, 220, 280, 281, - 280, 341, 281, 281, 341, 342, 341, 402, 342, 342, 402, 403, - 402, 463, 403, 403, 463, 464, 463, 524, 464, 464, 524, 525, - 524, 585, 525, 525, 585, 586, 585, 646, 586, 586, 646, 647, - 646, 707, 647, 647, 707, 708, 707, 768, 708, 708, 768, 769, - 768, 829, 769, 769, 829, 830, 829, 890, 830, 830, 890, 891, - 890, 951, 891, 891, 951, 952, 951, 1012, 952, 952, 1012, 1013, - 1012, 1073, 1013, 1013, 1073, 1074, 1073, 1134, 1074, 1074, 1134, 1135, - 1134, 1195, 1135, 1135, 1195, 1196, 1195, 1256, 1196, 1196, 1256, 1257, - 1256, 1317, 1257, 1257, 1317, 1318, 1317, 1378, 1318, 1318, 1378, 1379, - 1378, 1439, 1379, 1379, 1439, 1440, 1439, 1500, 1440, 1440, 1500, 1501, - 1500, 1561, 1501, 1501, 1561, 1562, 1561, 1622, 1562, 1562, 1622, 1623, - 1622, 1683, 1623, 1623, 1683, 1684, 1683, 1744, 1684, 1684, 1744, 1745, - 1744, 1805, 1745, 1745, 1805, 1806, 1805, 1866, 1806, 1806, 1866, 1867, - 1866, 1927, 1867, 1867, 1927, 1928, 1927, 1988, 1928, 1928, 1988, 1989, - 1988, 2049, 1989, 1989, 2049, 2050, 2049, 2110, 2050, 2050, 2110, 2111, - 2110, 2171, 2111, 2111, 2171, 2172, 2171, 2232, 2172, 2172, 2232, 2233, - 2232, 2293, 2233, 2233, 2293, 2294, 2293, 2354, 2294, 2294, 2354, 2355, - 2354, 2415, 2355, 2355, 2415, 2416, 2415, 2476, 2416, 2416, 2476, 2477, - 2476, 2537, 2477, 2477, 2537, 2538, 2537, 2598, 2538, 2538, 2598, 2599, - 2598, 2659, 2599, 2599, 2659, 2660, 2659, 2720, 2660, 2660, 2720, 2721, - 2720, 2781, 2721, 2721, 2781, 2782, 2781, 2842, 2782, 2782, 2842, 2843, - 2842, 2903, 2843, 2843, 2903, 2904, 2903, 2964, 2904, 2904, 2964, 2965, - 2964, 3025, 2965, 2965, 3025, 3026, 3025, 3086, 3026, 3026, 3086, 3087, - 3086, 3147, 3087, 3087, 3147, 3148, 3147, 3208, 3148, 3148, 3208, 3209, - 3208, 3269, 3209, 3209, 3269, 3270, 3269, 3330, 3270, 3270, 3330, 3331, - 3330, 3391, 3331, 3331, 3391, 3392, 3391, 3452, 3392, 3392, 3452, 3453, - 3452, 3513, 3453, 3453, 3513, 3514, 3513, 3574, 3514, 3514, 3574, 3575, - 3574, 3635, 3575, 3575, 3635, 3636, 3635, 3696, 3636, 3636, 3696, 3697, - 37, 98, 38, 38, 98, 99, 98, 159, 99, 99, 159, 160, - 159, 220, 160, 160, 220, 221, 220, 281, 221, 221, 281, 282, - 281, 342, 282, 282, 342, 343, 342, 403, 343, 343, 403, 404, - 403, 464, 404, 404, 464, 465, 464, 525, 465, 465, 525, 526, - 525, 586, 526, 526, 586, 587, 586, 647, 587, 587, 647, 648, - 647, 708, 648, 648, 708, 709, 708, 769, 709, 709, 769, 770, - 769, 830, 770, 770, 830, 831, 830, 891, 831, 831, 891, 892, - 891, 952, 892, 892, 952, 953, 952, 1013, 953, 953, 1013, 1014, - 1013, 1074, 1014, 1014, 1074, 1075, 1074, 1135, 1075, 1075, 1135, 1136, - 1135, 1196, 1136, 1136, 1196, 1197, 1196, 1257, 1197, 1197, 1257, 1258, - 1257, 1318, 1258, 1258, 1318, 1319, 1318, 1379, 1319, 1319, 1379, 1380, - 1379, 1440, 1380, 1380, 1440, 1441, 1440, 1501, 1441, 1441, 1501, 1502, - 1501, 1562, 1502, 1502, 1562, 1563, 1562, 1623, 1563, 1563, 1623, 1624, - 1623, 1684, 1624, 1624, 1684, 1685, 1684, 1745, 1685, 1685, 1745, 1746, - 1745, 1806, 1746, 1746, 1806, 1807, 1806, 1867, 1807, 1807, 1867, 1868, - 1867, 1928, 1868, 1868, 1928, 1929, 1928, 1989, 1929, 1929, 1989, 1990, - 1989, 2050, 1990, 1990, 2050, 2051, 2050, 2111, 2051, 2051, 2111, 2112, - 2111, 2172, 2112, 2112, 2172, 2173, 2172, 2233, 2173, 2173, 2233, 2234, - 2233, 2294, 2234, 2234, 2294, 2295, 2294, 2355, 2295, 2295, 2355, 2356, - 2355, 2416, 2356, 2356, 2416, 2417, 2416, 2477, 2417, 2417, 2477, 2478, - 2477, 2538, 2478, 2478, 2538, 2539, 2538, 2599, 2539, 2539, 2599, 2600, - 2599, 2660, 2600, 2600, 2660, 2661, 2660, 2721, 2661, 2661, 2721, 2722, - 2721, 2782, 2722, 2722, 2782, 2783, 2782, 2843, 2783, 2783, 2843, 2844, - 2843, 2904, 2844, 2844, 2904, 2905, 2904, 2965, 2905, 2905, 2965, 2966, - 2965, 3026, 2966, 2966, 3026, 3027, 3026, 3087, 3027, 3027, 3087, 3088, - 3087, 3148, 3088, 3088, 3148, 3149, 3148, 3209, 3149, 3149, 3209, 3210, - 3209, 3270, 3210, 3210, 3270, 3271, 3270, 3331, 3271, 3271, 3331, 3332, - 3331, 3392, 3332, 3332, 3392, 3393, 3392, 3453, 3393, 3393, 3453, 3454, - 3453, 3514, 3454, 3454, 3514, 3515, 3514, 3575, 3515, 3515, 3575, 3576, - 3575, 3636, 3576, 3576, 3636, 3637, 3636, 3697, 3637, 3637, 3697, 3698, - 38, 99, 39, 39, 99, 100, 99, 160, 100, 100, 160, 161, - 160, 221, 161, 161, 221, 222, 221, 282, 222, 222, 282, 283, - 282, 343, 283, 283, 343, 344, 343, 404, 344, 344, 404, 405, - 404, 465, 405, 405, 465, 466, 465, 526, 466, 466, 526, 527, - 526, 587, 527, 527, 587, 588, 587, 648, 588, 588, 648, 649, - 648, 709, 649, 649, 709, 710, 709, 770, 710, 710, 770, 771, - 770, 831, 771, 771, 831, 832, 831, 892, 832, 832, 892, 893, - 892, 953, 893, 893, 953, 954, 953, 1014, 954, 954, 1014, 1015, - 1014, 1075, 1015, 1015, 1075, 1076, 1075, 1136, 1076, 1076, 1136, 1137, - 1136, 1197, 1137, 1137, 1197, 1198, 1197, 1258, 1198, 1198, 1258, 1259, - 1258, 1319, 1259, 1259, 1319, 1320, 1319, 1380, 1320, 1320, 1380, 1381, - 1380, 1441, 1381, 1381, 1441, 1442, 1441, 1502, 1442, 1442, 1502, 1503, - 1502, 1563, 1503, 1503, 1563, 1564, 1563, 1624, 1564, 1564, 1624, 1625, - 1624, 1685, 1625, 1625, 1685, 1686, 1685, 1746, 1686, 1686, 1746, 1747, - 1746, 1807, 1747, 1747, 1807, 1808, 1807, 1868, 1808, 1808, 1868, 1869, - 1868, 1929, 1869, 1869, 1929, 1930, 1929, 1990, 1930, 1930, 1990, 1991, - 1990, 2051, 1991, 1991, 2051, 2052, 2051, 2112, 2052, 2052, 2112, 2113, - 2112, 2173, 2113, 2113, 2173, 2174, 2173, 2234, 2174, 2174, 2234, 2235, - 2234, 2295, 2235, 2235, 2295, 2296, 2295, 2356, 2296, 2296, 2356, 2357, - 2356, 2417, 2357, 2357, 2417, 2418, 2417, 2478, 2418, 2418, 2478, 2479, - 2478, 2539, 2479, 2479, 2539, 2540, 2539, 2600, 2540, 2540, 2600, 2601, - 2600, 2661, 2601, 2601, 2661, 2662, 2661, 2722, 2662, 2662, 2722, 2723, - 2722, 2783, 2723, 2723, 2783, 2784, 2783, 2844, 2784, 2784, 2844, 2845, - 2844, 2905, 2845, 2845, 2905, 2906, 2905, 2966, 2906, 2906, 2966, 2967, - 2966, 3027, 2967, 2967, 3027, 3028, 3027, 3088, 3028, 3028, 3088, 3089, - 3088, 3149, 3089, 3089, 3149, 3150, 3149, 3210, 3150, 3150, 3210, 3211, - 3210, 3271, 3211, 3211, 3271, 3272, 3271, 3332, 3272, 3272, 3332, 3333, - 3332, 3393, 3333, 3333, 3393, 3394, 3393, 3454, 3394, 3394, 3454, 3455, - 3454, 3515, 3455, 3455, 3515, 3516, 3515, 3576, 3516, 3516, 3576, 3577, - 3576, 3637, 3577, 3577, 3637, 3638, 3637, 3698, 3638, 3638, 3698, 3699, - 39, 100, 40, 40, 100, 101, 100, 161, 101, 101, 161, 162, - 161, 222, 162, 162, 222, 223, 222, 283, 223, 223, 283, 284, - 283, 344, 284, 284, 344, 345, 344, 405, 345, 345, 405, 406, - 405, 466, 406, 406, 466, 467, 466, 527, 467, 467, 527, 528, - 527, 588, 528, 528, 588, 589, 588, 649, 589, 589, 649, 650, - 649, 710, 650, 650, 710, 711, 710, 771, 711, 711, 771, 772, - 771, 832, 772, 772, 832, 833, 832, 893, 833, 833, 893, 894, - 893, 954, 894, 894, 954, 955, 954, 1015, 955, 955, 1015, 1016, - 1015, 1076, 1016, 1016, 1076, 1077, 1076, 1137, 1077, 1077, 1137, 1138, - 1137, 1198, 1138, 1138, 1198, 1199, 1198, 1259, 1199, 1199, 1259, 1260, - 1259, 1320, 1260, 1260, 1320, 1321, 1320, 1381, 1321, 1321, 1381, 1382, - 1381, 1442, 1382, 1382, 1442, 1443, 1442, 1503, 1443, 1443, 1503, 1504, - 1503, 1564, 1504, 1504, 1564, 1565, 1564, 1625, 1565, 1565, 1625, 1626, - 1625, 1686, 1626, 1626, 1686, 1687, 1686, 1747, 1687, 1687, 1747, 1748, - 1747, 1808, 1748, 1748, 1808, 1809, 1808, 1869, 1809, 1809, 1869, 1870, - 1869, 1930, 1870, 1870, 1930, 1931, 1930, 1991, 1931, 1931, 1991, 1992, - 1991, 2052, 1992, 1992, 2052, 2053, 2052, 2113, 2053, 2053, 2113, 2114, - 2113, 2174, 2114, 2114, 2174, 2175, 2174, 2235, 2175, 2175, 2235, 2236, - 2235, 2296, 2236, 2236, 2296, 2297, 2296, 2357, 2297, 2297, 2357, 2358, - 2357, 2418, 2358, 2358, 2418, 2419, 2418, 2479, 2419, 2419, 2479, 2480, - 2479, 2540, 2480, 2480, 2540, 2541, 2540, 2601, 2541, 2541, 2601, 2602, - 2601, 2662, 2602, 2602, 2662, 2663, 2662, 2723, 2663, 2663, 2723, 2724, - 2723, 2784, 2724, 2724, 2784, 2785, 2784, 2845, 2785, 2785, 2845, 2846, - 2845, 2906, 2846, 2846, 2906, 2907, 2906, 2967, 2907, 2907, 2967, 2968, - 2967, 3028, 2968, 2968, 3028, 3029, 3028, 3089, 3029, 3029, 3089, 3090, - 3089, 3150, 3090, 3090, 3150, 3151, 3150, 3211, 3151, 3151, 3211, 3212, - 3211, 3272, 3212, 3212, 3272, 3273, 3272, 3333, 3273, 3273, 3333, 3334, - 3333, 3394, 3334, 3334, 3394, 3395, 3394, 3455, 3395, 3395, 3455, 3456, - 3455, 3516, 3456, 3456, 3516, 3517, 3516, 3577, 3517, 3517, 3577, 3578, - 3577, 3638, 3578, 3578, 3638, 3639, 3638, 3699, 3639, 3639, 3699, 3700, - 40, 101, 41, 41, 101, 102, 101, 162, 102, 102, 162, 163, - 162, 223, 163, 163, 223, 224, 223, 284, 224, 224, 284, 285, - 284, 345, 285, 285, 345, 346, 345, 406, 346, 346, 406, 407, - 406, 467, 407, 407, 467, 468, 467, 528, 468, 468, 528, 529, - 528, 589, 529, 529, 589, 590, 589, 650, 590, 590, 650, 651, - 650, 711, 651, 651, 711, 712, 711, 772, 712, 712, 772, 773, - 772, 833, 773, 773, 833, 834, 833, 894, 834, 834, 894, 895, - 894, 955, 895, 895, 955, 956, 955, 1016, 956, 956, 1016, 1017, - 1016, 1077, 1017, 1017, 1077, 1078, 1077, 1138, 1078, 1078, 1138, 1139, - 1138, 1199, 1139, 1139, 1199, 1200, 1199, 1260, 1200, 1200, 1260, 1261, - 1260, 1321, 1261, 1261, 1321, 1322, 1321, 1382, 1322, 1322, 1382, 1383, - 1382, 1443, 1383, 1383, 1443, 1444, 1443, 1504, 1444, 1444, 1504, 1505, - 1504, 1565, 1505, 1505, 1565, 1566, 1565, 1626, 1566, 1566, 1626, 1627, - 1626, 1687, 1627, 1627, 1687, 1688, 1687, 1748, 1688, 1688, 1748, 1749, - 1748, 1809, 1749, 1749, 1809, 1810, 1809, 1870, 1810, 1810, 1870, 1871, - 1870, 1931, 1871, 1871, 1931, 1932, 1931, 1992, 1932, 1932, 1992, 1993, - 1992, 2053, 1993, 1993, 2053, 2054, 2053, 2114, 2054, 2054, 2114, 2115, - 2114, 2175, 2115, 2115, 2175, 2176, 2175, 2236, 2176, 2176, 2236, 2237, - 2236, 2297, 2237, 2237, 2297, 2298, 2297, 2358, 2298, 2298, 2358, 2359, - 2358, 2419, 2359, 2359, 2419, 2420, 2419, 2480, 2420, 2420, 2480, 2481, - 2480, 2541, 2481, 2481, 2541, 2542, 2541, 2602, 2542, 2542, 2602, 2603, - 2602, 2663, 2603, 2603, 2663, 2664, 2663, 2724, 2664, 2664, 2724, 2725, - 2724, 2785, 2725, 2725, 2785, 2786, 2785, 2846, 2786, 2786, 2846, 2847, - 2846, 2907, 2847, 2847, 2907, 2908, 2907, 2968, 2908, 2908, 2968, 2969, - 2968, 3029, 2969, 2969, 3029, 3030, 3029, 3090, 3030, 3030, 3090, 3091, - 3090, 3151, 3091, 3091, 3151, 3152, 3151, 3212, 3152, 3152, 3212, 3213, - 3212, 3273, 3213, 3213, 3273, 3274, 3273, 3334, 3274, 3274, 3334, 3335, - 3334, 3395, 3335, 3335, 3395, 3396, 3395, 3456, 3396, 3396, 3456, 3457, - 3456, 3517, 3457, 3457, 3517, 3518, 3517, 3578, 3518, 3518, 3578, 3579, - 3578, 3639, 3579, 3579, 3639, 3640, 3639, 3700, 3640, 3640, 3700, 3701, - 41, 102, 42, 42, 102, 103, 102, 163, 103, 103, 163, 164, - 163, 224, 164, 164, 224, 225, 224, 285, 225, 225, 285, 286, - 285, 346, 286, 286, 346, 347, 346, 407, 347, 347, 407, 408, - 407, 468, 408, 408, 468, 469, 468, 529, 469, 469, 529, 530, - 529, 590, 530, 530, 590, 591, 590, 651, 591, 591, 651, 652, - 651, 712, 652, 652, 712, 713, 712, 773, 713, 713, 773, 774, - 773, 834, 774, 774, 834, 835, 834, 895, 835, 835, 895, 896, - 895, 956, 896, 896, 956, 957, 956, 1017, 957, 957, 1017, 1018, - 1017, 1078, 1018, 1018, 1078, 1079, 1078, 1139, 1079, 1079, 1139, 1140, - 1139, 1200, 1140, 1140, 1200, 1201, 1200, 1261, 1201, 1201, 1261, 1262, - 1261, 1322, 1262, 1262, 1322, 1323, 1322, 1383, 1323, 1323, 1383, 1384, - 1383, 1444, 1384, 1384, 1444, 1445, 1444, 1505, 1445, 1445, 1505, 1506, - 1505, 1566, 1506, 1506, 1566, 1567, 1566, 1627, 1567, 1567, 1627, 1628, - 1627, 1688, 1628, 1628, 1688, 1689, 1688, 1749, 1689, 1689, 1749, 1750, - 1749, 1810, 1750, 1750, 1810, 1811, 1810, 1871, 1811, 1811, 1871, 1872, - 1871, 1932, 1872, 1872, 1932, 1933, 1932, 1993, 1933, 1933, 1993, 1994, - 1993, 2054, 1994, 1994, 2054, 2055, 2054, 2115, 2055, 2055, 2115, 2116, - 2115, 2176, 2116, 2116, 2176, 2177, 2176, 2237, 2177, 2177, 2237, 2238, - 2237, 2298, 2238, 2238, 2298, 2299, 2298, 2359, 2299, 2299, 2359, 2360, - 2359, 2420, 2360, 2360, 2420, 2421, 2420, 2481, 2421, 2421, 2481, 2482, - 2481, 2542, 2482, 2482, 2542, 2543, 2542, 2603, 2543, 2543, 2603, 2604, - 2603, 2664, 2604, 2604, 2664, 2665, 2664, 2725, 2665, 2665, 2725, 2726, - 2725, 2786, 2726, 2726, 2786, 2787, 2786, 2847, 2787, 2787, 2847, 2848, - 2847, 2908, 2848, 2848, 2908, 2909, 2908, 2969, 2909, 2909, 2969, 2970, - 2969, 3030, 2970, 2970, 3030, 3031, 3030, 3091, 3031, 3031, 3091, 3092, - 3091, 3152, 3092, 3092, 3152, 3153, 3152, 3213, 3153, 3153, 3213, 3214, - 3213, 3274, 3214, 3214, 3274, 3275, 3274, 3335, 3275, 3275, 3335, 3336, - 3335, 3396, 3336, 3336, 3396, 3397, 3396, 3457, 3397, 3397, 3457, 3458, - 3457, 3518, 3458, 3458, 3518, 3519, 3518, 3579, 3519, 3519, 3579, 3580, - 3579, 3640, 3580, 3580, 3640, 3641, 3640, 3701, 3641, 3641, 3701, 3702, - 42, 103, 43, 43, 103, 104, 103, 164, 104, 104, 164, 165, - 164, 225, 165, 165, 225, 226, 225, 286, 226, 226, 286, 287, - 286, 347, 287, 287, 347, 348, 347, 408, 348, 348, 408, 409, - 408, 469, 409, 409, 469, 470, 469, 530, 470, 470, 530, 531, - 530, 591, 531, 531, 591, 592, 591, 652, 592, 592, 652, 653, - 652, 713, 653, 653, 713, 714, 713, 774, 714, 714, 774, 775, - 774, 835, 775, 775, 835, 836, 835, 896, 836, 836, 896, 897, - 896, 957, 897, 897, 957, 958, 957, 1018, 958, 958, 1018, 1019, - 1018, 1079, 1019, 1019, 1079, 1080, 1079, 1140, 1080, 1080, 1140, 1141, - 1140, 1201, 1141, 1141, 1201, 1202, 1201, 1262, 1202, 1202, 1262, 1263, - 1262, 1323, 1263, 1263, 1323, 1324, 1323, 1384, 1324, 1324, 1384, 1385, - 1384, 1445, 1385, 1385, 1445, 1446, 1445, 1506, 1446, 1446, 1506, 1507, - 1506, 1567, 1507, 1507, 1567, 1568, 1567, 1628, 1568, 1568, 1628, 1629, - 1628, 1689, 1629, 1629, 1689, 1690, 1689, 1750, 1690, 1690, 1750, 1751, - 1750, 1811, 1751, 1751, 1811, 1812, 1811, 1872, 1812, 1812, 1872, 1873, - 1872, 1933, 1873, 1873, 1933, 1934, 1933, 1994, 1934, 1934, 1994, 1995, - 1994, 2055, 1995, 1995, 2055, 2056, 2055, 2116, 2056, 2056, 2116, 2117, - 2116, 2177, 2117, 2117, 2177, 2178, 2177, 2238, 2178, 2178, 2238, 2239, - 2238, 2299, 2239, 2239, 2299, 2300, 2299, 2360, 2300, 2300, 2360, 2361, - 2360, 2421, 2361, 2361, 2421, 2422, 2421, 2482, 2422, 2422, 2482, 2483, - 2482, 2543, 2483, 2483, 2543, 2544, 2543, 2604, 2544, 2544, 2604, 2605, - 2604, 2665, 2605, 2605, 2665, 2666, 2665, 2726, 2666, 2666, 2726, 2727, - 2726, 2787, 2727, 2727, 2787, 2788, 2787, 2848, 2788, 2788, 2848, 2849, - 2848, 2909, 2849, 2849, 2909, 2910, 2909, 2970, 2910, 2910, 2970, 2971, - 2970, 3031, 2971, 2971, 3031, 3032, 3031, 3092, 3032, 3032, 3092, 3093, - 3092, 3153, 3093, 3093, 3153, 3154, 3153, 3214, 3154, 3154, 3214, 3215, - 3214, 3275, 3215, 3215, 3275, 3276, 3275, 3336, 3276, 3276, 3336, 3337, - 3336, 3397, 3337, 3337, 3397, 3398, 3397, 3458, 3398, 3398, 3458, 3459, - 3458, 3519, 3459, 3459, 3519, 3520, 3519, 3580, 3520, 3520, 3580, 3581, - 3580, 3641, 3581, 3581, 3641, 3642, 3641, 3702, 3642, 3642, 3702, 3703, - 43, 104, 44, 44, 104, 105, 104, 165, 105, 105, 165, 166, - 165, 226, 166, 166, 226, 227, 226, 287, 227, 227, 287, 288, - 287, 348, 288, 288, 348, 349, 348, 409, 349, 349, 409, 410, - 409, 470, 410, 410, 470, 471, 470, 531, 471, 471, 531, 532, - 531, 592, 532, 532, 592, 593, 592, 653, 593, 593, 653, 654, - 653, 714, 654, 654, 714, 715, 714, 775, 715, 715, 775, 776, - 775, 836, 776, 776, 836, 837, 836, 897, 837, 837, 897, 898, - 897, 958, 898, 898, 958, 959, 958, 1019, 959, 959, 1019, 1020, - 1019, 1080, 1020, 1020, 1080, 1081, 1080, 1141, 1081, 1081, 1141, 1142, - 1141, 1202, 1142, 1142, 1202, 1203, 1202, 1263, 1203, 1203, 1263, 1264, - 1263, 1324, 1264, 1264, 1324, 1325, 1324, 1385, 1325, 1325, 1385, 1386, - 1385, 1446, 1386, 1386, 1446, 1447, 1446, 1507, 1447, 1447, 1507, 1508, - 1507, 1568, 1508, 1508, 1568, 1569, 1568, 1629, 1569, 1569, 1629, 1630, - 1629, 1690, 1630, 1630, 1690, 1691, 1690, 1751, 1691, 1691, 1751, 1752, - 1751, 1812, 1752, 1752, 1812, 1813, 1812, 1873, 1813, 1813, 1873, 1874, - 1873, 1934, 1874, 1874, 1934, 1935, 1934, 1995, 1935, 1935, 1995, 1996, - 1995, 2056, 1996, 1996, 2056, 2057, 2056, 2117, 2057, 2057, 2117, 2118, - 2117, 2178, 2118, 2118, 2178, 2179, 2178, 2239, 2179, 2179, 2239, 2240, - 2239, 2300, 2240, 2240, 2300, 2301, 2300, 2361, 2301, 2301, 2361, 2362, - 2361, 2422, 2362, 2362, 2422, 2423, 2422, 2483, 2423, 2423, 2483, 2484, - 2483, 2544, 2484, 2484, 2544, 2545, 2544, 2605, 2545, 2545, 2605, 2606, - 2605, 2666, 2606, 2606, 2666, 2667, 2666, 2727, 2667, 2667, 2727, 2728, - 2727, 2788, 2728, 2728, 2788, 2789, 2788, 2849, 2789, 2789, 2849, 2850, - 2849, 2910, 2850, 2850, 2910, 2911, 2910, 2971, 2911, 2911, 2971, 2972, - 2971, 3032, 2972, 2972, 3032, 3033, 3032, 3093, 3033, 3033, 3093, 3094, - 3093, 3154, 3094, 3094, 3154, 3155, 3154, 3215, 3155, 3155, 3215, 3216, - 3215, 3276, 3216, 3216, 3276, 3277, 3276, 3337, 3277, 3277, 3337, 3338, - 3337, 3398, 3338, 3338, 3398, 3399, 3398, 3459, 3399, 3399, 3459, 3460, - 3459, 3520, 3460, 3460, 3520, 3521, 3520, 3581, 3521, 3521, 3581, 3582, - 3581, 3642, 3582, 3582, 3642, 3643, 3642, 3703, 3643, 3643, 3703, 3704, - 44, 105, 45, 45, 105, 106, 105, 166, 106, 106, 166, 167, - 166, 227, 167, 167, 227, 228, 227, 288, 228, 228, 288, 289, - 288, 349, 289, 289, 349, 350, 349, 410, 350, 350, 410, 411, - 410, 471, 411, 411, 471, 472, 471, 532, 472, 472, 532, 533, - 532, 593, 533, 533, 593, 594, 593, 654, 594, 594, 654, 655, - 654, 715, 655, 655, 715, 716, 715, 776, 716, 716, 776, 777, - 776, 837, 777, 777, 837, 838, 837, 898, 838, 838, 898, 899, - 898, 959, 899, 899, 959, 960, 959, 1020, 960, 960, 1020, 1021, - 1020, 1081, 1021, 1021, 1081, 1082, 1081, 1142, 1082, 1082, 1142, 1143, - 1142, 1203, 1143, 1143, 1203, 1204, 1203, 1264, 1204, 1204, 1264, 1265, - 1264, 1325, 1265, 1265, 1325, 1326, 1325, 1386, 1326, 1326, 1386, 1387, - 1386, 1447, 1387, 1387, 1447, 1448, 1447, 1508, 1448, 1448, 1508, 1509, - 1508, 1569, 1509, 1509, 1569, 1570, 1569, 1630, 1570, 1570, 1630, 1631, - 1630, 1691, 1631, 1631, 1691, 1692, 1691, 1752, 1692, 1692, 1752, 1753, - 1752, 1813, 1753, 1753, 1813, 1814, 1813, 1874, 1814, 1814, 1874, 1875, - 1874, 1935, 1875, 1875, 1935, 1936, 1935, 1996, 1936, 1936, 1996, 1997, - 1996, 2057, 1997, 1997, 2057, 2058, 2057, 2118, 2058, 2058, 2118, 2119, - 2118, 2179, 2119, 2119, 2179, 2180, 2179, 2240, 2180, 2180, 2240, 2241, - 2240, 2301, 2241, 2241, 2301, 2302, 2301, 2362, 2302, 2302, 2362, 2363, - 2362, 2423, 2363, 2363, 2423, 2424, 2423, 2484, 2424, 2424, 2484, 2485, - 2484, 2545, 2485, 2485, 2545, 2546, 2545, 2606, 2546, 2546, 2606, 2607, - 2606, 2667, 2607, 2607, 2667, 2668, 2667, 2728, 2668, 2668, 2728, 2729, - 2728, 2789, 2729, 2729, 2789, 2790, 2789, 2850, 2790, 2790, 2850, 2851, - 2850, 2911, 2851, 2851, 2911, 2912, 2911, 2972, 2912, 2912, 2972, 2973, - 2972, 3033, 2973, 2973, 3033, 3034, 3033, 3094, 3034, 3034, 3094, 3095, - 3094, 3155, 3095, 3095, 3155, 3156, 3155, 3216, 3156, 3156, 3216, 3217, - 3216, 3277, 3217, 3217, 3277, 3278, 3277, 3338, 3278, 3278, 3338, 3339, - 3338, 3399, 3339, 3339, 3399, 3400, 3399, 3460, 3400, 3400, 3460, 3461, - 3460, 3521, 3461, 3461, 3521, 3522, 3521, 3582, 3522, 3522, 3582, 3583, - 3582, 3643, 3583, 3583, 3643, 3644, 3643, 3704, 3644, 3644, 3704, 3705, - 45, 106, 46, 46, 106, 107, 106, 167, 107, 107, 167, 168, - 167, 228, 168, 168, 228, 229, 228, 289, 229, 229, 289, 290, - 289, 350, 290, 290, 350, 351, 350, 411, 351, 351, 411, 412, - 411, 472, 412, 412, 472, 473, 472, 533, 473, 473, 533, 534, - 533, 594, 534, 534, 594, 595, 594, 655, 595, 595, 655, 656, - 655, 716, 656, 656, 716, 717, 716, 777, 717, 717, 777, 778, - 777, 838, 778, 778, 838, 839, 838, 899, 839, 839, 899, 900, - 899, 960, 900, 900, 960, 961, 960, 1021, 961, 961, 1021, 1022, - 1021, 1082, 1022, 1022, 1082, 1083, 1082, 1143, 1083, 1083, 1143, 1144, - 1143, 1204, 1144, 1144, 1204, 1205, 1204, 1265, 1205, 1205, 1265, 1266, - 1265, 1326, 1266, 1266, 1326, 1327, 1326, 1387, 1327, 1327, 1387, 1388, - 1387, 1448, 1388, 1388, 1448, 1449, 1448, 1509, 1449, 1449, 1509, 1510, - 1509, 1570, 1510, 1510, 1570, 1571, 1570, 1631, 1571, 1571, 1631, 1632, - 1631, 1692, 1632, 1632, 1692, 1693, 1692, 1753, 1693, 1693, 1753, 1754, - 1753, 1814, 1754, 1754, 1814, 1815, 1814, 1875, 1815, 1815, 1875, 1876, - 1875, 1936, 1876, 1876, 1936, 1937, 1936, 1997, 1937, 1937, 1997, 1998, - 1997, 2058, 1998, 1998, 2058, 2059, 2058, 2119, 2059, 2059, 2119, 2120, - 2119, 2180, 2120, 2120, 2180, 2181, 2180, 2241, 2181, 2181, 2241, 2242, - 2241, 2302, 2242, 2242, 2302, 2303, 2302, 2363, 2303, 2303, 2363, 2364, - 2363, 2424, 2364, 2364, 2424, 2425, 2424, 2485, 2425, 2425, 2485, 2486, - 2485, 2546, 2486, 2486, 2546, 2547, 2546, 2607, 2547, 2547, 2607, 2608, - 2607, 2668, 2608, 2608, 2668, 2669, 2668, 2729, 2669, 2669, 2729, 2730, - 2729, 2790, 2730, 2730, 2790, 2791, 2790, 2851, 2791, 2791, 2851, 2852, - 2851, 2912, 2852, 2852, 2912, 2913, 2912, 2973, 2913, 2913, 2973, 2974, - 2973, 3034, 2974, 2974, 3034, 3035, 3034, 3095, 3035, 3035, 3095, 3096, - 3095, 3156, 3096, 3096, 3156, 3157, 3156, 3217, 3157, 3157, 3217, 3218, - 3217, 3278, 3218, 3218, 3278, 3279, 3278, 3339, 3279, 3279, 3339, 3340, - 3339, 3400, 3340, 3340, 3400, 3401, 3400, 3461, 3401, 3401, 3461, 3462, - 3461, 3522, 3462, 3462, 3522, 3523, 3522, 3583, 3523, 3523, 3583, 3584, - 3583, 3644, 3584, 3584, 3644, 3645, 3644, 3705, 3645, 3645, 3705, 3706, - 46, 107, 47, 47, 107, 108, 107, 168, 108, 108, 168, 169, - 168, 229, 169, 169, 229, 230, 229, 290, 230, 230, 290, 291, - 290, 351, 291, 291, 351, 352, 351, 412, 352, 352, 412, 413, - 412, 473, 413, 413, 473, 474, 473, 534, 474, 474, 534, 535, - 534, 595, 535, 535, 595, 596, 595, 656, 596, 596, 656, 657, - 656, 717, 657, 657, 717, 718, 717, 778, 718, 718, 778, 779, - 778, 839, 779, 779, 839, 840, 839, 900, 840, 840, 900, 901, - 900, 961, 901, 901, 961, 962, 961, 1022, 962, 962, 1022, 1023, - 1022, 1083, 1023, 1023, 1083, 1084, 1083, 1144, 1084, 1084, 1144, 1145, - 1144, 1205, 1145, 1145, 1205, 1206, 1205, 1266, 1206, 1206, 1266, 1267, - 1266, 1327, 1267, 1267, 1327, 1328, 1327, 1388, 1328, 1328, 1388, 1389, - 1388, 1449, 1389, 1389, 1449, 1450, 1449, 1510, 1450, 1450, 1510, 1511, - 1510, 1571, 1511, 1511, 1571, 1572, 1571, 1632, 1572, 1572, 1632, 1633, - 1632, 1693, 1633, 1633, 1693, 1694, 1693, 1754, 1694, 1694, 1754, 1755, - 1754, 1815, 1755, 1755, 1815, 1816, 1815, 1876, 1816, 1816, 1876, 1877, - 1876, 1937, 1877, 1877, 1937, 1938, 1937, 1998, 1938, 1938, 1998, 1999, - 1998, 2059, 1999, 1999, 2059, 2060, 2059, 2120, 2060, 2060, 2120, 2121, - 2120, 2181, 2121, 2121, 2181, 2182, 2181, 2242, 2182, 2182, 2242, 2243, - 2242, 2303, 2243, 2243, 2303, 2304, 2303, 2364, 2304, 2304, 2364, 2365, - 2364, 2425, 2365, 2365, 2425, 2426, 2425, 2486, 2426, 2426, 2486, 2487, - 2486, 2547, 2487, 2487, 2547, 2548, 2547, 2608, 2548, 2548, 2608, 2609, - 2608, 2669, 2609, 2609, 2669, 2670, 2669, 2730, 2670, 2670, 2730, 2731, - 2730, 2791, 2731, 2731, 2791, 2792, 2791, 2852, 2792, 2792, 2852, 2853, - 2852, 2913, 2853, 2853, 2913, 2914, 2913, 2974, 2914, 2914, 2974, 2975, - 2974, 3035, 2975, 2975, 3035, 3036, 3035, 3096, 3036, 3036, 3096, 3097, - 3096, 3157, 3097, 3097, 3157, 3158, 3157, 3218, 3158, 3158, 3218, 3219, - 3218, 3279, 3219, 3219, 3279, 3280, 3279, 3340, 3280, 3280, 3340, 3341, - 3340, 3401, 3341, 3341, 3401, 3402, 3401, 3462, 3402, 3402, 3462, 3463, - 3462, 3523, 3463, 3463, 3523, 3524, 3523, 3584, 3524, 3524, 3584, 3585, - 3584, 3645, 3585, 3585, 3645, 3646, 3645, 3706, 3646, 3646, 3706, 3707, - 47, 108, 48, 48, 108, 109, 108, 169, 109, 109, 169, 170, - 169, 230, 170, 170, 230, 231, 230, 291, 231, 231, 291, 292, - 291, 352, 292, 292, 352, 353, 352, 413, 353, 353, 413, 414, - 413, 474, 414, 414, 474, 475, 474, 535, 475, 475, 535, 536, - 535, 596, 536, 536, 596, 597, 596, 657, 597, 597, 657, 658, - 657, 718, 658, 658, 718, 719, 718, 779, 719, 719, 779, 780, - 779, 840, 780, 780, 840, 841, 840, 901, 841, 841, 901, 902, - 901, 962, 902, 902, 962, 963, 962, 1023, 963, 963, 1023, 1024, - 1023, 1084, 1024, 1024, 1084, 1085, 1084, 1145, 1085, 1085, 1145, 1146, - 1145, 1206, 1146, 1146, 1206, 1207, 1206, 1267, 1207, 1207, 1267, 1268, - 1267, 1328, 1268, 1268, 1328, 1329, 1328, 1389, 1329, 1329, 1389, 1390, - 1389, 1450, 1390, 1390, 1450, 1451, 1450, 1511, 1451, 1451, 1511, 1512, - 1511, 1572, 1512, 1512, 1572, 1573, 1572, 1633, 1573, 1573, 1633, 1634, - 1633, 1694, 1634, 1634, 1694, 1695, 1694, 1755, 1695, 1695, 1755, 1756, - 1755, 1816, 1756, 1756, 1816, 1817, 1816, 1877, 1817, 1817, 1877, 1878, - 1877, 1938, 1878, 1878, 1938, 1939, 1938, 1999, 1939, 1939, 1999, 2000, - 1999, 2060, 2000, 2000, 2060, 2061, 2060, 2121, 2061, 2061, 2121, 2122, - 2121, 2182, 2122, 2122, 2182, 2183, 2182, 2243, 2183, 2183, 2243, 2244, - 2243, 2304, 2244, 2244, 2304, 2305, 2304, 2365, 2305, 2305, 2365, 2366, - 2365, 2426, 2366, 2366, 2426, 2427, 2426, 2487, 2427, 2427, 2487, 2488, - 2487, 2548, 2488, 2488, 2548, 2549, 2548, 2609, 2549, 2549, 2609, 2610, - 2609, 2670, 2610, 2610, 2670, 2671, 2670, 2731, 2671, 2671, 2731, 2732, - 2731, 2792, 2732, 2732, 2792, 2793, 2792, 2853, 2793, 2793, 2853, 2854, - 2853, 2914, 2854, 2854, 2914, 2915, 2914, 2975, 2915, 2915, 2975, 2976, - 2975, 3036, 2976, 2976, 3036, 3037, 3036, 3097, 3037, 3037, 3097, 3098, - 3097, 3158, 3098, 3098, 3158, 3159, 3158, 3219, 3159, 3159, 3219, 3220, - 3219, 3280, 3220, 3220, 3280, 3281, 3280, 3341, 3281, 3281, 3341, 3342, - 3341, 3402, 3342, 3342, 3402, 3403, 3402, 3463, 3403, 3403, 3463, 3464, - 3463, 3524, 3464, 3464, 3524, 3525, 3524, 3585, 3525, 3525, 3585, 3586, - 3585, 3646, 3586, 3586, 3646, 3647, 3646, 3707, 3647, 3647, 3707, 3708, - 48, 109, 49, 49, 109, 110, 109, 170, 110, 110, 170, 171, - 170, 231, 171, 171, 231, 232, 231, 292, 232, 232, 292, 293, - 292, 353, 293, 293, 353, 354, 353, 414, 354, 354, 414, 415, - 414, 475, 415, 415, 475, 476, 475, 536, 476, 476, 536, 537, - 536, 597, 537, 537, 597, 598, 597, 658, 598, 598, 658, 659, - 658, 719, 659, 659, 719, 720, 719, 780, 720, 720, 780, 781, - 780, 841, 781, 781, 841, 842, 841, 902, 842, 842, 902, 903, - 902, 963, 903, 903, 963, 964, 963, 1024, 964, 964, 1024, 1025, - 1024, 1085, 1025, 1025, 1085, 1086, 1085, 1146, 1086, 1086, 1146, 1147, - 1146, 1207, 1147, 1147, 1207, 1208, 1207, 1268, 1208, 1208, 1268, 1269, - 1268, 1329, 1269, 1269, 1329, 1330, 1329, 1390, 1330, 1330, 1390, 1391, - 1390, 1451, 1391, 1391, 1451, 1452, 1451, 1512, 1452, 1452, 1512, 1513, - 1512, 1573, 1513, 1513, 1573, 1574, 1573, 1634, 1574, 1574, 1634, 1635, - 1634, 1695, 1635, 1635, 1695, 1696, 1695, 1756, 1696, 1696, 1756, 1757, - 1756, 1817, 1757, 1757, 1817, 1818, 1817, 1878, 1818, 1818, 1878, 1879, - 1878, 1939, 1879, 1879, 1939, 1940, 1939, 2000, 1940, 1940, 2000, 2001, - 2000, 2061, 2001, 2001, 2061, 2062, 2061, 2122, 2062, 2062, 2122, 2123, - 2122, 2183, 2123, 2123, 2183, 2184, 2183, 2244, 2184, 2184, 2244, 2245, - 2244, 2305, 2245, 2245, 2305, 2306, 2305, 2366, 2306, 2306, 2366, 2367, - 2366, 2427, 2367, 2367, 2427, 2428, 2427, 2488, 2428, 2428, 2488, 2489, - 2488, 2549, 2489, 2489, 2549, 2550, 2549, 2610, 2550, 2550, 2610, 2611, - 2610, 2671, 2611, 2611, 2671, 2672, 2671, 2732, 2672, 2672, 2732, 2733, - 2732, 2793, 2733, 2733, 2793, 2794, 2793, 2854, 2794, 2794, 2854, 2855, - 2854, 2915, 2855, 2855, 2915, 2916, 2915, 2976, 2916, 2916, 2976, 2977, - 2976, 3037, 2977, 2977, 3037, 3038, 3037, 3098, 3038, 3038, 3098, 3099, - 3098, 3159, 3099, 3099, 3159, 3160, 3159, 3220, 3160, 3160, 3220, 3221, - 3220, 3281, 3221, 3221, 3281, 3282, 3281, 3342, 3282, 3282, 3342, 3343, - 3342, 3403, 3343, 3343, 3403, 3404, 3403, 3464, 3404, 3404, 3464, 3465, - 3464, 3525, 3465, 3465, 3525, 3526, 3525, 3586, 3526, 3526, 3586, 3587, - 3586, 3647, 3587, 3587, 3647, 3648, 3647, 3708, 3648, 3648, 3708, 3709, - 49, 110, 50, 50, 110, 111, 110, 171, 111, 111, 171, 172, - 171, 232, 172, 172, 232, 233, 232, 293, 233, 233, 293, 294, - 293, 354, 294, 294, 354, 355, 354, 415, 355, 355, 415, 416, - 415, 476, 416, 416, 476, 477, 476, 537, 477, 477, 537, 538, - 537, 598, 538, 538, 598, 599, 598, 659, 599, 599, 659, 660, - 659, 720, 660, 660, 720, 721, 720, 781, 721, 721, 781, 782, - 781, 842, 782, 782, 842, 843, 842, 903, 843, 843, 903, 904, - 903, 964, 904, 904, 964, 965, 964, 1025, 965, 965, 1025, 1026, - 1025, 1086, 1026, 1026, 1086, 1087, 1086, 1147, 1087, 1087, 1147, 1148, - 1147, 1208, 1148, 1148, 1208, 1209, 1208, 1269, 1209, 1209, 1269, 1270, - 1269, 1330, 1270, 1270, 1330, 1331, 1330, 1391, 1331, 1331, 1391, 1392, - 1391, 1452, 1392, 1392, 1452, 1453, 1452, 1513, 1453, 1453, 1513, 1514, - 1513, 1574, 1514, 1514, 1574, 1575, 1574, 1635, 1575, 1575, 1635, 1636, - 1635, 1696, 1636, 1636, 1696, 1697, 1696, 1757, 1697, 1697, 1757, 1758, - 1757, 1818, 1758, 1758, 1818, 1819, 1818, 1879, 1819, 1819, 1879, 1880, - 1879, 1940, 1880, 1880, 1940, 1941, 1940, 2001, 1941, 1941, 2001, 2002, - 2001, 2062, 2002, 2002, 2062, 2063, 2062, 2123, 2063, 2063, 2123, 2124, - 2123, 2184, 2124, 2124, 2184, 2185, 2184, 2245, 2185, 2185, 2245, 2246, - 2245, 2306, 2246, 2246, 2306, 2307, 2306, 2367, 2307, 2307, 2367, 2368, - 2367, 2428, 2368, 2368, 2428, 2429, 2428, 2489, 2429, 2429, 2489, 2490, - 2489, 2550, 2490, 2490, 2550, 2551, 2550, 2611, 2551, 2551, 2611, 2612, - 2611, 2672, 2612, 2612, 2672, 2673, 2672, 2733, 2673, 2673, 2733, 2734, - 2733, 2794, 2734, 2734, 2794, 2795, 2794, 2855, 2795, 2795, 2855, 2856, - 2855, 2916, 2856, 2856, 2916, 2917, 2916, 2977, 2917, 2917, 2977, 2978, - 2977, 3038, 2978, 2978, 3038, 3039, 3038, 3099, 3039, 3039, 3099, 3100, - 3099, 3160, 3100, 3100, 3160, 3161, 3160, 3221, 3161, 3161, 3221, 3222, - 3221, 3282, 3222, 3222, 3282, 3283, 3282, 3343, 3283, 3283, 3343, 3344, - 3343, 3404, 3344, 3344, 3404, 3405, 3404, 3465, 3405, 3405, 3465, 3466, - 3465, 3526, 3466, 3466, 3526, 3527, 3526, 3587, 3527, 3527, 3587, 3588, - 3587, 3648, 3588, 3588, 3648, 3649, 3648, 3709, 3649, 3649, 3709, 3710, - 50, 111, 51, 51, 111, 112, 111, 172, 112, 112, 172, 173, - 172, 233, 173, 173, 233, 234, 233, 294, 234, 234, 294, 295, - 294, 355, 295, 295, 355, 356, 355, 416, 356, 356, 416, 417, - 416, 477, 417, 417, 477, 478, 477, 538, 478, 478, 538, 539, - 538, 599, 539, 539, 599, 600, 599, 660, 600, 600, 660, 661, - 660, 721, 661, 661, 721, 722, 721, 782, 722, 722, 782, 783, - 782, 843, 783, 783, 843, 844, 843, 904, 844, 844, 904, 905, - 904, 965, 905, 905, 965, 966, 965, 1026, 966, 966, 1026, 1027, - 1026, 1087, 1027, 1027, 1087, 1088, 1087, 1148, 1088, 1088, 1148, 1149, - 1148, 1209, 1149, 1149, 1209, 1210, 1209, 1270, 1210, 1210, 1270, 1271, - 1270, 1331, 1271, 1271, 1331, 1332, 1331, 1392, 1332, 1332, 1392, 1393, - 1392, 1453, 1393, 1393, 1453, 1454, 1453, 1514, 1454, 1454, 1514, 1515, - 1514, 1575, 1515, 1515, 1575, 1576, 1575, 1636, 1576, 1576, 1636, 1637, - 1636, 1697, 1637, 1637, 1697, 1698, 1697, 1758, 1698, 1698, 1758, 1759, - 1758, 1819, 1759, 1759, 1819, 1820, 1819, 1880, 1820, 1820, 1880, 1881, - 1880, 1941, 1881, 1881, 1941, 1942, 1941, 2002, 1942, 1942, 2002, 2003, - 2002, 2063, 2003, 2003, 2063, 2064, 2063, 2124, 2064, 2064, 2124, 2125, - 2124, 2185, 2125, 2125, 2185, 2186, 2185, 2246, 2186, 2186, 2246, 2247, - 2246, 2307, 2247, 2247, 2307, 2308, 2307, 2368, 2308, 2308, 2368, 2369, - 2368, 2429, 2369, 2369, 2429, 2430, 2429, 2490, 2430, 2430, 2490, 2491, - 2490, 2551, 2491, 2491, 2551, 2552, 2551, 2612, 2552, 2552, 2612, 2613, - 2612, 2673, 2613, 2613, 2673, 2674, 2673, 2734, 2674, 2674, 2734, 2735, - 2734, 2795, 2735, 2735, 2795, 2796, 2795, 2856, 2796, 2796, 2856, 2857, - 2856, 2917, 2857, 2857, 2917, 2918, 2917, 2978, 2918, 2918, 2978, 2979, - 2978, 3039, 2979, 2979, 3039, 3040, 3039, 3100, 3040, 3040, 3100, 3101, - 3100, 3161, 3101, 3101, 3161, 3162, 3161, 3222, 3162, 3162, 3222, 3223, - 3222, 3283, 3223, 3223, 3283, 3284, 3283, 3344, 3284, 3284, 3344, 3345, - 3344, 3405, 3345, 3345, 3405, 3406, 3405, 3466, 3406, 3406, 3466, 3467, - 3466, 3527, 3467, 3467, 3527, 3528, 3527, 3588, 3528, 3528, 3588, 3589, - 3588, 3649, 3589, 3589, 3649, 3650, 3649, 3710, 3650, 3650, 3710, 3711, - 51, 112, 52, 52, 112, 113, 112, 173, 113, 113, 173, 174, - 173, 234, 174, 174, 234, 235, 234, 295, 235, 235, 295, 296, - 295, 356, 296, 296, 356, 357, 356, 417, 357, 357, 417, 418, - 417, 478, 418, 418, 478, 479, 478, 539, 479, 479, 539, 540, - 539, 600, 540, 540, 600, 601, 600, 661, 601, 601, 661, 662, - 661, 722, 662, 662, 722, 723, 722, 783, 723, 723, 783, 784, - 783, 844, 784, 784, 844, 845, 844, 905, 845, 845, 905, 906, - 905, 966, 906, 906, 966, 967, 966, 1027, 967, 967, 1027, 1028, - 1027, 1088, 1028, 1028, 1088, 1089, 1088, 1149, 1089, 1089, 1149, 1150, - 1149, 1210, 1150, 1150, 1210, 1211, 1210, 1271, 1211, 1211, 1271, 1272, - 1271, 1332, 1272, 1272, 1332, 1333, 1332, 1393, 1333, 1333, 1393, 1394, - 1393, 1454, 1394, 1394, 1454, 1455, 1454, 1515, 1455, 1455, 1515, 1516, - 1515, 1576, 1516, 1516, 1576, 1577, 1576, 1637, 1577, 1577, 1637, 1638, - 1637, 1698, 1638, 1638, 1698, 1699, 1698, 1759, 1699, 1699, 1759, 1760, - 1759, 1820, 1760, 1760, 1820, 1821, 1820, 1881, 1821, 1821, 1881, 1882, - 1881, 1942, 1882, 1882, 1942, 1943, 1942, 2003, 1943, 1943, 2003, 2004, - 2003, 2064, 2004, 2004, 2064, 2065, 2064, 2125, 2065, 2065, 2125, 2126, - 2125, 2186, 2126, 2126, 2186, 2187, 2186, 2247, 2187, 2187, 2247, 2248, - 2247, 2308, 2248, 2248, 2308, 2309, 2308, 2369, 2309, 2309, 2369, 2370, - 2369, 2430, 2370, 2370, 2430, 2431, 2430, 2491, 2431, 2431, 2491, 2492, - 2491, 2552, 2492, 2492, 2552, 2553, 2552, 2613, 2553, 2553, 2613, 2614, - 2613, 2674, 2614, 2614, 2674, 2675, 2674, 2735, 2675, 2675, 2735, 2736, - 2735, 2796, 2736, 2736, 2796, 2797, 2796, 2857, 2797, 2797, 2857, 2858, - 2857, 2918, 2858, 2858, 2918, 2919, 2918, 2979, 2919, 2919, 2979, 2980, - 2979, 3040, 2980, 2980, 3040, 3041, 3040, 3101, 3041, 3041, 3101, 3102, - 3101, 3162, 3102, 3102, 3162, 3163, 3162, 3223, 3163, 3163, 3223, 3224, - 3223, 3284, 3224, 3224, 3284, 3285, 3284, 3345, 3285, 3285, 3345, 3346, - 3345, 3406, 3346, 3346, 3406, 3407, 3406, 3467, 3407, 3407, 3467, 3468, - 3467, 3528, 3468, 3468, 3528, 3529, 3528, 3589, 3529, 3529, 3589, 3590, - 3589, 3650, 3590, 3590, 3650, 3651, 3650, 3711, 3651, 3651, 3711, 3712, - 52, 113, 53, 53, 113, 114, 113, 174, 114, 114, 174, 175, - 174, 235, 175, 175, 235, 236, 235, 296, 236, 236, 296, 297, - 296, 357, 297, 297, 357, 358, 357, 418, 358, 358, 418, 419, - 418, 479, 419, 419, 479, 480, 479, 540, 480, 480, 540, 541, - 540, 601, 541, 541, 601, 602, 601, 662, 602, 602, 662, 663, - 662, 723, 663, 663, 723, 724, 723, 784, 724, 724, 784, 785, - 784, 845, 785, 785, 845, 846, 845, 906, 846, 846, 906, 907, - 906, 967, 907, 907, 967, 968, 967, 1028, 968, 968, 1028, 1029, - 1028, 1089, 1029, 1029, 1089, 1090, 1089, 1150, 1090, 1090, 1150, 1151, - 1150, 1211, 1151, 1151, 1211, 1212, 1211, 1272, 1212, 1212, 1272, 1273, - 1272, 1333, 1273, 1273, 1333, 1334, 1333, 1394, 1334, 1334, 1394, 1395, - 1394, 1455, 1395, 1395, 1455, 1456, 1455, 1516, 1456, 1456, 1516, 1517, - 1516, 1577, 1517, 1517, 1577, 1578, 1577, 1638, 1578, 1578, 1638, 1639, - 1638, 1699, 1639, 1639, 1699, 1700, 1699, 1760, 1700, 1700, 1760, 1761, - 1760, 1821, 1761, 1761, 1821, 1822, 1821, 1882, 1822, 1822, 1882, 1883, - 1882, 1943, 1883, 1883, 1943, 1944, 1943, 2004, 1944, 1944, 2004, 2005, - 2004, 2065, 2005, 2005, 2065, 2066, 2065, 2126, 2066, 2066, 2126, 2127, - 2126, 2187, 2127, 2127, 2187, 2188, 2187, 2248, 2188, 2188, 2248, 2249, - 2248, 2309, 2249, 2249, 2309, 2310, 2309, 2370, 2310, 2310, 2370, 2371, - 2370, 2431, 2371, 2371, 2431, 2432, 2431, 2492, 2432, 2432, 2492, 2493, - 2492, 2553, 2493, 2493, 2553, 2554, 2553, 2614, 2554, 2554, 2614, 2615, - 2614, 2675, 2615, 2615, 2675, 2676, 2675, 2736, 2676, 2676, 2736, 2737, - 2736, 2797, 2737, 2737, 2797, 2798, 2797, 2858, 2798, 2798, 2858, 2859, - 2858, 2919, 2859, 2859, 2919, 2920, 2919, 2980, 2920, 2920, 2980, 2981, - 2980, 3041, 2981, 2981, 3041, 3042, 3041, 3102, 3042, 3042, 3102, 3103, - 3102, 3163, 3103, 3103, 3163, 3164, 3163, 3224, 3164, 3164, 3224, 3225, - 3224, 3285, 3225, 3225, 3285, 3286, 3285, 3346, 3286, 3286, 3346, 3347, - 3346, 3407, 3347, 3347, 3407, 3408, 3407, 3468, 3408, 3408, 3468, 3469, - 3468, 3529, 3469, 3469, 3529, 3530, 3529, 3590, 3530, 3530, 3590, 3591, - 3590, 3651, 3591, 3591, 3651, 3652, 3651, 3712, 3652, 3652, 3712, 3713, - 53, 114, 54, 54, 114, 115, 114, 175, 115, 115, 175, 176, - 175, 236, 176, 176, 236, 237, 236, 297, 237, 237, 297, 298, - 297, 358, 298, 298, 358, 359, 358, 419, 359, 359, 419, 420, - 419, 480, 420, 420, 480, 481, 480, 541, 481, 481, 541, 542, - 541, 602, 542, 542, 602, 603, 602, 663, 603, 603, 663, 664, - 663, 724, 664, 664, 724, 725, 724, 785, 725, 725, 785, 786, - 785, 846, 786, 786, 846, 847, 846, 907, 847, 847, 907, 908, - 907, 968, 908, 908, 968, 969, 968, 1029, 969, 969, 1029, 1030, - 1029, 1090, 1030, 1030, 1090, 1091, 1090, 1151, 1091, 1091, 1151, 1152, - 1151, 1212, 1152, 1152, 1212, 1213, 1212, 1273, 1213, 1213, 1273, 1274, - 1273, 1334, 1274, 1274, 1334, 1335, 1334, 1395, 1335, 1335, 1395, 1396, - 1395, 1456, 1396, 1396, 1456, 1457, 1456, 1517, 1457, 1457, 1517, 1518, - 1517, 1578, 1518, 1518, 1578, 1579, 1578, 1639, 1579, 1579, 1639, 1640, - 1639, 1700, 1640, 1640, 1700, 1701, 1700, 1761, 1701, 1701, 1761, 1762, - 1761, 1822, 1762, 1762, 1822, 1823, 1822, 1883, 1823, 1823, 1883, 1884, - 1883, 1944, 1884, 1884, 1944, 1945, 1944, 2005, 1945, 1945, 2005, 2006, - 2005, 2066, 2006, 2006, 2066, 2067, 2066, 2127, 2067, 2067, 2127, 2128, - 2127, 2188, 2128, 2128, 2188, 2189, 2188, 2249, 2189, 2189, 2249, 2250, - 2249, 2310, 2250, 2250, 2310, 2311, 2310, 2371, 2311, 2311, 2371, 2372, - 2371, 2432, 2372, 2372, 2432, 2433, 2432, 2493, 2433, 2433, 2493, 2494, - 2493, 2554, 2494, 2494, 2554, 2555, 2554, 2615, 2555, 2555, 2615, 2616, - 2615, 2676, 2616, 2616, 2676, 2677, 2676, 2737, 2677, 2677, 2737, 2738, - 2737, 2798, 2738, 2738, 2798, 2799, 2798, 2859, 2799, 2799, 2859, 2860, - 2859, 2920, 2860, 2860, 2920, 2921, 2920, 2981, 2921, 2921, 2981, 2982, - 2981, 3042, 2982, 2982, 3042, 3043, 3042, 3103, 3043, 3043, 3103, 3104, - 3103, 3164, 3104, 3104, 3164, 3165, 3164, 3225, 3165, 3165, 3225, 3226, - 3225, 3286, 3226, 3226, 3286, 3287, 3286, 3347, 3287, 3287, 3347, 3348, - 3347, 3408, 3348, 3348, 3408, 3409, 3408, 3469, 3409, 3409, 3469, 3470, - 3469, 3530, 3470, 3470, 3530, 3531, 3530, 3591, 3531, 3531, 3591, 3592, - 3591, 3652, 3592, 3592, 3652, 3653, 3652, 3713, 3653, 3653, 3713, 3714, - 54, 115, 55, 55, 115, 116, 115, 176, 116, 116, 176, 177, - 176, 237, 177, 177, 237, 238, 237, 298, 238, 238, 298, 299, - 298, 359, 299, 299, 359, 360, 359, 420, 360, 360, 420, 421, - 420, 481, 421, 421, 481, 482, 481, 542, 482, 482, 542, 543, - 542, 603, 543, 543, 603, 604, 603, 664, 604, 604, 664, 665, - 664, 725, 665, 665, 725, 726, 725, 786, 726, 726, 786, 787, - 786, 847, 787, 787, 847, 848, 847, 908, 848, 848, 908, 909, - 908, 969, 909, 909, 969, 970, 969, 1030, 970, 970, 1030, 1031, - 1030, 1091, 1031, 1031, 1091, 1092, 1091, 1152, 1092, 1092, 1152, 1153, - 1152, 1213, 1153, 1153, 1213, 1214, 1213, 1274, 1214, 1214, 1274, 1275, - 1274, 1335, 1275, 1275, 1335, 1336, 1335, 1396, 1336, 1336, 1396, 1397, - 1396, 1457, 1397, 1397, 1457, 1458, 1457, 1518, 1458, 1458, 1518, 1519, - 1518, 1579, 1519, 1519, 1579, 1580, 1579, 1640, 1580, 1580, 1640, 1641, - 1640, 1701, 1641, 1641, 1701, 1702, 1701, 1762, 1702, 1702, 1762, 1763, - 1762, 1823, 1763, 1763, 1823, 1824, 1823, 1884, 1824, 1824, 1884, 1885, - 1884, 1945, 1885, 1885, 1945, 1946, 1945, 2006, 1946, 1946, 2006, 2007, - 2006, 2067, 2007, 2007, 2067, 2068, 2067, 2128, 2068, 2068, 2128, 2129, - 2128, 2189, 2129, 2129, 2189, 2190, 2189, 2250, 2190, 2190, 2250, 2251, - 2250, 2311, 2251, 2251, 2311, 2312, 2311, 2372, 2312, 2312, 2372, 2373, - 2372, 2433, 2373, 2373, 2433, 2434, 2433, 2494, 2434, 2434, 2494, 2495, - 2494, 2555, 2495, 2495, 2555, 2556, 2555, 2616, 2556, 2556, 2616, 2617, - 2616, 2677, 2617, 2617, 2677, 2678, 2677, 2738, 2678, 2678, 2738, 2739, - 2738, 2799, 2739, 2739, 2799, 2800, 2799, 2860, 2800, 2800, 2860, 2861, - 2860, 2921, 2861, 2861, 2921, 2922, 2921, 2982, 2922, 2922, 2982, 2983, - 2982, 3043, 2983, 2983, 3043, 3044, 3043, 3104, 3044, 3044, 3104, 3105, - 3104, 3165, 3105, 3105, 3165, 3166, 3165, 3226, 3166, 3166, 3226, 3227, - 3226, 3287, 3227, 3227, 3287, 3288, 3287, 3348, 3288, 3288, 3348, 3349, - 3348, 3409, 3349, 3349, 3409, 3410, 3409, 3470, 3410, 3410, 3470, 3471, - 3470, 3531, 3471, 3471, 3531, 3532, 3531, 3592, 3532, 3532, 3592, 3593, - 3592, 3653, 3593, 3593, 3653, 3654, 3653, 3714, 3654, 3654, 3714, 3715, - 55, 116, 56, 56, 116, 117, 116, 177, 117, 117, 177, 178, - 177, 238, 178, 178, 238, 239, 238, 299, 239, 239, 299, 300, - 299, 360, 300, 300, 360, 361, 360, 421, 361, 361, 421, 422, - 421, 482, 422, 422, 482, 483, 482, 543, 483, 483, 543, 544, - 543, 604, 544, 544, 604, 605, 604, 665, 605, 605, 665, 666, - 665, 726, 666, 666, 726, 727, 726, 787, 727, 727, 787, 788, - 787, 848, 788, 788, 848, 849, 848, 909, 849, 849, 909, 910, - 909, 970, 910, 910, 970, 971, 970, 1031, 971, 971, 1031, 1032, - 1031, 1092, 1032, 1032, 1092, 1093, 1092, 1153, 1093, 1093, 1153, 1154, - 1153, 1214, 1154, 1154, 1214, 1215, 1214, 1275, 1215, 1215, 1275, 1276, - 1275, 1336, 1276, 1276, 1336, 1337, 1336, 1397, 1337, 1337, 1397, 1398, - 1397, 1458, 1398, 1398, 1458, 1459, 1458, 1519, 1459, 1459, 1519, 1520, - 1519, 1580, 1520, 1520, 1580, 1581, 1580, 1641, 1581, 1581, 1641, 1642, - 1641, 1702, 1642, 1642, 1702, 1703, 1702, 1763, 1703, 1703, 1763, 1764, - 1763, 1824, 1764, 1764, 1824, 1825, 1824, 1885, 1825, 1825, 1885, 1886, - 1885, 1946, 1886, 1886, 1946, 1947, 1946, 2007, 1947, 1947, 2007, 2008, - 2007, 2068, 2008, 2008, 2068, 2069, 2068, 2129, 2069, 2069, 2129, 2130, - 2129, 2190, 2130, 2130, 2190, 2191, 2190, 2251, 2191, 2191, 2251, 2252, - 2251, 2312, 2252, 2252, 2312, 2313, 2312, 2373, 2313, 2313, 2373, 2374, - 2373, 2434, 2374, 2374, 2434, 2435, 2434, 2495, 2435, 2435, 2495, 2496, - 2495, 2556, 2496, 2496, 2556, 2557, 2556, 2617, 2557, 2557, 2617, 2618, - 2617, 2678, 2618, 2618, 2678, 2679, 2678, 2739, 2679, 2679, 2739, 2740, - 2739, 2800, 2740, 2740, 2800, 2801, 2800, 2861, 2801, 2801, 2861, 2862, - 2861, 2922, 2862, 2862, 2922, 2923, 2922, 2983, 2923, 2923, 2983, 2984, - 2983, 3044, 2984, 2984, 3044, 3045, 3044, 3105, 3045, 3045, 3105, 3106, - 3105, 3166, 3106, 3106, 3166, 3167, 3166, 3227, 3167, 3167, 3227, 3228, - 3227, 3288, 3228, 3228, 3288, 3289, 3288, 3349, 3289, 3289, 3349, 3350, - 3349, 3410, 3350, 3350, 3410, 3411, 3410, 3471, 3411, 3411, 3471, 3472, - 3471, 3532, 3472, 3472, 3532, 3533, 3532, 3593, 3533, 3533, 3593, 3594, - 3593, 3654, 3594, 3594, 3654, 3655, 3654, 3715, 3655, 3655, 3715, 3716, - 56, 117, 57, 57, 117, 118, 117, 178, 118, 118, 178, 179, - 178, 239, 179, 179, 239, 240, 239, 300, 240, 240, 300, 301, - 300, 361, 301, 301, 361, 362, 361, 422, 362, 362, 422, 423, - 422, 483, 423, 423, 483, 484, 483, 544, 484, 484, 544, 545, - 544, 605, 545, 545, 605, 606, 605, 666, 606, 606, 666, 667, - 666, 727, 667, 667, 727, 728, 727, 788, 728, 728, 788, 789, - 788, 849, 789, 789, 849, 850, 849, 910, 850, 850, 910, 911, - 910, 971, 911, 911, 971, 972, 971, 1032, 972, 972, 1032, 1033, - 1032, 1093, 1033, 1033, 1093, 1094, 1093, 1154, 1094, 1094, 1154, 1155, - 1154, 1215, 1155, 1155, 1215, 1216, 1215, 1276, 1216, 1216, 1276, 1277, - 1276, 1337, 1277, 1277, 1337, 1338, 1337, 1398, 1338, 1338, 1398, 1399, - 1398, 1459, 1399, 1399, 1459, 1460, 1459, 1520, 1460, 1460, 1520, 1521, - 1520, 1581, 1521, 1521, 1581, 1582, 1581, 1642, 1582, 1582, 1642, 1643, - 1642, 1703, 1643, 1643, 1703, 1704, 1703, 1764, 1704, 1704, 1764, 1765, - 1764, 1825, 1765, 1765, 1825, 1826, 1825, 1886, 1826, 1826, 1886, 1887, - 1886, 1947, 1887, 1887, 1947, 1948, 1947, 2008, 1948, 1948, 2008, 2009, - 2008, 2069, 2009, 2009, 2069, 2070, 2069, 2130, 2070, 2070, 2130, 2131, - 2130, 2191, 2131, 2131, 2191, 2192, 2191, 2252, 2192, 2192, 2252, 2253, - 2252, 2313, 2253, 2253, 2313, 2314, 2313, 2374, 2314, 2314, 2374, 2375, - 2374, 2435, 2375, 2375, 2435, 2436, 2435, 2496, 2436, 2436, 2496, 2497, - 2496, 2557, 2497, 2497, 2557, 2558, 2557, 2618, 2558, 2558, 2618, 2619, - 2618, 2679, 2619, 2619, 2679, 2680, 2679, 2740, 2680, 2680, 2740, 2741, - 2740, 2801, 2741, 2741, 2801, 2802, 2801, 2862, 2802, 2802, 2862, 2863, - 2862, 2923, 2863, 2863, 2923, 2924, 2923, 2984, 2924, 2924, 2984, 2985, - 2984, 3045, 2985, 2985, 3045, 3046, 3045, 3106, 3046, 3046, 3106, 3107, - 3106, 3167, 3107, 3107, 3167, 3168, 3167, 3228, 3168, 3168, 3228, 3229, - 3228, 3289, 3229, 3229, 3289, 3290, 3289, 3350, 3290, 3290, 3350, 3351, - 3350, 3411, 3351, 3351, 3411, 3412, 3411, 3472, 3412, 3412, 3472, 3473, - 3472, 3533, 3473, 3473, 3533, 3534, 3533, 3594, 3534, 3534, 3594, 3595, - 3594, 3655, 3595, 3595, 3655, 3656, 3655, 3716, 3656, 3656, 3716, 3717, - 57, 118, 58, 58, 118, 119, 118, 179, 119, 119, 179, 180, - 179, 240, 180, 180, 240, 241, 240, 301, 241, 241, 301, 302, - 301, 362, 302, 302, 362, 363, 362, 423, 363, 363, 423, 424, - 423, 484, 424, 424, 484, 485, 484, 545, 485, 485, 545, 546, - 545, 606, 546, 546, 606, 607, 606, 667, 607, 607, 667, 668, - 667, 728, 668, 668, 728, 729, 728, 789, 729, 729, 789, 790, - 789, 850, 790, 790, 850, 851, 850, 911, 851, 851, 911, 912, - 911, 972, 912, 912, 972, 973, 972, 1033, 973, 973, 1033, 1034, - 1033, 1094, 1034, 1034, 1094, 1095, 1094, 1155, 1095, 1095, 1155, 1156, - 1155, 1216, 1156, 1156, 1216, 1217, 1216, 1277, 1217, 1217, 1277, 1278, - 1277, 1338, 1278, 1278, 1338, 1339, 1338, 1399, 1339, 1339, 1399, 1400, - 1399, 1460, 1400, 1400, 1460, 1461, 1460, 1521, 1461, 1461, 1521, 1522, - 1521, 1582, 1522, 1522, 1582, 1583, 1582, 1643, 1583, 1583, 1643, 1644, - 1643, 1704, 1644, 1644, 1704, 1705, 1704, 1765, 1705, 1705, 1765, 1766, - 1765, 1826, 1766, 1766, 1826, 1827, 1826, 1887, 1827, 1827, 1887, 1888, - 1887, 1948, 1888, 1888, 1948, 1949, 1948, 2009, 1949, 1949, 2009, 2010, - 2009, 2070, 2010, 2010, 2070, 2071, 2070, 2131, 2071, 2071, 2131, 2132, - 2131, 2192, 2132, 2132, 2192, 2193, 2192, 2253, 2193, 2193, 2253, 2254, - 2253, 2314, 2254, 2254, 2314, 2315, 2314, 2375, 2315, 2315, 2375, 2376, - 2375, 2436, 2376, 2376, 2436, 2437, 2436, 2497, 2437, 2437, 2497, 2498, - 2497, 2558, 2498, 2498, 2558, 2559, 2558, 2619, 2559, 2559, 2619, 2620, - 2619, 2680, 2620, 2620, 2680, 2681, 2680, 2741, 2681, 2681, 2741, 2742, - 2741, 2802, 2742, 2742, 2802, 2803, 2802, 2863, 2803, 2803, 2863, 2864, - 2863, 2924, 2864, 2864, 2924, 2925, 2924, 2985, 2925, 2925, 2985, 2986, - 2985, 3046, 2986, 2986, 3046, 3047, 3046, 3107, 3047, 3047, 3107, 3108, - 3107, 3168, 3108, 3108, 3168, 3169, 3168, 3229, 3169, 3169, 3229, 3230, - 3229, 3290, 3230, 3230, 3290, 3291, 3290, 3351, 3291, 3291, 3351, 3352, - 3351, 3412, 3352, 3352, 3412, 3413, 3412, 3473, 3413, 3413, 3473, 3474, - 3473, 3534, 3474, 3474, 3534, 3535, 3534, 3595, 3535, 3535, 3595, 3596, - 3595, 3656, 3596, 3596, 3656, 3657, 3656, 3717, 3657, 3657, 3717, 3718, - 58, 119, 59, 59, 119, 120, 119, 180, 120, 120, 180, 181, - 180, 241, 181, 181, 241, 242, 241, 302, 242, 242, 302, 303, - 302, 363, 303, 303, 363, 364, 363, 424, 364, 364, 424, 425, - 424, 485, 425, 425, 485, 486, 485, 546, 486, 486, 546, 547, - 546, 607, 547, 547, 607, 608, 607, 668, 608, 608, 668, 669, - 668, 729, 669, 669, 729, 730, 729, 790, 730, 730, 790, 791, - 790, 851, 791, 791, 851, 852, 851, 912, 852, 852, 912, 913, - 912, 973, 913, 913, 973, 974, 973, 1034, 974, 974, 1034, 1035, - 1034, 1095, 1035, 1035, 1095, 1096, 1095, 1156, 1096, 1096, 1156, 1157, - 1156, 1217, 1157, 1157, 1217, 1218, 1217, 1278, 1218, 1218, 1278, 1279, - 1278, 1339, 1279, 1279, 1339, 1340, 1339, 1400, 1340, 1340, 1400, 1401, - 1400, 1461, 1401, 1401, 1461, 1462, 1461, 1522, 1462, 1462, 1522, 1523, - 1522, 1583, 1523, 1523, 1583, 1584, 1583, 1644, 1584, 1584, 1644, 1645, - 1644, 1705, 1645, 1645, 1705, 1706, 1705, 1766, 1706, 1706, 1766, 1767, - 1766, 1827, 1767, 1767, 1827, 1828, 1827, 1888, 1828, 1828, 1888, 1889, - 1888, 1949, 1889, 1889, 1949, 1950, 1949, 2010, 1950, 1950, 2010, 2011, - 2010, 2071, 2011, 2011, 2071, 2072, 2071, 2132, 2072, 2072, 2132, 2133, - 2132, 2193, 2133, 2133, 2193, 2194, 2193, 2254, 2194, 2194, 2254, 2255, - 2254, 2315, 2255, 2255, 2315, 2316, 2315, 2376, 2316, 2316, 2376, 2377, - 2376, 2437, 2377, 2377, 2437, 2438, 2437, 2498, 2438, 2438, 2498, 2499, - 2498, 2559, 2499, 2499, 2559, 2560, 2559, 2620, 2560, 2560, 2620, 2621, - 2620, 2681, 2621, 2621, 2681, 2682, 2681, 2742, 2682, 2682, 2742, 2743, - 2742, 2803, 2743, 2743, 2803, 2804, 2803, 2864, 2804, 2804, 2864, 2865, - 2864, 2925, 2865, 2865, 2925, 2926, 2925, 2986, 2926, 2926, 2986, 2987, - 2986, 3047, 2987, 2987, 3047, 3048, 3047, 3108, 3048, 3048, 3108, 3109, - 3108, 3169, 3109, 3109, 3169, 3170, 3169, 3230, 3170, 3170, 3230, 3231, - 3230, 3291, 3231, 3231, 3291, 3292, 3291, 3352, 3292, 3292, 3352, 3353, - 3352, 3413, 3353, 3353, 3413, 3414, 3413, 3474, 3414, 3414, 3474, 3475, - 3474, 3535, 3475, 3475, 3535, 3536, 3535, 3596, 3536, 3536, 3596, 3597, - 3596, 3657, 3597, 3597, 3657, 3658, 3657, 3718, 3658, 3658, 3718, 3719, - 59, 120, 60, 60, 120, 121, 120, 181, 121, 121, 181, 182, - 181, 242, 182, 182, 242, 243, 242, 303, 243, 243, 303, 304, - 303, 364, 304, 304, 364, 365, 364, 425, 365, 365, 425, 426, - 425, 486, 426, 426, 486, 487, 486, 547, 487, 487, 547, 548, - 547, 608, 548, 548, 608, 609, 608, 669, 609, 609, 669, 670, - 669, 730, 670, 670, 730, 731, 730, 791, 731, 731, 791, 792, - 791, 852, 792, 792, 852, 853, 852, 913, 853, 853, 913, 914, - 913, 974, 914, 914, 974, 975, 974, 1035, 975, 975, 1035, 1036, - 1035, 1096, 1036, 1036, 1096, 1097, 1096, 1157, 1097, 1097, 1157, 1158, - 1157, 1218, 1158, 1158, 1218, 1219, 1218, 1279, 1219, 1219, 1279, 1280, - 1279, 1340, 1280, 1280, 1340, 1341, 1340, 1401, 1341, 1341, 1401, 1402, - 1401, 1462, 1402, 1402, 1462, 1463, 1462, 1523, 1463, 1463, 1523, 1524, - 1523, 1584, 1524, 1524, 1584, 1585, 1584, 1645, 1585, 1585, 1645, 1646, - 1645, 1706, 1646, 1646, 1706, 1707, 1706, 1767, 1707, 1707, 1767, 1768, - 1767, 1828, 1768, 1768, 1828, 1829, 1828, 1889, 1829, 1829, 1889, 1890, - 1889, 1950, 1890, 1890, 1950, 1951, 1950, 2011, 1951, 1951, 2011, 2012, - 2011, 2072, 2012, 2012, 2072, 2073, 2072, 2133, 2073, 2073, 2133, 2134, - 2133, 2194, 2134, 2134, 2194, 2195, 2194, 2255, 2195, 2195, 2255, 2256, - 2255, 2316, 2256, 2256, 2316, 2317, 2316, 2377, 2317, 2317, 2377, 2378, - 2377, 2438, 2378, 2378, 2438, 2439, 2438, 2499, 2439, 2439, 2499, 2500, - 2499, 2560, 2500, 2500, 2560, 2561, 2560, 2621, 2561, 2561, 2621, 2622, - 2621, 2682, 2622, 2622, 2682, 2683, 2682, 2743, 2683, 2683, 2743, 2744, - 2743, 2804, 2744, 2744, 2804, 2805, 2804, 2865, 2805, 2805, 2865, 2866, - 2865, 2926, 2866, 2866, 2926, 2927, 2926, 2987, 2927, 2927, 2987, 2988, - 2987, 3048, 2988, 2988, 3048, 3049, 3048, 3109, 3049, 3049, 3109, 3110, - 3109, 3170, 3110, 3110, 3170, 3171, 3170, 3231, 3171, 3171, 3231, 3232, - 3231, 3292, 3232, 3232, 3292, 3293, 3292, 3353, 3293, 3293, 3353, 3354, - 3353, 3414, 3354, 3354, 3414, 3415, 3414, 3475, 3415, 3415, 3475, 3476, - 3475, 3536, 3476, 3476, 3536, 3537, 3536, 3597, 3537, 3537, 3597, 3598, - 3597, 3658, 3598, 3598, 3658, 3659, 3658, 3719, 3659, 3659, 3719, 3720, -}; - -} /* namespace Pdraw */ - -#endif /* USE_GLES2 */ +/** + * Parrot Drones Awesome Video Viewer Library + * OpenGL ES 2.0 HMD distortion correction + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Extracted from FPVToolbox by Frédéric Bertolus + * https://github.com/niavok/fpvtoolbox + * and translated from Java to C++ + */ + +#ifdef USE_GLES2 + +# include + +namespace Pdraw { + +extern const uint32_t pdraw_gles2HmdIndices[21600] = { + 0, 61, 1, 1, 61, 62, 61, 122, 62, 62, 122, 123, + 122, 183, 123, 123, 183, 184, 183, 244, 184, 184, 244, 245, + 244, 305, 245, 245, 305, 306, 305, 366, 306, 306, 366, 367, + 366, 427, 367, 367, 427, 428, 427, 488, 428, 428, 488, 489, + 488, 549, 489, 489, 549, 550, 549, 610, 550, 550, 610, 611, + 610, 671, 611, 611, 671, 672, 671, 732, 672, 672, 732, 733, + 732, 793, 733, 733, 793, 794, 793, 854, 794, 794, 854, 855, + 854, 915, 855, 855, 915, 916, 915, 976, 916, 916, 976, 977, + 976, 1037, 977, 977, 1037, 1038, 1037, 1098, 1038, 1038, 1098, 1099, + 1098, 1159, 1099, 1099, 1159, 1160, 1159, 1220, 1160, 1160, 1220, 1221, + 1220, 1281, 1221, 1221, 1281, 1282, 1281, 1342, 1282, 1282, 1342, 1343, + 1342, 1403, 1343, 1343, 1403, 1404, 1403, 1464, 1404, 1404, 1464, 1465, + 1464, 1525, 1465, 1465, 1525, 1526, 1525, 1586, 1526, 1526, 1586, 1587, + 1586, 1647, 1587, 1587, 1647, 1648, 1647, 1708, 1648, 1648, 1708, 1709, + 1708, 1769, 1709, 1709, 1769, 1770, 1769, 1830, 1770, 1770, 1830, 1831, + 1830, 1891, 1831, 1831, 1891, 1892, 1891, 1952, 1892, 1892, 1952, 1953, + 1952, 2013, 1953, 1953, 2013, 2014, 2013, 2074, 2014, 2014, 2074, 2075, + 2074, 2135, 2075, 2075, 2135, 2136, 2135, 2196, 2136, 2136, 2196, 2197, + 2196, 2257, 2197, 2197, 2257, 2258, 2257, 2318, 2258, 2258, 2318, 2319, + 2318, 2379, 2319, 2319, 2379, 2380, 2379, 2440, 2380, 2380, 2440, 2441, + 2440, 2501, 2441, 2441, 2501, 2502, 2501, 2562, 2502, 2502, 2562, 2563, + 2562, 2623, 2563, 2563, 2623, 2624, 2623, 2684, 2624, 2624, 2684, 2685, + 2684, 2745, 2685, 2685, 2745, 2746, 2745, 2806, 2746, 2746, 2806, 2807, + 2806, 2867, 2807, 2807, 2867, 2868, 2867, 2928, 2868, 2868, 2928, 2929, + 2928, 2989, 2929, 2929, 2989, 2990, 2989, 3050, 2990, 2990, 3050, 3051, + 3050, 3111, 3051, 3051, 3111, 3112, 3111, 3172, 3112, 3112, 3172, 3173, + 3172, 3233, 3173, 3173, 3233, 3234, 3233, 3294, 3234, 3234, 3294, 3295, + 3294, 3355, 3295, 3295, 3355, 3356, 3355, 3416, 3356, 3356, 3416, 3417, + 3416, 3477, 3417, 3417, 3477, 3478, 3477, 3538, 3478, 3478, 3538, 3539, + 3538, 3599, 3539, 3539, 3599, 3600, 3599, 3660, 3600, 3600, 3660, 3661, + 1, 62, 2, 2, 62, 63, 62, 123, 63, 63, 123, 124, + 123, 184, 124, 124, 184, 185, 184, 245, 185, 185, 245, 246, + 245, 306, 246, 246, 306, 307, 306, 367, 307, 307, 367, 368, + 367, 428, 368, 368, 428, 429, 428, 489, 429, 429, 489, 490, + 489, 550, 490, 490, 550, 551, 550, 611, 551, 551, 611, 612, + 611, 672, 612, 612, 672, 673, 672, 733, 673, 673, 733, 734, + 733, 794, 734, 734, 794, 795, 794, 855, 795, 795, 855, 856, + 855, 916, 856, 856, 916, 917, 916, 977, 917, 917, 977, 978, + 977, 1038, 978, 978, 1038, 1039, 1038, 1099, 1039, 1039, 1099, 1100, + 1099, 1160, 1100, 1100, 1160, 1161, 1160, 1221, 1161, 1161, 1221, 1222, + 1221, 1282, 1222, 1222, 1282, 1283, 1282, 1343, 1283, 1283, 1343, 1344, + 1343, 1404, 1344, 1344, 1404, 1405, 1404, 1465, 1405, 1405, 1465, 1466, + 1465, 1526, 1466, 1466, 1526, 1527, 1526, 1587, 1527, 1527, 1587, 1588, + 1587, 1648, 1588, 1588, 1648, 1649, 1648, 1709, 1649, 1649, 1709, 1710, + 1709, 1770, 1710, 1710, 1770, 1771, 1770, 1831, 1771, 1771, 1831, 1832, + 1831, 1892, 1832, 1832, 1892, 1893, 1892, 1953, 1893, 1893, 1953, 1954, + 1953, 2014, 1954, 1954, 2014, 2015, 2014, 2075, 2015, 2015, 2075, 2076, + 2075, 2136, 2076, 2076, 2136, 2137, 2136, 2197, 2137, 2137, 2197, 2198, + 2197, 2258, 2198, 2198, 2258, 2259, 2258, 2319, 2259, 2259, 2319, 2320, + 2319, 2380, 2320, 2320, 2380, 2381, 2380, 2441, 2381, 2381, 2441, 2442, + 2441, 2502, 2442, 2442, 2502, 2503, 2502, 2563, 2503, 2503, 2563, 2564, + 2563, 2624, 2564, 2564, 2624, 2625, 2624, 2685, 2625, 2625, 2685, 2686, + 2685, 2746, 2686, 2686, 2746, 2747, 2746, 2807, 2747, 2747, 2807, 2808, + 2807, 2868, 2808, 2808, 2868, 2869, 2868, 2929, 2869, 2869, 2929, 2930, + 2929, 2990, 2930, 2930, 2990, 2991, 2990, 3051, 2991, 2991, 3051, 3052, + 3051, 3112, 3052, 3052, 3112, 3113, 3112, 3173, 3113, 3113, 3173, 3174, + 3173, 3234, 3174, 3174, 3234, 3235, 3234, 3295, 3235, 3235, 3295, 3296, + 3295, 3356, 3296, 3296, 3356, 3357, 3356, 3417, 3357, 3357, 3417, 3418, + 3417, 3478, 3418, 3418, 3478, 3479, 3478, 3539, 3479, 3479, 3539, 3540, + 3539, 3600, 3540, 3540, 3600, 3601, 3600, 3661, 3601, 3601, 3661, 3662, + 2, 63, 3, 3, 63, 64, 63, 124, 64, 64, 124, 125, + 124, 185, 125, 125, 185, 186, 185, 246, 186, 186, 246, 247, + 246, 307, 247, 247, 307, 308, 307, 368, 308, 308, 368, 369, + 368, 429, 369, 369, 429, 430, 429, 490, 430, 430, 490, 491, + 490, 551, 491, 491, 551, 552, 551, 612, 552, 552, 612, 613, + 612, 673, 613, 613, 673, 674, 673, 734, 674, 674, 734, 735, + 734, 795, 735, 735, 795, 796, 795, 856, 796, 796, 856, 857, + 856, 917, 857, 857, 917, 918, 917, 978, 918, 918, 978, 979, + 978, 1039, 979, 979, 1039, 1040, 1039, 1100, 1040, 1040, 1100, 1101, + 1100, 1161, 1101, 1101, 1161, 1162, 1161, 1222, 1162, 1162, 1222, 1223, + 1222, 1283, 1223, 1223, 1283, 1284, 1283, 1344, 1284, 1284, 1344, 1345, + 1344, 1405, 1345, 1345, 1405, 1406, 1405, 1466, 1406, 1406, 1466, 1467, + 1466, 1527, 1467, 1467, 1527, 1528, 1527, 1588, 1528, 1528, 1588, 1589, + 1588, 1649, 1589, 1589, 1649, 1650, 1649, 1710, 1650, 1650, 1710, 1711, + 1710, 1771, 1711, 1711, 1771, 1772, 1771, 1832, 1772, 1772, 1832, 1833, + 1832, 1893, 1833, 1833, 1893, 1894, 1893, 1954, 1894, 1894, 1954, 1955, + 1954, 2015, 1955, 1955, 2015, 2016, 2015, 2076, 2016, 2016, 2076, 2077, + 2076, 2137, 2077, 2077, 2137, 2138, 2137, 2198, 2138, 2138, 2198, 2199, + 2198, 2259, 2199, 2199, 2259, 2260, 2259, 2320, 2260, 2260, 2320, 2321, + 2320, 2381, 2321, 2321, 2381, 2382, 2381, 2442, 2382, 2382, 2442, 2443, + 2442, 2503, 2443, 2443, 2503, 2504, 2503, 2564, 2504, 2504, 2564, 2565, + 2564, 2625, 2565, 2565, 2625, 2626, 2625, 2686, 2626, 2626, 2686, 2687, + 2686, 2747, 2687, 2687, 2747, 2748, 2747, 2808, 2748, 2748, 2808, 2809, + 2808, 2869, 2809, 2809, 2869, 2870, 2869, 2930, 2870, 2870, 2930, 2931, + 2930, 2991, 2931, 2931, 2991, 2992, 2991, 3052, 2992, 2992, 3052, 3053, + 3052, 3113, 3053, 3053, 3113, 3114, 3113, 3174, 3114, 3114, 3174, 3175, + 3174, 3235, 3175, 3175, 3235, 3236, 3235, 3296, 3236, 3236, 3296, 3297, + 3296, 3357, 3297, 3297, 3357, 3358, 3357, 3418, 3358, 3358, 3418, 3419, + 3418, 3479, 3419, 3419, 3479, 3480, 3479, 3540, 3480, 3480, 3540, 3541, + 3540, 3601, 3541, 3541, 3601, 3602, 3601, 3662, 3602, 3602, 3662, 3663, + 3, 64, 4, 4, 64, 65, 64, 125, 65, 65, 125, 126, + 125, 186, 126, 126, 186, 187, 186, 247, 187, 187, 247, 248, + 247, 308, 248, 248, 308, 309, 308, 369, 309, 309, 369, 370, + 369, 430, 370, 370, 430, 431, 430, 491, 431, 431, 491, 492, + 491, 552, 492, 492, 552, 553, 552, 613, 553, 553, 613, 614, + 613, 674, 614, 614, 674, 675, 674, 735, 675, 675, 735, 736, + 735, 796, 736, 736, 796, 797, 796, 857, 797, 797, 857, 858, + 857, 918, 858, 858, 918, 919, 918, 979, 919, 919, 979, 980, + 979, 1040, 980, 980, 1040, 1041, 1040, 1101, 1041, 1041, 1101, 1102, + 1101, 1162, 1102, 1102, 1162, 1163, 1162, 1223, 1163, 1163, 1223, 1224, + 1223, 1284, 1224, 1224, 1284, 1285, 1284, 1345, 1285, 1285, 1345, 1346, + 1345, 1406, 1346, 1346, 1406, 1407, 1406, 1467, 1407, 1407, 1467, 1468, + 1467, 1528, 1468, 1468, 1528, 1529, 1528, 1589, 1529, 1529, 1589, 1590, + 1589, 1650, 1590, 1590, 1650, 1651, 1650, 1711, 1651, 1651, 1711, 1712, + 1711, 1772, 1712, 1712, 1772, 1773, 1772, 1833, 1773, 1773, 1833, 1834, + 1833, 1894, 1834, 1834, 1894, 1895, 1894, 1955, 1895, 1895, 1955, 1956, + 1955, 2016, 1956, 1956, 2016, 2017, 2016, 2077, 2017, 2017, 2077, 2078, + 2077, 2138, 2078, 2078, 2138, 2139, 2138, 2199, 2139, 2139, 2199, 2200, + 2199, 2260, 2200, 2200, 2260, 2261, 2260, 2321, 2261, 2261, 2321, 2322, + 2321, 2382, 2322, 2322, 2382, 2383, 2382, 2443, 2383, 2383, 2443, 2444, + 2443, 2504, 2444, 2444, 2504, 2505, 2504, 2565, 2505, 2505, 2565, 2566, + 2565, 2626, 2566, 2566, 2626, 2627, 2626, 2687, 2627, 2627, 2687, 2688, + 2687, 2748, 2688, 2688, 2748, 2749, 2748, 2809, 2749, 2749, 2809, 2810, + 2809, 2870, 2810, 2810, 2870, 2871, 2870, 2931, 2871, 2871, 2931, 2932, + 2931, 2992, 2932, 2932, 2992, 2993, 2992, 3053, 2993, 2993, 3053, 3054, + 3053, 3114, 3054, 3054, 3114, 3115, 3114, 3175, 3115, 3115, 3175, 3176, + 3175, 3236, 3176, 3176, 3236, 3237, 3236, 3297, 3237, 3237, 3297, 3298, + 3297, 3358, 3298, 3298, 3358, 3359, 3358, 3419, 3359, 3359, 3419, 3420, + 3419, 3480, 3420, 3420, 3480, 3481, 3480, 3541, 3481, 3481, 3541, 3542, + 3541, 3602, 3542, 3542, 3602, 3603, 3602, 3663, 3603, 3603, 3663, 3664, + 4, 65, 5, 5, 65, 66, 65, 126, 66, 66, 126, 127, + 126, 187, 127, 127, 187, 188, 187, 248, 188, 188, 248, 249, + 248, 309, 249, 249, 309, 310, 309, 370, 310, 310, 370, 371, + 370, 431, 371, 371, 431, 432, 431, 492, 432, 432, 492, 493, + 492, 553, 493, 493, 553, 554, 553, 614, 554, 554, 614, 615, + 614, 675, 615, 615, 675, 676, 675, 736, 676, 676, 736, 737, + 736, 797, 737, 737, 797, 798, 797, 858, 798, 798, 858, 859, + 858, 919, 859, 859, 919, 920, 919, 980, 920, 920, 980, 981, + 980, 1041, 981, 981, 1041, 1042, 1041, 1102, 1042, 1042, 1102, 1103, + 1102, 1163, 1103, 1103, 1163, 1164, 1163, 1224, 1164, 1164, 1224, 1225, + 1224, 1285, 1225, 1225, 1285, 1286, 1285, 1346, 1286, 1286, 1346, 1347, + 1346, 1407, 1347, 1347, 1407, 1408, 1407, 1468, 1408, 1408, 1468, 1469, + 1468, 1529, 1469, 1469, 1529, 1530, 1529, 1590, 1530, 1530, 1590, 1591, + 1590, 1651, 1591, 1591, 1651, 1652, 1651, 1712, 1652, 1652, 1712, 1713, + 1712, 1773, 1713, 1713, 1773, 1774, 1773, 1834, 1774, 1774, 1834, 1835, + 1834, 1895, 1835, 1835, 1895, 1896, 1895, 1956, 1896, 1896, 1956, 1957, + 1956, 2017, 1957, 1957, 2017, 2018, 2017, 2078, 2018, 2018, 2078, 2079, + 2078, 2139, 2079, 2079, 2139, 2140, 2139, 2200, 2140, 2140, 2200, 2201, + 2200, 2261, 2201, 2201, 2261, 2262, 2261, 2322, 2262, 2262, 2322, 2323, + 2322, 2383, 2323, 2323, 2383, 2384, 2383, 2444, 2384, 2384, 2444, 2445, + 2444, 2505, 2445, 2445, 2505, 2506, 2505, 2566, 2506, 2506, 2566, 2567, + 2566, 2627, 2567, 2567, 2627, 2628, 2627, 2688, 2628, 2628, 2688, 2689, + 2688, 2749, 2689, 2689, 2749, 2750, 2749, 2810, 2750, 2750, 2810, 2811, + 2810, 2871, 2811, 2811, 2871, 2872, 2871, 2932, 2872, 2872, 2932, 2933, + 2932, 2993, 2933, 2933, 2993, 2994, 2993, 3054, 2994, 2994, 3054, 3055, + 3054, 3115, 3055, 3055, 3115, 3116, 3115, 3176, 3116, 3116, 3176, 3177, + 3176, 3237, 3177, 3177, 3237, 3238, 3237, 3298, 3238, 3238, 3298, 3299, + 3298, 3359, 3299, 3299, 3359, 3360, 3359, 3420, 3360, 3360, 3420, 3421, + 3420, 3481, 3421, 3421, 3481, 3482, 3481, 3542, 3482, 3482, 3542, 3543, + 3542, 3603, 3543, 3543, 3603, 3604, 3603, 3664, 3604, 3604, 3664, 3665, + 5, 66, 6, 6, 66, 67, 66, 127, 67, 67, 127, 128, + 127, 188, 128, 128, 188, 189, 188, 249, 189, 189, 249, 250, + 249, 310, 250, 250, 310, 311, 310, 371, 311, 311, 371, 372, + 371, 432, 372, 372, 432, 433, 432, 493, 433, 433, 493, 494, + 493, 554, 494, 494, 554, 555, 554, 615, 555, 555, 615, 616, + 615, 676, 616, 616, 676, 677, 676, 737, 677, 677, 737, 738, + 737, 798, 738, 738, 798, 799, 798, 859, 799, 799, 859, 860, + 859, 920, 860, 860, 920, 921, 920, 981, 921, 921, 981, 982, + 981, 1042, 982, 982, 1042, 1043, 1042, 1103, 1043, 1043, 1103, 1104, + 1103, 1164, 1104, 1104, 1164, 1165, 1164, 1225, 1165, 1165, 1225, 1226, + 1225, 1286, 1226, 1226, 1286, 1287, 1286, 1347, 1287, 1287, 1347, 1348, + 1347, 1408, 1348, 1348, 1408, 1409, 1408, 1469, 1409, 1409, 1469, 1470, + 1469, 1530, 1470, 1470, 1530, 1531, 1530, 1591, 1531, 1531, 1591, 1592, + 1591, 1652, 1592, 1592, 1652, 1653, 1652, 1713, 1653, 1653, 1713, 1714, + 1713, 1774, 1714, 1714, 1774, 1775, 1774, 1835, 1775, 1775, 1835, 1836, + 1835, 1896, 1836, 1836, 1896, 1897, 1896, 1957, 1897, 1897, 1957, 1958, + 1957, 2018, 1958, 1958, 2018, 2019, 2018, 2079, 2019, 2019, 2079, 2080, + 2079, 2140, 2080, 2080, 2140, 2141, 2140, 2201, 2141, 2141, 2201, 2202, + 2201, 2262, 2202, 2202, 2262, 2263, 2262, 2323, 2263, 2263, 2323, 2324, + 2323, 2384, 2324, 2324, 2384, 2385, 2384, 2445, 2385, 2385, 2445, 2446, + 2445, 2506, 2446, 2446, 2506, 2507, 2506, 2567, 2507, 2507, 2567, 2568, + 2567, 2628, 2568, 2568, 2628, 2629, 2628, 2689, 2629, 2629, 2689, 2690, + 2689, 2750, 2690, 2690, 2750, 2751, 2750, 2811, 2751, 2751, 2811, 2812, + 2811, 2872, 2812, 2812, 2872, 2873, 2872, 2933, 2873, 2873, 2933, 2934, + 2933, 2994, 2934, 2934, 2994, 2995, 2994, 3055, 2995, 2995, 3055, 3056, + 3055, 3116, 3056, 3056, 3116, 3117, 3116, 3177, 3117, 3117, 3177, 3178, + 3177, 3238, 3178, 3178, 3238, 3239, 3238, 3299, 3239, 3239, 3299, 3300, + 3299, 3360, 3300, 3300, 3360, 3361, 3360, 3421, 3361, 3361, 3421, 3422, + 3421, 3482, 3422, 3422, 3482, 3483, 3482, 3543, 3483, 3483, 3543, 3544, + 3543, 3604, 3544, 3544, 3604, 3605, 3604, 3665, 3605, 3605, 3665, 3666, + 6, 67, 7, 7, 67, 68, 67, 128, 68, 68, 128, 129, + 128, 189, 129, 129, 189, 190, 189, 250, 190, 190, 250, 251, + 250, 311, 251, 251, 311, 312, 311, 372, 312, 312, 372, 373, + 372, 433, 373, 373, 433, 434, 433, 494, 434, 434, 494, 495, + 494, 555, 495, 495, 555, 556, 555, 616, 556, 556, 616, 617, + 616, 677, 617, 617, 677, 678, 677, 738, 678, 678, 738, 739, + 738, 799, 739, 739, 799, 800, 799, 860, 800, 800, 860, 861, + 860, 921, 861, 861, 921, 922, 921, 982, 922, 922, 982, 983, + 982, 1043, 983, 983, 1043, 1044, 1043, 1104, 1044, 1044, 1104, 1105, + 1104, 1165, 1105, 1105, 1165, 1166, 1165, 1226, 1166, 1166, 1226, 1227, + 1226, 1287, 1227, 1227, 1287, 1288, 1287, 1348, 1288, 1288, 1348, 1349, + 1348, 1409, 1349, 1349, 1409, 1410, 1409, 1470, 1410, 1410, 1470, 1471, + 1470, 1531, 1471, 1471, 1531, 1532, 1531, 1592, 1532, 1532, 1592, 1593, + 1592, 1653, 1593, 1593, 1653, 1654, 1653, 1714, 1654, 1654, 1714, 1715, + 1714, 1775, 1715, 1715, 1775, 1776, 1775, 1836, 1776, 1776, 1836, 1837, + 1836, 1897, 1837, 1837, 1897, 1898, 1897, 1958, 1898, 1898, 1958, 1959, + 1958, 2019, 1959, 1959, 2019, 2020, 2019, 2080, 2020, 2020, 2080, 2081, + 2080, 2141, 2081, 2081, 2141, 2142, 2141, 2202, 2142, 2142, 2202, 2203, + 2202, 2263, 2203, 2203, 2263, 2264, 2263, 2324, 2264, 2264, 2324, 2325, + 2324, 2385, 2325, 2325, 2385, 2386, 2385, 2446, 2386, 2386, 2446, 2447, + 2446, 2507, 2447, 2447, 2507, 2508, 2507, 2568, 2508, 2508, 2568, 2569, + 2568, 2629, 2569, 2569, 2629, 2630, 2629, 2690, 2630, 2630, 2690, 2691, + 2690, 2751, 2691, 2691, 2751, 2752, 2751, 2812, 2752, 2752, 2812, 2813, + 2812, 2873, 2813, 2813, 2873, 2874, 2873, 2934, 2874, 2874, 2934, 2935, + 2934, 2995, 2935, 2935, 2995, 2996, 2995, 3056, 2996, 2996, 3056, 3057, + 3056, 3117, 3057, 3057, 3117, 3118, 3117, 3178, 3118, 3118, 3178, 3179, + 3178, 3239, 3179, 3179, 3239, 3240, 3239, 3300, 3240, 3240, 3300, 3301, + 3300, 3361, 3301, 3301, 3361, 3362, 3361, 3422, 3362, 3362, 3422, 3423, + 3422, 3483, 3423, 3423, 3483, 3484, 3483, 3544, 3484, 3484, 3544, 3545, + 3544, 3605, 3545, 3545, 3605, 3606, 3605, 3666, 3606, 3606, 3666, 3667, + 7, 68, 8, 8, 68, 69, 68, 129, 69, 69, 129, 130, + 129, 190, 130, 130, 190, 191, 190, 251, 191, 191, 251, 252, + 251, 312, 252, 252, 312, 313, 312, 373, 313, 313, 373, 374, + 373, 434, 374, 374, 434, 435, 434, 495, 435, 435, 495, 496, + 495, 556, 496, 496, 556, 557, 556, 617, 557, 557, 617, 618, + 617, 678, 618, 618, 678, 679, 678, 739, 679, 679, 739, 740, + 739, 800, 740, 740, 800, 801, 800, 861, 801, 801, 861, 862, + 861, 922, 862, 862, 922, 923, 922, 983, 923, 923, 983, 984, + 983, 1044, 984, 984, 1044, 1045, 1044, 1105, 1045, 1045, 1105, 1106, + 1105, 1166, 1106, 1106, 1166, 1167, 1166, 1227, 1167, 1167, 1227, 1228, + 1227, 1288, 1228, 1228, 1288, 1289, 1288, 1349, 1289, 1289, 1349, 1350, + 1349, 1410, 1350, 1350, 1410, 1411, 1410, 1471, 1411, 1411, 1471, 1472, + 1471, 1532, 1472, 1472, 1532, 1533, 1532, 1593, 1533, 1533, 1593, 1594, + 1593, 1654, 1594, 1594, 1654, 1655, 1654, 1715, 1655, 1655, 1715, 1716, + 1715, 1776, 1716, 1716, 1776, 1777, 1776, 1837, 1777, 1777, 1837, 1838, + 1837, 1898, 1838, 1838, 1898, 1899, 1898, 1959, 1899, 1899, 1959, 1960, + 1959, 2020, 1960, 1960, 2020, 2021, 2020, 2081, 2021, 2021, 2081, 2082, + 2081, 2142, 2082, 2082, 2142, 2143, 2142, 2203, 2143, 2143, 2203, 2204, + 2203, 2264, 2204, 2204, 2264, 2265, 2264, 2325, 2265, 2265, 2325, 2326, + 2325, 2386, 2326, 2326, 2386, 2387, 2386, 2447, 2387, 2387, 2447, 2448, + 2447, 2508, 2448, 2448, 2508, 2509, 2508, 2569, 2509, 2509, 2569, 2570, + 2569, 2630, 2570, 2570, 2630, 2631, 2630, 2691, 2631, 2631, 2691, 2692, + 2691, 2752, 2692, 2692, 2752, 2753, 2752, 2813, 2753, 2753, 2813, 2814, + 2813, 2874, 2814, 2814, 2874, 2875, 2874, 2935, 2875, 2875, 2935, 2936, + 2935, 2996, 2936, 2936, 2996, 2997, 2996, 3057, 2997, 2997, 3057, 3058, + 3057, 3118, 3058, 3058, 3118, 3119, 3118, 3179, 3119, 3119, 3179, 3180, + 3179, 3240, 3180, 3180, 3240, 3241, 3240, 3301, 3241, 3241, 3301, 3302, + 3301, 3362, 3302, 3302, 3362, 3363, 3362, 3423, 3363, 3363, 3423, 3424, + 3423, 3484, 3424, 3424, 3484, 3485, 3484, 3545, 3485, 3485, 3545, 3546, + 3545, 3606, 3546, 3546, 3606, 3607, 3606, 3667, 3607, 3607, 3667, 3668, + 8, 69, 9, 9, 69, 70, 69, 130, 70, 70, 130, 131, + 130, 191, 131, 131, 191, 192, 191, 252, 192, 192, 252, 253, + 252, 313, 253, 253, 313, 314, 313, 374, 314, 314, 374, 375, + 374, 435, 375, 375, 435, 436, 435, 496, 436, 436, 496, 497, + 496, 557, 497, 497, 557, 558, 557, 618, 558, 558, 618, 619, + 618, 679, 619, 619, 679, 680, 679, 740, 680, 680, 740, 741, + 740, 801, 741, 741, 801, 802, 801, 862, 802, 802, 862, 863, + 862, 923, 863, 863, 923, 924, 923, 984, 924, 924, 984, 985, + 984, 1045, 985, 985, 1045, 1046, 1045, 1106, 1046, 1046, 1106, 1107, + 1106, 1167, 1107, 1107, 1167, 1168, 1167, 1228, 1168, 1168, 1228, 1229, + 1228, 1289, 1229, 1229, 1289, 1290, 1289, 1350, 1290, 1290, 1350, 1351, + 1350, 1411, 1351, 1351, 1411, 1412, 1411, 1472, 1412, 1412, 1472, 1473, + 1472, 1533, 1473, 1473, 1533, 1534, 1533, 1594, 1534, 1534, 1594, 1595, + 1594, 1655, 1595, 1595, 1655, 1656, 1655, 1716, 1656, 1656, 1716, 1717, + 1716, 1777, 1717, 1717, 1777, 1778, 1777, 1838, 1778, 1778, 1838, 1839, + 1838, 1899, 1839, 1839, 1899, 1900, 1899, 1960, 1900, 1900, 1960, 1961, + 1960, 2021, 1961, 1961, 2021, 2022, 2021, 2082, 2022, 2022, 2082, 2083, + 2082, 2143, 2083, 2083, 2143, 2144, 2143, 2204, 2144, 2144, 2204, 2205, + 2204, 2265, 2205, 2205, 2265, 2266, 2265, 2326, 2266, 2266, 2326, 2327, + 2326, 2387, 2327, 2327, 2387, 2388, 2387, 2448, 2388, 2388, 2448, 2449, + 2448, 2509, 2449, 2449, 2509, 2510, 2509, 2570, 2510, 2510, 2570, 2571, + 2570, 2631, 2571, 2571, 2631, 2632, 2631, 2692, 2632, 2632, 2692, 2693, + 2692, 2753, 2693, 2693, 2753, 2754, 2753, 2814, 2754, 2754, 2814, 2815, + 2814, 2875, 2815, 2815, 2875, 2876, 2875, 2936, 2876, 2876, 2936, 2937, + 2936, 2997, 2937, 2937, 2997, 2998, 2997, 3058, 2998, 2998, 3058, 3059, + 3058, 3119, 3059, 3059, 3119, 3120, 3119, 3180, 3120, 3120, 3180, 3181, + 3180, 3241, 3181, 3181, 3241, 3242, 3241, 3302, 3242, 3242, 3302, 3303, + 3302, 3363, 3303, 3303, 3363, 3364, 3363, 3424, 3364, 3364, 3424, 3425, + 3424, 3485, 3425, 3425, 3485, 3486, 3485, 3546, 3486, 3486, 3546, 3547, + 3546, 3607, 3547, 3547, 3607, 3608, 3607, 3668, 3608, 3608, 3668, 3669, + 9, 70, 10, 10, 70, 71, 70, 131, 71, 71, 131, 132, + 131, 192, 132, 132, 192, 193, 192, 253, 193, 193, 253, 254, + 253, 314, 254, 254, 314, 315, 314, 375, 315, 315, 375, 376, + 375, 436, 376, 376, 436, 437, 436, 497, 437, 437, 497, 498, + 497, 558, 498, 498, 558, 559, 558, 619, 559, 559, 619, 620, + 619, 680, 620, 620, 680, 681, 680, 741, 681, 681, 741, 742, + 741, 802, 742, 742, 802, 803, 802, 863, 803, 803, 863, 864, + 863, 924, 864, 864, 924, 925, 924, 985, 925, 925, 985, 986, + 985, 1046, 986, 986, 1046, 1047, 1046, 1107, 1047, 1047, 1107, 1108, + 1107, 1168, 1108, 1108, 1168, 1169, 1168, 1229, 1169, 1169, 1229, 1230, + 1229, 1290, 1230, 1230, 1290, 1291, 1290, 1351, 1291, 1291, 1351, 1352, + 1351, 1412, 1352, 1352, 1412, 1413, 1412, 1473, 1413, 1413, 1473, 1474, + 1473, 1534, 1474, 1474, 1534, 1535, 1534, 1595, 1535, 1535, 1595, 1596, + 1595, 1656, 1596, 1596, 1656, 1657, 1656, 1717, 1657, 1657, 1717, 1718, + 1717, 1778, 1718, 1718, 1778, 1779, 1778, 1839, 1779, 1779, 1839, 1840, + 1839, 1900, 1840, 1840, 1900, 1901, 1900, 1961, 1901, 1901, 1961, 1962, + 1961, 2022, 1962, 1962, 2022, 2023, 2022, 2083, 2023, 2023, 2083, 2084, + 2083, 2144, 2084, 2084, 2144, 2145, 2144, 2205, 2145, 2145, 2205, 2206, + 2205, 2266, 2206, 2206, 2266, 2267, 2266, 2327, 2267, 2267, 2327, 2328, + 2327, 2388, 2328, 2328, 2388, 2389, 2388, 2449, 2389, 2389, 2449, 2450, + 2449, 2510, 2450, 2450, 2510, 2511, 2510, 2571, 2511, 2511, 2571, 2572, + 2571, 2632, 2572, 2572, 2632, 2633, 2632, 2693, 2633, 2633, 2693, 2694, + 2693, 2754, 2694, 2694, 2754, 2755, 2754, 2815, 2755, 2755, 2815, 2816, + 2815, 2876, 2816, 2816, 2876, 2877, 2876, 2937, 2877, 2877, 2937, 2938, + 2937, 2998, 2938, 2938, 2998, 2999, 2998, 3059, 2999, 2999, 3059, 3060, + 3059, 3120, 3060, 3060, 3120, 3121, 3120, 3181, 3121, 3121, 3181, 3182, + 3181, 3242, 3182, 3182, 3242, 3243, 3242, 3303, 3243, 3243, 3303, 3304, + 3303, 3364, 3304, 3304, 3364, 3365, 3364, 3425, 3365, 3365, 3425, 3426, + 3425, 3486, 3426, 3426, 3486, 3487, 3486, 3547, 3487, 3487, 3547, 3548, + 3547, 3608, 3548, 3548, 3608, 3609, 3608, 3669, 3609, 3609, 3669, 3670, + 10, 71, 11, 11, 71, 72, 71, 132, 72, 72, 132, 133, + 132, 193, 133, 133, 193, 194, 193, 254, 194, 194, 254, 255, + 254, 315, 255, 255, 315, 316, 315, 376, 316, 316, 376, 377, + 376, 437, 377, 377, 437, 438, 437, 498, 438, 438, 498, 499, + 498, 559, 499, 499, 559, 560, 559, 620, 560, 560, 620, 621, + 620, 681, 621, 621, 681, 682, 681, 742, 682, 682, 742, 743, + 742, 803, 743, 743, 803, 804, 803, 864, 804, 804, 864, 865, + 864, 925, 865, 865, 925, 926, 925, 986, 926, 926, 986, 987, + 986, 1047, 987, 987, 1047, 1048, 1047, 1108, 1048, 1048, 1108, 1109, + 1108, 1169, 1109, 1109, 1169, 1170, 1169, 1230, 1170, 1170, 1230, 1231, + 1230, 1291, 1231, 1231, 1291, 1292, 1291, 1352, 1292, 1292, 1352, 1353, + 1352, 1413, 1353, 1353, 1413, 1414, 1413, 1474, 1414, 1414, 1474, 1475, + 1474, 1535, 1475, 1475, 1535, 1536, 1535, 1596, 1536, 1536, 1596, 1597, + 1596, 1657, 1597, 1597, 1657, 1658, 1657, 1718, 1658, 1658, 1718, 1719, + 1718, 1779, 1719, 1719, 1779, 1780, 1779, 1840, 1780, 1780, 1840, 1841, + 1840, 1901, 1841, 1841, 1901, 1902, 1901, 1962, 1902, 1902, 1962, 1963, + 1962, 2023, 1963, 1963, 2023, 2024, 2023, 2084, 2024, 2024, 2084, 2085, + 2084, 2145, 2085, 2085, 2145, 2146, 2145, 2206, 2146, 2146, 2206, 2207, + 2206, 2267, 2207, 2207, 2267, 2268, 2267, 2328, 2268, 2268, 2328, 2329, + 2328, 2389, 2329, 2329, 2389, 2390, 2389, 2450, 2390, 2390, 2450, 2451, + 2450, 2511, 2451, 2451, 2511, 2512, 2511, 2572, 2512, 2512, 2572, 2573, + 2572, 2633, 2573, 2573, 2633, 2634, 2633, 2694, 2634, 2634, 2694, 2695, + 2694, 2755, 2695, 2695, 2755, 2756, 2755, 2816, 2756, 2756, 2816, 2817, + 2816, 2877, 2817, 2817, 2877, 2878, 2877, 2938, 2878, 2878, 2938, 2939, + 2938, 2999, 2939, 2939, 2999, 3000, 2999, 3060, 3000, 3000, 3060, 3061, + 3060, 3121, 3061, 3061, 3121, 3122, 3121, 3182, 3122, 3122, 3182, 3183, + 3182, 3243, 3183, 3183, 3243, 3244, 3243, 3304, 3244, 3244, 3304, 3305, + 3304, 3365, 3305, 3305, 3365, 3366, 3365, 3426, 3366, 3366, 3426, 3427, + 3426, 3487, 3427, 3427, 3487, 3488, 3487, 3548, 3488, 3488, 3548, 3549, + 3548, 3609, 3549, 3549, 3609, 3610, 3609, 3670, 3610, 3610, 3670, 3671, + 11, 72, 12, 12, 72, 73, 72, 133, 73, 73, 133, 134, + 133, 194, 134, 134, 194, 195, 194, 255, 195, 195, 255, 256, + 255, 316, 256, 256, 316, 317, 316, 377, 317, 317, 377, 378, + 377, 438, 378, 378, 438, 439, 438, 499, 439, 439, 499, 500, + 499, 560, 500, 500, 560, 561, 560, 621, 561, 561, 621, 622, + 621, 682, 622, 622, 682, 683, 682, 743, 683, 683, 743, 744, + 743, 804, 744, 744, 804, 805, 804, 865, 805, 805, 865, 866, + 865, 926, 866, 866, 926, 927, 926, 987, 927, 927, 987, 988, + 987, 1048, 988, 988, 1048, 1049, 1048, 1109, 1049, 1049, 1109, 1110, + 1109, 1170, 1110, 1110, 1170, 1171, 1170, 1231, 1171, 1171, 1231, 1232, + 1231, 1292, 1232, 1232, 1292, 1293, 1292, 1353, 1293, 1293, 1353, 1354, + 1353, 1414, 1354, 1354, 1414, 1415, 1414, 1475, 1415, 1415, 1475, 1476, + 1475, 1536, 1476, 1476, 1536, 1537, 1536, 1597, 1537, 1537, 1597, 1598, + 1597, 1658, 1598, 1598, 1658, 1659, 1658, 1719, 1659, 1659, 1719, 1720, + 1719, 1780, 1720, 1720, 1780, 1781, 1780, 1841, 1781, 1781, 1841, 1842, + 1841, 1902, 1842, 1842, 1902, 1903, 1902, 1963, 1903, 1903, 1963, 1964, + 1963, 2024, 1964, 1964, 2024, 2025, 2024, 2085, 2025, 2025, 2085, 2086, + 2085, 2146, 2086, 2086, 2146, 2147, 2146, 2207, 2147, 2147, 2207, 2208, + 2207, 2268, 2208, 2208, 2268, 2269, 2268, 2329, 2269, 2269, 2329, 2330, + 2329, 2390, 2330, 2330, 2390, 2391, 2390, 2451, 2391, 2391, 2451, 2452, + 2451, 2512, 2452, 2452, 2512, 2513, 2512, 2573, 2513, 2513, 2573, 2574, + 2573, 2634, 2574, 2574, 2634, 2635, 2634, 2695, 2635, 2635, 2695, 2696, + 2695, 2756, 2696, 2696, 2756, 2757, 2756, 2817, 2757, 2757, 2817, 2818, + 2817, 2878, 2818, 2818, 2878, 2879, 2878, 2939, 2879, 2879, 2939, 2940, + 2939, 3000, 2940, 2940, 3000, 3001, 3000, 3061, 3001, 3001, 3061, 3062, + 3061, 3122, 3062, 3062, 3122, 3123, 3122, 3183, 3123, 3123, 3183, 3184, + 3183, 3244, 3184, 3184, 3244, 3245, 3244, 3305, 3245, 3245, 3305, 3306, + 3305, 3366, 3306, 3306, 3366, 3367, 3366, 3427, 3367, 3367, 3427, 3428, + 3427, 3488, 3428, 3428, 3488, 3489, 3488, 3549, 3489, 3489, 3549, 3550, + 3549, 3610, 3550, 3550, 3610, 3611, 3610, 3671, 3611, 3611, 3671, 3672, + 12, 73, 13, 13, 73, 74, 73, 134, 74, 74, 134, 135, + 134, 195, 135, 135, 195, 196, 195, 256, 196, 196, 256, 257, + 256, 317, 257, 257, 317, 318, 317, 378, 318, 318, 378, 379, + 378, 439, 379, 379, 439, 440, 439, 500, 440, 440, 500, 501, + 500, 561, 501, 501, 561, 562, 561, 622, 562, 562, 622, 623, + 622, 683, 623, 623, 683, 684, 683, 744, 684, 684, 744, 745, + 744, 805, 745, 745, 805, 806, 805, 866, 806, 806, 866, 867, + 866, 927, 867, 867, 927, 928, 927, 988, 928, 928, 988, 989, + 988, 1049, 989, 989, 1049, 1050, 1049, 1110, 1050, 1050, 1110, 1111, + 1110, 1171, 1111, 1111, 1171, 1172, 1171, 1232, 1172, 1172, 1232, 1233, + 1232, 1293, 1233, 1233, 1293, 1294, 1293, 1354, 1294, 1294, 1354, 1355, + 1354, 1415, 1355, 1355, 1415, 1416, 1415, 1476, 1416, 1416, 1476, 1477, + 1476, 1537, 1477, 1477, 1537, 1538, 1537, 1598, 1538, 1538, 1598, 1599, + 1598, 1659, 1599, 1599, 1659, 1660, 1659, 1720, 1660, 1660, 1720, 1721, + 1720, 1781, 1721, 1721, 1781, 1782, 1781, 1842, 1782, 1782, 1842, 1843, + 1842, 1903, 1843, 1843, 1903, 1904, 1903, 1964, 1904, 1904, 1964, 1965, + 1964, 2025, 1965, 1965, 2025, 2026, 2025, 2086, 2026, 2026, 2086, 2087, + 2086, 2147, 2087, 2087, 2147, 2148, 2147, 2208, 2148, 2148, 2208, 2209, + 2208, 2269, 2209, 2209, 2269, 2270, 2269, 2330, 2270, 2270, 2330, 2331, + 2330, 2391, 2331, 2331, 2391, 2392, 2391, 2452, 2392, 2392, 2452, 2453, + 2452, 2513, 2453, 2453, 2513, 2514, 2513, 2574, 2514, 2514, 2574, 2575, + 2574, 2635, 2575, 2575, 2635, 2636, 2635, 2696, 2636, 2636, 2696, 2697, + 2696, 2757, 2697, 2697, 2757, 2758, 2757, 2818, 2758, 2758, 2818, 2819, + 2818, 2879, 2819, 2819, 2879, 2880, 2879, 2940, 2880, 2880, 2940, 2941, + 2940, 3001, 2941, 2941, 3001, 3002, 3001, 3062, 3002, 3002, 3062, 3063, + 3062, 3123, 3063, 3063, 3123, 3124, 3123, 3184, 3124, 3124, 3184, 3185, + 3184, 3245, 3185, 3185, 3245, 3246, 3245, 3306, 3246, 3246, 3306, 3307, + 3306, 3367, 3307, 3307, 3367, 3368, 3367, 3428, 3368, 3368, 3428, 3429, + 3428, 3489, 3429, 3429, 3489, 3490, 3489, 3550, 3490, 3490, 3550, 3551, + 3550, 3611, 3551, 3551, 3611, 3612, 3611, 3672, 3612, 3612, 3672, 3673, + 13, 74, 14, 14, 74, 75, 74, 135, 75, 75, 135, 136, + 135, 196, 136, 136, 196, 197, 196, 257, 197, 197, 257, 258, + 257, 318, 258, 258, 318, 319, 318, 379, 319, 319, 379, 380, + 379, 440, 380, 380, 440, 441, 440, 501, 441, 441, 501, 502, + 501, 562, 502, 502, 562, 563, 562, 623, 563, 563, 623, 624, + 623, 684, 624, 624, 684, 685, 684, 745, 685, 685, 745, 746, + 745, 806, 746, 746, 806, 807, 806, 867, 807, 807, 867, 868, + 867, 928, 868, 868, 928, 929, 928, 989, 929, 929, 989, 990, + 989, 1050, 990, 990, 1050, 1051, 1050, 1111, 1051, 1051, 1111, 1112, + 1111, 1172, 1112, 1112, 1172, 1173, 1172, 1233, 1173, 1173, 1233, 1234, + 1233, 1294, 1234, 1234, 1294, 1295, 1294, 1355, 1295, 1295, 1355, 1356, + 1355, 1416, 1356, 1356, 1416, 1417, 1416, 1477, 1417, 1417, 1477, 1478, + 1477, 1538, 1478, 1478, 1538, 1539, 1538, 1599, 1539, 1539, 1599, 1600, + 1599, 1660, 1600, 1600, 1660, 1661, 1660, 1721, 1661, 1661, 1721, 1722, + 1721, 1782, 1722, 1722, 1782, 1783, 1782, 1843, 1783, 1783, 1843, 1844, + 1843, 1904, 1844, 1844, 1904, 1905, 1904, 1965, 1905, 1905, 1965, 1966, + 1965, 2026, 1966, 1966, 2026, 2027, 2026, 2087, 2027, 2027, 2087, 2088, + 2087, 2148, 2088, 2088, 2148, 2149, 2148, 2209, 2149, 2149, 2209, 2210, + 2209, 2270, 2210, 2210, 2270, 2271, 2270, 2331, 2271, 2271, 2331, 2332, + 2331, 2392, 2332, 2332, 2392, 2393, 2392, 2453, 2393, 2393, 2453, 2454, + 2453, 2514, 2454, 2454, 2514, 2515, 2514, 2575, 2515, 2515, 2575, 2576, + 2575, 2636, 2576, 2576, 2636, 2637, 2636, 2697, 2637, 2637, 2697, 2698, + 2697, 2758, 2698, 2698, 2758, 2759, 2758, 2819, 2759, 2759, 2819, 2820, + 2819, 2880, 2820, 2820, 2880, 2881, 2880, 2941, 2881, 2881, 2941, 2942, + 2941, 3002, 2942, 2942, 3002, 3003, 3002, 3063, 3003, 3003, 3063, 3064, + 3063, 3124, 3064, 3064, 3124, 3125, 3124, 3185, 3125, 3125, 3185, 3186, + 3185, 3246, 3186, 3186, 3246, 3247, 3246, 3307, 3247, 3247, 3307, 3308, + 3307, 3368, 3308, 3308, 3368, 3369, 3368, 3429, 3369, 3369, 3429, 3430, + 3429, 3490, 3430, 3430, 3490, 3491, 3490, 3551, 3491, 3491, 3551, 3552, + 3551, 3612, 3552, 3552, 3612, 3613, 3612, 3673, 3613, 3613, 3673, 3674, + 14, 75, 15, 15, 75, 76, 75, 136, 76, 76, 136, 137, + 136, 197, 137, 137, 197, 198, 197, 258, 198, 198, 258, 259, + 258, 319, 259, 259, 319, 320, 319, 380, 320, 320, 380, 381, + 380, 441, 381, 381, 441, 442, 441, 502, 442, 442, 502, 503, + 502, 563, 503, 503, 563, 564, 563, 624, 564, 564, 624, 625, + 624, 685, 625, 625, 685, 686, 685, 746, 686, 686, 746, 747, + 746, 807, 747, 747, 807, 808, 807, 868, 808, 808, 868, 869, + 868, 929, 869, 869, 929, 930, 929, 990, 930, 930, 990, 991, + 990, 1051, 991, 991, 1051, 1052, 1051, 1112, 1052, 1052, 1112, 1113, + 1112, 1173, 1113, 1113, 1173, 1174, 1173, 1234, 1174, 1174, 1234, 1235, + 1234, 1295, 1235, 1235, 1295, 1296, 1295, 1356, 1296, 1296, 1356, 1357, + 1356, 1417, 1357, 1357, 1417, 1418, 1417, 1478, 1418, 1418, 1478, 1479, + 1478, 1539, 1479, 1479, 1539, 1540, 1539, 1600, 1540, 1540, 1600, 1601, + 1600, 1661, 1601, 1601, 1661, 1662, 1661, 1722, 1662, 1662, 1722, 1723, + 1722, 1783, 1723, 1723, 1783, 1784, 1783, 1844, 1784, 1784, 1844, 1845, + 1844, 1905, 1845, 1845, 1905, 1906, 1905, 1966, 1906, 1906, 1966, 1967, + 1966, 2027, 1967, 1967, 2027, 2028, 2027, 2088, 2028, 2028, 2088, 2089, + 2088, 2149, 2089, 2089, 2149, 2150, 2149, 2210, 2150, 2150, 2210, 2211, + 2210, 2271, 2211, 2211, 2271, 2272, 2271, 2332, 2272, 2272, 2332, 2333, + 2332, 2393, 2333, 2333, 2393, 2394, 2393, 2454, 2394, 2394, 2454, 2455, + 2454, 2515, 2455, 2455, 2515, 2516, 2515, 2576, 2516, 2516, 2576, 2577, + 2576, 2637, 2577, 2577, 2637, 2638, 2637, 2698, 2638, 2638, 2698, 2699, + 2698, 2759, 2699, 2699, 2759, 2760, 2759, 2820, 2760, 2760, 2820, 2821, + 2820, 2881, 2821, 2821, 2881, 2882, 2881, 2942, 2882, 2882, 2942, 2943, + 2942, 3003, 2943, 2943, 3003, 3004, 3003, 3064, 3004, 3004, 3064, 3065, + 3064, 3125, 3065, 3065, 3125, 3126, 3125, 3186, 3126, 3126, 3186, 3187, + 3186, 3247, 3187, 3187, 3247, 3248, 3247, 3308, 3248, 3248, 3308, 3309, + 3308, 3369, 3309, 3309, 3369, 3370, 3369, 3430, 3370, 3370, 3430, 3431, + 3430, 3491, 3431, 3431, 3491, 3492, 3491, 3552, 3492, 3492, 3552, 3553, + 3552, 3613, 3553, 3553, 3613, 3614, 3613, 3674, 3614, 3614, 3674, 3675, + 15, 76, 16, 16, 76, 77, 76, 137, 77, 77, 137, 138, + 137, 198, 138, 138, 198, 199, 198, 259, 199, 199, 259, 260, + 259, 320, 260, 260, 320, 321, 320, 381, 321, 321, 381, 382, + 381, 442, 382, 382, 442, 443, 442, 503, 443, 443, 503, 504, + 503, 564, 504, 504, 564, 565, 564, 625, 565, 565, 625, 626, + 625, 686, 626, 626, 686, 687, 686, 747, 687, 687, 747, 748, + 747, 808, 748, 748, 808, 809, 808, 869, 809, 809, 869, 870, + 869, 930, 870, 870, 930, 931, 930, 991, 931, 931, 991, 992, + 991, 1052, 992, 992, 1052, 1053, 1052, 1113, 1053, 1053, 1113, 1114, + 1113, 1174, 1114, 1114, 1174, 1175, 1174, 1235, 1175, 1175, 1235, 1236, + 1235, 1296, 1236, 1236, 1296, 1297, 1296, 1357, 1297, 1297, 1357, 1358, + 1357, 1418, 1358, 1358, 1418, 1419, 1418, 1479, 1419, 1419, 1479, 1480, + 1479, 1540, 1480, 1480, 1540, 1541, 1540, 1601, 1541, 1541, 1601, 1602, + 1601, 1662, 1602, 1602, 1662, 1663, 1662, 1723, 1663, 1663, 1723, 1724, + 1723, 1784, 1724, 1724, 1784, 1785, 1784, 1845, 1785, 1785, 1845, 1846, + 1845, 1906, 1846, 1846, 1906, 1907, 1906, 1967, 1907, 1907, 1967, 1968, + 1967, 2028, 1968, 1968, 2028, 2029, 2028, 2089, 2029, 2029, 2089, 2090, + 2089, 2150, 2090, 2090, 2150, 2151, 2150, 2211, 2151, 2151, 2211, 2212, + 2211, 2272, 2212, 2212, 2272, 2273, 2272, 2333, 2273, 2273, 2333, 2334, + 2333, 2394, 2334, 2334, 2394, 2395, 2394, 2455, 2395, 2395, 2455, 2456, + 2455, 2516, 2456, 2456, 2516, 2517, 2516, 2577, 2517, 2517, 2577, 2578, + 2577, 2638, 2578, 2578, 2638, 2639, 2638, 2699, 2639, 2639, 2699, 2700, + 2699, 2760, 2700, 2700, 2760, 2761, 2760, 2821, 2761, 2761, 2821, 2822, + 2821, 2882, 2822, 2822, 2882, 2883, 2882, 2943, 2883, 2883, 2943, 2944, + 2943, 3004, 2944, 2944, 3004, 3005, 3004, 3065, 3005, 3005, 3065, 3066, + 3065, 3126, 3066, 3066, 3126, 3127, 3126, 3187, 3127, 3127, 3187, 3188, + 3187, 3248, 3188, 3188, 3248, 3249, 3248, 3309, 3249, 3249, 3309, 3310, + 3309, 3370, 3310, 3310, 3370, 3371, 3370, 3431, 3371, 3371, 3431, 3432, + 3431, 3492, 3432, 3432, 3492, 3493, 3492, 3553, 3493, 3493, 3553, 3554, + 3553, 3614, 3554, 3554, 3614, 3615, 3614, 3675, 3615, 3615, 3675, 3676, + 16, 77, 17, 17, 77, 78, 77, 138, 78, 78, 138, 139, + 138, 199, 139, 139, 199, 200, 199, 260, 200, 200, 260, 261, + 260, 321, 261, 261, 321, 322, 321, 382, 322, 322, 382, 383, + 382, 443, 383, 383, 443, 444, 443, 504, 444, 444, 504, 505, + 504, 565, 505, 505, 565, 566, 565, 626, 566, 566, 626, 627, + 626, 687, 627, 627, 687, 688, 687, 748, 688, 688, 748, 749, + 748, 809, 749, 749, 809, 810, 809, 870, 810, 810, 870, 871, + 870, 931, 871, 871, 931, 932, 931, 992, 932, 932, 992, 993, + 992, 1053, 993, 993, 1053, 1054, 1053, 1114, 1054, 1054, 1114, 1115, + 1114, 1175, 1115, 1115, 1175, 1176, 1175, 1236, 1176, 1176, 1236, 1237, + 1236, 1297, 1237, 1237, 1297, 1298, 1297, 1358, 1298, 1298, 1358, 1359, + 1358, 1419, 1359, 1359, 1419, 1420, 1419, 1480, 1420, 1420, 1480, 1481, + 1480, 1541, 1481, 1481, 1541, 1542, 1541, 1602, 1542, 1542, 1602, 1603, + 1602, 1663, 1603, 1603, 1663, 1664, 1663, 1724, 1664, 1664, 1724, 1725, + 1724, 1785, 1725, 1725, 1785, 1786, 1785, 1846, 1786, 1786, 1846, 1847, + 1846, 1907, 1847, 1847, 1907, 1908, 1907, 1968, 1908, 1908, 1968, 1969, + 1968, 2029, 1969, 1969, 2029, 2030, 2029, 2090, 2030, 2030, 2090, 2091, + 2090, 2151, 2091, 2091, 2151, 2152, 2151, 2212, 2152, 2152, 2212, 2213, + 2212, 2273, 2213, 2213, 2273, 2274, 2273, 2334, 2274, 2274, 2334, 2335, + 2334, 2395, 2335, 2335, 2395, 2396, 2395, 2456, 2396, 2396, 2456, 2457, + 2456, 2517, 2457, 2457, 2517, 2518, 2517, 2578, 2518, 2518, 2578, 2579, + 2578, 2639, 2579, 2579, 2639, 2640, 2639, 2700, 2640, 2640, 2700, 2701, + 2700, 2761, 2701, 2701, 2761, 2762, 2761, 2822, 2762, 2762, 2822, 2823, + 2822, 2883, 2823, 2823, 2883, 2884, 2883, 2944, 2884, 2884, 2944, 2945, + 2944, 3005, 2945, 2945, 3005, 3006, 3005, 3066, 3006, 3006, 3066, 3067, + 3066, 3127, 3067, 3067, 3127, 3128, 3127, 3188, 3128, 3128, 3188, 3189, + 3188, 3249, 3189, 3189, 3249, 3250, 3249, 3310, 3250, 3250, 3310, 3311, + 3310, 3371, 3311, 3311, 3371, 3372, 3371, 3432, 3372, 3372, 3432, 3433, + 3432, 3493, 3433, 3433, 3493, 3494, 3493, 3554, 3494, 3494, 3554, 3555, + 3554, 3615, 3555, 3555, 3615, 3616, 3615, 3676, 3616, 3616, 3676, 3677, + 17, 78, 18, 18, 78, 79, 78, 139, 79, 79, 139, 140, + 139, 200, 140, 140, 200, 201, 200, 261, 201, 201, 261, 262, + 261, 322, 262, 262, 322, 323, 322, 383, 323, 323, 383, 384, + 383, 444, 384, 384, 444, 445, 444, 505, 445, 445, 505, 506, + 505, 566, 506, 506, 566, 567, 566, 627, 567, 567, 627, 628, + 627, 688, 628, 628, 688, 689, 688, 749, 689, 689, 749, 750, + 749, 810, 750, 750, 810, 811, 810, 871, 811, 811, 871, 872, + 871, 932, 872, 872, 932, 933, 932, 993, 933, 933, 993, 994, + 993, 1054, 994, 994, 1054, 1055, 1054, 1115, 1055, 1055, 1115, 1116, + 1115, 1176, 1116, 1116, 1176, 1177, 1176, 1237, 1177, 1177, 1237, 1238, + 1237, 1298, 1238, 1238, 1298, 1299, 1298, 1359, 1299, 1299, 1359, 1360, + 1359, 1420, 1360, 1360, 1420, 1421, 1420, 1481, 1421, 1421, 1481, 1482, + 1481, 1542, 1482, 1482, 1542, 1543, 1542, 1603, 1543, 1543, 1603, 1604, + 1603, 1664, 1604, 1604, 1664, 1665, 1664, 1725, 1665, 1665, 1725, 1726, + 1725, 1786, 1726, 1726, 1786, 1787, 1786, 1847, 1787, 1787, 1847, 1848, + 1847, 1908, 1848, 1848, 1908, 1909, 1908, 1969, 1909, 1909, 1969, 1970, + 1969, 2030, 1970, 1970, 2030, 2031, 2030, 2091, 2031, 2031, 2091, 2092, + 2091, 2152, 2092, 2092, 2152, 2153, 2152, 2213, 2153, 2153, 2213, 2214, + 2213, 2274, 2214, 2214, 2274, 2275, 2274, 2335, 2275, 2275, 2335, 2336, + 2335, 2396, 2336, 2336, 2396, 2397, 2396, 2457, 2397, 2397, 2457, 2458, + 2457, 2518, 2458, 2458, 2518, 2519, 2518, 2579, 2519, 2519, 2579, 2580, + 2579, 2640, 2580, 2580, 2640, 2641, 2640, 2701, 2641, 2641, 2701, 2702, + 2701, 2762, 2702, 2702, 2762, 2763, 2762, 2823, 2763, 2763, 2823, 2824, + 2823, 2884, 2824, 2824, 2884, 2885, 2884, 2945, 2885, 2885, 2945, 2946, + 2945, 3006, 2946, 2946, 3006, 3007, 3006, 3067, 3007, 3007, 3067, 3068, + 3067, 3128, 3068, 3068, 3128, 3129, 3128, 3189, 3129, 3129, 3189, 3190, + 3189, 3250, 3190, 3190, 3250, 3251, 3250, 3311, 3251, 3251, 3311, 3312, + 3311, 3372, 3312, 3312, 3372, 3373, 3372, 3433, 3373, 3373, 3433, 3434, + 3433, 3494, 3434, 3434, 3494, 3495, 3494, 3555, 3495, 3495, 3555, 3556, + 3555, 3616, 3556, 3556, 3616, 3617, 3616, 3677, 3617, 3617, 3677, 3678, + 18, 79, 19, 19, 79, 80, 79, 140, 80, 80, 140, 141, + 140, 201, 141, 141, 201, 202, 201, 262, 202, 202, 262, 263, + 262, 323, 263, 263, 323, 324, 323, 384, 324, 324, 384, 385, + 384, 445, 385, 385, 445, 446, 445, 506, 446, 446, 506, 507, + 506, 567, 507, 507, 567, 568, 567, 628, 568, 568, 628, 629, + 628, 689, 629, 629, 689, 690, 689, 750, 690, 690, 750, 751, + 750, 811, 751, 751, 811, 812, 811, 872, 812, 812, 872, 873, + 872, 933, 873, 873, 933, 934, 933, 994, 934, 934, 994, 995, + 994, 1055, 995, 995, 1055, 1056, 1055, 1116, 1056, 1056, 1116, 1117, + 1116, 1177, 1117, 1117, 1177, 1178, 1177, 1238, 1178, 1178, 1238, 1239, + 1238, 1299, 1239, 1239, 1299, 1300, 1299, 1360, 1300, 1300, 1360, 1361, + 1360, 1421, 1361, 1361, 1421, 1422, 1421, 1482, 1422, 1422, 1482, 1483, + 1482, 1543, 1483, 1483, 1543, 1544, 1543, 1604, 1544, 1544, 1604, 1605, + 1604, 1665, 1605, 1605, 1665, 1666, 1665, 1726, 1666, 1666, 1726, 1727, + 1726, 1787, 1727, 1727, 1787, 1788, 1787, 1848, 1788, 1788, 1848, 1849, + 1848, 1909, 1849, 1849, 1909, 1910, 1909, 1970, 1910, 1910, 1970, 1971, + 1970, 2031, 1971, 1971, 2031, 2032, 2031, 2092, 2032, 2032, 2092, 2093, + 2092, 2153, 2093, 2093, 2153, 2154, 2153, 2214, 2154, 2154, 2214, 2215, + 2214, 2275, 2215, 2215, 2275, 2276, 2275, 2336, 2276, 2276, 2336, 2337, + 2336, 2397, 2337, 2337, 2397, 2398, 2397, 2458, 2398, 2398, 2458, 2459, + 2458, 2519, 2459, 2459, 2519, 2520, 2519, 2580, 2520, 2520, 2580, 2581, + 2580, 2641, 2581, 2581, 2641, 2642, 2641, 2702, 2642, 2642, 2702, 2703, + 2702, 2763, 2703, 2703, 2763, 2764, 2763, 2824, 2764, 2764, 2824, 2825, + 2824, 2885, 2825, 2825, 2885, 2886, 2885, 2946, 2886, 2886, 2946, 2947, + 2946, 3007, 2947, 2947, 3007, 3008, 3007, 3068, 3008, 3008, 3068, 3069, + 3068, 3129, 3069, 3069, 3129, 3130, 3129, 3190, 3130, 3130, 3190, 3191, + 3190, 3251, 3191, 3191, 3251, 3252, 3251, 3312, 3252, 3252, 3312, 3313, + 3312, 3373, 3313, 3313, 3373, 3374, 3373, 3434, 3374, 3374, 3434, 3435, + 3434, 3495, 3435, 3435, 3495, 3496, 3495, 3556, 3496, 3496, 3556, 3557, + 3556, 3617, 3557, 3557, 3617, 3618, 3617, 3678, 3618, 3618, 3678, 3679, + 19, 80, 20, 20, 80, 81, 80, 141, 81, 81, 141, 142, + 141, 202, 142, 142, 202, 203, 202, 263, 203, 203, 263, 264, + 263, 324, 264, 264, 324, 325, 324, 385, 325, 325, 385, 386, + 385, 446, 386, 386, 446, 447, 446, 507, 447, 447, 507, 508, + 507, 568, 508, 508, 568, 569, 568, 629, 569, 569, 629, 630, + 629, 690, 630, 630, 690, 691, 690, 751, 691, 691, 751, 752, + 751, 812, 752, 752, 812, 813, 812, 873, 813, 813, 873, 874, + 873, 934, 874, 874, 934, 935, 934, 995, 935, 935, 995, 996, + 995, 1056, 996, 996, 1056, 1057, 1056, 1117, 1057, 1057, 1117, 1118, + 1117, 1178, 1118, 1118, 1178, 1179, 1178, 1239, 1179, 1179, 1239, 1240, + 1239, 1300, 1240, 1240, 1300, 1301, 1300, 1361, 1301, 1301, 1361, 1362, + 1361, 1422, 1362, 1362, 1422, 1423, 1422, 1483, 1423, 1423, 1483, 1484, + 1483, 1544, 1484, 1484, 1544, 1545, 1544, 1605, 1545, 1545, 1605, 1606, + 1605, 1666, 1606, 1606, 1666, 1667, 1666, 1727, 1667, 1667, 1727, 1728, + 1727, 1788, 1728, 1728, 1788, 1789, 1788, 1849, 1789, 1789, 1849, 1850, + 1849, 1910, 1850, 1850, 1910, 1911, 1910, 1971, 1911, 1911, 1971, 1972, + 1971, 2032, 1972, 1972, 2032, 2033, 2032, 2093, 2033, 2033, 2093, 2094, + 2093, 2154, 2094, 2094, 2154, 2155, 2154, 2215, 2155, 2155, 2215, 2216, + 2215, 2276, 2216, 2216, 2276, 2277, 2276, 2337, 2277, 2277, 2337, 2338, + 2337, 2398, 2338, 2338, 2398, 2399, 2398, 2459, 2399, 2399, 2459, 2460, + 2459, 2520, 2460, 2460, 2520, 2521, 2520, 2581, 2521, 2521, 2581, 2582, + 2581, 2642, 2582, 2582, 2642, 2643, 2642, 2703, 2643, 2643, 2703, 2704, + 2703, 2764, 2704, 2704, 2764, 2765, 2764, 2825, 2765, 2765, 2825, 2826, + 2825, 2886, 2826, 2826, 2886, 2887, 2886, 2947, 2887, 2887, 2947, 2948, + 2947, 3008, 2948, 2948, 3008, 3009, 3008, 3069, 3009, 3009, 3069, 3070, + 3069, 3130, 3070, 3070, 3130, 3131, 3130, 3191, 3131, 3131, 3191, 3192, + 3191, 3252, 3192, 3192, 3252, 3253, 3252, 3313, 3253, 3253, 3313, 3314, + 3313, 3374, 3314, 3314, 3374, 3375, 3374, 3435, 3375, 3375, 3435, 3436, + 3435, 3496, 3436, 3436, 3496, 3497, 3496, 3557, 3497, 3497, 3557, 3558, + 3557, 3618, 3558, 3558, 3618, 3619, 3618, 3679, 3619, 3619, 3679, 3680, + 20, 81, 21, 21, 81, 82, 81, 142, 82, 82, 142, 143, + 142, 203, 143, 143, 203, 204, 203, 264, 204, 204, 264, 265, + 264, 325, 265, 265, 325, 326, 325, 386, 326, 326, 386, 387, + 386, 447, 387, 387, 447, 448, 447, 508, 448, 448, 508, 509, + 508, 569, 509, 509, 569, 570, 569, 630, 570, 570, 630, 631, + 630, 691, 631, 631, 691, 692, 691, 752, 692, 692, 752, 753, + 752, 813, 753, 753, 813, 814, 813, 874, 814, 814, 874, 875, + 874, 935, 875, 875, 935, 936, 935, 996, 936, 936, 996, 997, + 996, 1057, 997, 997, 1057, 1058, 1057, 1118, 1058, 1058, 1118, 1119, + 1118, 1179, 1119, 1119, 1179, 1180, 1179, 1240, 1180, 1180, 1240, 1241, + 1240, 1301, 1241, 1241, 1301, 1302, 1301, 1362, 1302, 1302, 1362, 1363, + 1362, 1423, 1363, 1363, 1423, 1424, 1423, 1484, 1424, 1424, 1484, 1485, + 1484, 1545, 1485, 1485, 1545, 1546, 1545, 1606, 1546, 1546, 1606, 1607, + 1606, 1667, 1607, 1607, 1667, 1668, 1667, 1728, 1668, 1668, 1728, 1729, + 1728, 1789, 1729, 1729, 1789, 1790, 1789, 1850, 1790, 1790, 1850, 1851, + 1850, 1911, 1851, 1851, 1911, 1912, 1911, 1972, 1912, 1912, 1972, 1973, + 1972, 2033, 1973, 1973, 2033, 2034, 2033, 2094, 2034, 2034, 2094, 2095, + 2094, 2155, 2095, 2095, 2155, 2156, 2155, 2216, 2156, 2156, 2216, 2217, + 2216, 2277, 2217, 2217, 2277, 2278, 2277, 2338, 2278, 2278, 2338, 2339, + 2338, 2399, 2339, 2339, 2399, 2400, 2399, 2460, 2400, 2400, 2460, 2461, + 2460, 2521, 2461, 2461, 2521, 2522, 2521, 2582, 2522, 2522, 2582, 2583, + 2582, 2643, 2583, 2583, 2643, 2644, 2643, 2704, 2644, 2644, 2704, 2705, + 2704, 2765, 2705, 2705, 2765, 2766, 2765, 2826, 2766, 2766, 2826, 2827, + 2826, 2887, 2827, 2827, 2887, 2888, 2887, 2948, 2888, 2888, 2948, 2949, + 2948, 3009, 2949, 2949, 3009, 3010, 3009, 3070, 3010, 3010, 3070, 3071, + 3070, 3131, 3071, 3071, 3131, 3132, 3131, 3192, 3132, 3132, 3192, 3193, + 3192, 3253, 3193, 3193, 3253, 3254, 3253, 3314, 3254, 3254, 3314, 3315, + 3314, 3375, 3315, 3315, 3375, 3376, 3375, 3436, 3376, 3376, 3436, 3437, + 3436, 3497, 3437, 3437, 3497, 3498, 3497, 3558, 3498, 3498, 3558, 3559, + 3558, 3619, 3559, 3559, 3619, 3620, 3619, 3680, 3620, 3620, 3680, 3681, + 21, 82, 22, 22, 82, 83, 82, 143, 83, 83, 143, 144, + 143, 204, 144, 144, 204, 205, 204, 265, 205, 205, 265, 266, + 265, 326, 266, 266, 326, 327, 326, 387, 327, 327, 387, 388, + 387, 448, 388, 388, 448, 449, 448, 509, 449, 449, 509, 510, + 509, 570, 510, 510, 570, 571, 570, 631, 571, 571, 631, 632, + 631, 692, 632, 632, 692, 693, 692, 753, 693, 693, 753, 754, + 753, 814, 754, 754, 814, 815, 814, 875, 815, 815, 875, 876, + 875, 936, 876, 876, 936, 937, 936, 997, 937, 937, 997, 998, + 997, 1058, 998, 998, 1058, 1059, 1058, 1119, 1059, 1059, 1119, 1120, + 1119, 1180, 1120, 1120, 1180, 1181, 1180, 1241, 1181, 1181, 1241, 1242, + 1241, 1302, 1242, 1242, 1302, 1303, 1302, 1363, 1303, 1303, 1363, 1364, + 1363, 1424, 1364, 1364, 1424, 1425, 1424, 1485, 1425, 1425, 1485, 1486, + 1485, 1546, 1486, 1486, 1546, 1547, 1546, 1607, 1547, 1547, 1607, 1608, + 1607, 1668, 1608, 1608, 1668, 1669, 1668, 1729, 1669, 1669, 1729, 1730, + 1729, 1790, 1730, 1730, 1790, 1791, 1790, 1851, 1791, 1791, 1851, 1852, + 1851, 1912, 1852, 1852, 1912, 1913, 1912, 1973, 1913, 1913, 1973, 1974, + 1973, 2034, 1974, 1974, 2034, 2035, 2034, 2095, 2035, 2035, 2095, 2096, + 2095, 2156, 2096, 2096, 2156, 2157, 2156, 2217, 2157, 2157, 2217, 2218, + 2217, 2278, 2218, 2218, 2278, 2279, 2278, 2339, 2279, 2279, 2339, 2340, + 2339, 2400, 2340, 2340, 2400, 2401, 2400, 2461, 2401, 2401, 2461, 2462, + 2461, 2522, 2462, 2462, 2522, 2523, 2522, 2583, 2523, 2523, 2583, 2584, + 2583, 2644, 2584, 2584, 2644, 2645, 2644, 2705, 2645, 2645, 2705, 2706, + 2705, 2766, 2706, 2706, 2766, 2767, 2766, 2827, 2767, 2767, 2827, 2828, + 2827, 2888, 2828, 2828, 2888, 2889, 2888, 2949, 2889, 2889, 2949, 2950, + 2949, 3010, 2950, 2950, 3010, 3011, 3010, 3071, 3011, 3011, 3071, 3072, + 3071, 3132, 3072, 3072, 3132, 3133, 3132, 3193, 3133, 3133, 3193, 3194, + 3193, 3254, 3194, 3194, 3254, 3255, 3254, 3315, 3255, 3255, 3315, 3316, + 3315, 3376, 3316, 3316, 3376, 3377, 3376, 3437, 3377, 3377, 3437, 3438, + 3437, 3498, 3438, 3438, 3498, 3499, 3498, 3559, 3499, 3499, 3559, 3560, + 3559, 3620, 3560, 3560, 3620, 3621, 3620, 3681, 3621, 3621, 3681, 3682, + 22, 83, 23, 23, 83, 84, 83, 144, 84, 84, 144, 145, + 144, 205, 145, 145, 205, 206, 205, 266, 206, 206, 266, 267, + 266, 327, 267, 267, 327, 328, 327, 388, 328, 328, 388, 389, + 388, 449, 389, 389, 449, 450, 449, 510, 450, 450, 510, 511, + 510, 571, 511, 511, 571, 572, 571, 632, 572, 572, 632, 633, + 632, 693, 633, 633, 693, 694, 693, 754, 694, 694, 754, 755, + 754, 815, 755, 755, 815, 816, 815, 876, 816, 816, 876, 877, + 876, 937, 877, 877, 937, 938, 937, 998, 938, 938, 998, 999, + 998, 1059, 999, 999, 1059, 1060, 1059, 1120, 1060, 1060, 1120, 1121, + 1120, 1181, 1121, 1121, 1181, 1182, 1181, 1242, 1182, 1182, 1242, 1243, + 1242, 1303, 1243, 1243, 1303, 1304, 1303, 1364, 1304, 1304, 1364, 1365, + 1364, 1425, 1365, 1365, 1425, 1426, 1425, 1486, 1426, 1426, 1486, 1487, + 1486, 1547, 1487, 1487, 1547, 1548, 1547, 1608, 1548, 1548, 1608, 1609, + 1608, 1669, 1609, 1609, 1669, 1670, 1669, 1730, 1670, 1670, 1730, 1731, + 1730, 1791, 1731, 1731, 1791, 1792, 1791, 1852, 1792, 1792, 1852, 1853, + 1852, 1913, 1853, 1853, 1913, 1914, 1913, 1974, 1914, 1914, 1974, 1975, + 1974, 2035, 1975, 1975, 2035, 2036, 2035, 2096, 2036, 2036, 2096, 2097, + 2096, 2157, 2097, 2097, 2157, 2158, 2157, 2218, 2158, 2158, 2218, 2219, + 2218, 2279, 2219, 2219, 2279, 2280, 2279, 2340, 2280, 2280, 2340, 2341, + 2340, 2401, 2341, 2341, 2401, 2402, 2401, 2462, 2402, 2402, 2462, 2463, + 2462, 2523, 2463, 2463, 2523, 2524, 2523, 2584, 2524, 2524, 2584, 2585, + 2584, 2645, 2585, 2585, 2645, 2646, 2645, 2706, 2646, 2646, 2706, 2707, + 2706, 2767, 2707, 2707, 2767, 2768, 2767, 2828, 2768, 2768, 2828, 2829, + 2828, 2889, 2829, 2829, 2889, 2890, 2889, 2950, 2890, 2890, 2950, 2951, + 2950, 3011, 2951, 2951, 3011, 3012, 3011, 3072, 3012, 3012, 3072, 3073, + 3072, 3133, 3073, 3073, 3133, 3134, 3133, 3194, 3134, 3134, 3194, 3195, + 3194, 3255, 3195, 3195, 3255, 3256, 3255, 3316, 3256, 3256, 3316, 3317, + 3316, 3377, 3317, 3317, 3377, 3378, 3377, 3438, 3378, 3378, 3438, 3439, + 3438, 3499, 3439, 3439, 3499, 3500, 3499, 3560, 3500, 3500, 3560, 3561, + 3560, 3621, 3561, 3561, 3621, 3622, 3621, 3682, 3622, 3622, 3682, 3683, + 23, 84, 24, 24, 84, 85, 84, 145, 85, 85, 145, 146, + 145, 206, 146, 146, 206, 207, 206, 267, 207, 207, 267, 268, + 267, 328, 268, 268, 328, 329, 328, 389, 329, 329, 389, 390, + 389, 450, 390, 390, 450, 451, 450, 511, 451, 451, 511, 512, + 511, 572, 512, 512, 572, 573, 572, 633, 573, 573, 633, 634, + 633, 694, 634, 634, 694, 695, 694, 755, 695, 695, 755, 756, + 755, 816, 756, 756, 816, 817, 816, 877, 817, 817, 877, 878, + 877, 938, 878, 878, 938, 939, 938, 999, 939, 939, 999, 1000, + 999, 1060, 1000, 1000, 1060, 1061, 1060, 1121, 1061, 1061, 1121, 1122, + 1121, 1182, 1122, 1122, 1182, 1183, 1182, 1243, 1183, 1183, 1243, 1244, + 1243, 1304, 1244, 1244, 1304, 1305, 1304, 1365, 1305, 1305, 1365, 1366, + 1365, 1426, 1366, 1366, 1426, 1427, 1426, 1487, 1427, 1427, 1487, 1488, + 1487, 1548, 1488, 1488, 1548, 1549, 1548, 1609, 1549, 1549, 1609, 1610, + 1609, 1670, 1610, 1610, 1670, 1671, 1670, 1731, 1671, 1671, 1731, 1732, + 1731, 1792, 1732, 1732, 1792, 1793, 1792, 1853, 1793, 1793, 1853, 1854, + 1853, 1914, 1854, 1854, 1914, 1915, 1914, 1975, 1915, 1915, 1975, 1976, + 1975, 2036, 1976, 1976, 2036, 2037, 2036, 2097, 2037, 2037, 2097, 2098, + 2097, 2158, 2098, 2098, 2158, 2159, 2158, 2219, 2159, 2159, 2219, 2220, + 2219, 2280, 2220, 2220, 2280, 2281, 2280, 2341, 2281, 2281, 2341, 2342, + 2341, 2402, 2342, 2342, 2402, 2403, 2402, 2463, 2403, 2403, 2463, 2464, + 2463, 2524, 2464, 2464, 2524, 2525, 2524, 2585, 2525, 2525, 2585, 2586, + 2585, 2646, 2586, 2586, 2646, 2647, 2646, 2707, 2647, 2647, 2707, 2708, + 2707, 2768, 2708, 2708, 2768, 2769, 2768, 2829, 2769, 2769, 2829, 2830, + 2829, 2890, 2830, 2830, 2890, 2891, 2890, 2951, 2891, 2891, 2951, 2952, + 2951, 3012, 2952, 2952, 3012, 3013, 3012, 3073, 3013, 3013, 3073, 3074, + 3073, 3134, 3074, 3074, 3134, 3135, 3134, 3195, 3135, 3135, 3195, 3196, + 3195, 3256, 3196, 3196, 3256, 3257, 3256, 3317, 3257, 3257, 3317, 3318, + 3317, 3378, 3318, 3318, 3378, 3379, 3378, 3439, 3379, 3379, 3439, 3440, + 3439, 3500, 3440, 3440, 3500, 3501, 3500, 3561, 3501, 3501, 3561, 3562, + 3561, 3622, 3562, 3562, 3622, 3623, 3622, 3683, 3623, 3623, 3683, 3684, + 24, 85, 25, 25, 85, 86, 85, 146, 86, 86, 146, 147, + 146, 207, 147, 147, 207, 208, 207, 268, 208, 208, 268, 269, + 268, 329, 269, 269, 329, 330, 329, 390, 330, 330, 390, 391, + 390, 451, 391, 391, 451, 452, 451, 512, 452, 452, 512, 513, + 512, 573, 513, 513, 573, 574, 573, 634, 574, 574, 634, 635, + 634, 695, 635, 635, 695, 696, 695, 756, 696, 696, 756, 757, + 756, 817, 757, 757, 817, 818, 817, 878, 818, 818, 878, 879, + 878, 939, 879, 879, 939, 940, 939, 1000, 940, 940, 1000, 1001, + 1000, 1061, 1001, 1001, 1061, 1062, 1061, 1122, 1062, 1062, 1122, 1123, + 1122, 1183, 1123, 1123, 1183, 1184, 1183, 1244, 1184, 1184, 1244, 1245, + 1244, 1305, 1245, 1245, 1305, 1306, 1305, 1366, 1306, 1306, 1366, 1367, + 1366, 1427, 1367, 1367, 1427, 1428, 1427, 1488, 1428, 1428, 1488, 1489, + 1488, 1549, 1489, 1489, 1549, 1550, 1549, 1610, 1550, 1550, 1610, 1611, + 1610, 1671, 1611, 1611, 1671, 1672, 1671, 1732, 1672, 1672, 1732, 1733, + 1732, 1793, 1733, 1733, 1793, 1794, 1793, 1854, 1794, 1794, 1854, 1855, + 1854, 1915, 1855, 1855, 1915, 1916, 1915, 1976, 1916, 1916, 1976, 1977, + 1976, 2037, 1977, 1977, 2037, 2038, 2037, 2098, 2038, 2038, 2098, 2099, + 2098, 2159, 2099, 2099, 2159, 2160, 2159, 2220, 2160, 2160, 2220, 2221, + 2220, 2281, 2221, 2221, 2281, 2282, 2281, 2342, 2282, 2282, 2342, 2343, + 2342, 2403, 2343, 2343, 2403, 2404, 2403, 2464, 2404, 2404, 2464, 2465, + 2464, 2525, 2465, 2465, 2525, 2526, 2525, 2586, 2526, 2526, 2586, 2587, + 2586, 2647, 2587, 2587, 2647, 2648, 2647, 2708, 2648, 2648, 2708, 2709, + 2708, 2769, 2709, 2709, 2769, 2770, 2769, 2830, 2770, 2770, 2830, 2831, + 2830, 2891, 2831, 2831, 2891, 2892, 2891, 2952, 2892, 2892, 2952, 2953, + 2952, 3013, 2953, 2953, 3013, 3014, 3013, 3074, 3014, 3014, 3074, 3075, + 3074, 3135, 3075, 3075, 3135, 3136, 3135, 3196, 3136, 3136, 3196, 3197, + 3196, 3257, 3197, 3197, 3257, 3258, 3257, 3318, 3258, 3258, 3318, 3319, + 3318, 3379, 3319, 3319, 3379, 3380, 3379, 3440, 3380, 3380, 3440, 3441, + 3440, 3501, 3441, 3441, 3501, 3502, 3501, 3562, 3502, 3502, 3562, 3563, + 3562, 3623, 3563, 3563, 3623, 3624, 3623, 3684, 3624, 3624, 3684, 3685, + 25, 86, 26, 26, 86, 87, 86, 147, 87, 87, 147, 148, + 147, 208, 148, 148, 208, 209, 208, 269, 209, 209, 269, 270, + 269, 330, 270, 270, 330, 331, 330, 391, 331, 331, 391, 392, + 391, 452, 392, 392, 452, 453, 452, 513, 453, 453, 513, 514, + 513, 574, 514, 514, 574, 575, 574, 635, 575, 575, 635, 636, + 635, 696, 636, 636, 696, 697, 696, 757, 697, 697, 757, 758, + 757, 818, 758, 758, 818, 819, 818, 879, 819, 819, 879, 880, + 879, 940, 880, 880, 940, 941, 940, 1001, 941, 941, 1001, 1002, + 1001, 1062, 1002, 1002, 1062, 1063, 1062, 1123, 1063, 1063, 1123, 1124, + 1123, 1184, 1124, 1124, 1184, 1185, 1184, 1245, 1185, 1185, 1245, 1246, + 1245, 1306, 1246, 1246, 1306, 1307, 1306, 1367, 1307, 1307, 1367, 1368, + 1367, 1428, 1368, 1368, 1428, 1429, 1428, 1489, 1429, 1429, 1489, 1490, + 1489, 1550, 1490, 1490, 1550, 1551, 1550, 1611, 1551, 1551, 1611, 1612, + 1611, 1672, 1612, 1612, 1672, 1673, 1672, 1733, 1673, 1673, 1733, 1734, + 1733, 1794, 1734, 1734, 1794, 1795, 1794, 1855, 1795, 1795, 1855, 1856, + 1855, 1916, 1856, 1856, 1916, 1917, 1916, 1977, 1917, 1917, 1977, 1978, + 1977, 2038, 1978, 1978, 2038, 2039, 2038, 2099, 2039, 2039, 2099, 2100, + 2099, 2160, 2100, 2100, 2160, 2161, 2160, 2221, 2161, 2161, 2221, 2222, + 2221, 2282, 2222, 2222, 2282, 2283, 2282, 2343, 2283, 2283, 2343, 2344, + 2343, 2404, 2344, 2344, 2404, 2405, 2404, 2465, 2405, 2405, 2465, 2466, + 2465, 2526, 2466, 2466, 2526, 2527, 2526, 2587, 2527, 2527, 2587, 2588, + 2587, 2648, 2588, 2588, 2648, 2649, 2648, 2709, 2649, 2649, 2709, 2710, + 2709, 2770, 2710, 2710, 2770, 2771, 2770, 2831, 2771, 2771, 2831, 2832, + 2831, 2892, 2832, 2832, 2892, 2893, 2892, 2953, 2893, 2893, 2953, 2954, + 2953, 3014, 2954, 2954, 3014, 3015, 3014, 3075, 3015, 3015, 3075, 3076, + 3075, 3136, 3076, 3076, 3136, 3137, 3136, 3197, 3137, 3137, 3197, 3198, + 3197, 3258, 3198, 3198, 3258, 3259, 3258, 3319, 3259, 3259, 3319, 3320, + 3319, 3380, 3320, 3320, 3380, 3381, 3380, 3441, 3381, 3381, 3441, 3442, + 3441, 3502, 3442, 3442, 3502, 3503, 3502, 3563, 3503, 3503, 3563, 3564, + 3563, 3624, 3564, 3564, 3624, 3625, 3624, 3685, 3625, 3625, 3685, 3686, + 26, 87, 27, 27, 87, 88, 87, 148, 88, 88, 148, 149, + 148, 209, 149, 149, 209, 210, 209, 270, 210, 210, 270, 271, + 270, 331, 271, 271, 331, 332, 331, 392, 332, 332, 392, 393, + 392, 453, 393, 393, 453, 454, 453, 514, 454, 454, 514, 515, + 514, 575, 515, 515, 575, 576, 575, 636, 576, 576, 636, 637, + 636, 697, 637, 637, 697, 698, 697, 758, 698, 698, 758, 759, + 758, 819, 759, 759, 819, 820, 819, 880, 820, 820, 880, 881, + 880, 941, 881, 881, 941, 942, 941, 1002, 942, 942, 1002, 1003, + 1002, 1063, 1003, 1003, 1063, 1064, 1063, 1124, 1064, 1064, 1124, 1125, + 1124, 1185, 1125, 1125, 1185, 1186, 1185, 1246, 1186, 1186, 1246, 1247, + 1246, 1307, 1247, 1247, 1307, 1308, 1307, 1368, 1308, 1308, 1368, 1369, + 1368, 1429, 1369, 1369, 1429, 1430, 1429, 1490, 1430, 1430, 1490, 1491, + 1490, 1551, 1491, 1491, 1551, 1552, 1551, 1612, 1552, 1552, 1612, 1613, + 1612, 1673, 1613, 1613, 1673, 1674, 1673, 1734, 1674, 1674, 1734, 1735, + 1734, 1795, 1735, 1735, 1795, 1796, 1795, 1856, 1796, 1796, 1856, 1857, + 1856, 1917, 1857, 1857, 1917, 1918, 1917, 1978, 1918, 1918, 1978, 1979, + 1978, 2039, 1979, 1979, 2039, 2040, 2039, 2100, 2040, 2040, 2100, 2101, + 2100, 2161, 2101, 2101, 2161, 2162, 2161, 2222, 2162, 2162, 2222, 2223, + 2222, 2283, 2223, 2223, 2283, 2284, 2283, 2344, 2284, 2284, 2344, 2345, + 2344, 2405, 2345, 2345, 2405, 2406, 2405, 2466, 2406, 2406, 2466, 2467, + 2466, 2527, 2467, 2467, 2527, 2528, 2527, 2588, 2528, 2528, 2588, 2589, + 2588, 2649, 2589, 2589, 2649, 2650, 2649, 2710, 2650, 2650, 2710, 2711, + 2710, 2771, 2711, 2711, 2771, 2772, 2771, 2832, 2772, 2772, 2832, 2833, + 2832, 2893, 2833, 2833, 2893, 2894, 2893, 2954, 2894, 2894, 2954, 2955, + 2954, 3015, 2955, 2955, 3015, 3016, 3015, 3076, 3016, 3016, 3076, 3077, + 3076, 3137, 3077, 3077, 3137, 3138, 3137, 3198, 3138, 3138, 3198, 3199, + 3198, 3259, 3199, 3199, 3259, 3260, 3259, 3320, 3260, 3260, 3320, 3321, + 3320, 3381, 3321, 3321, 3381, 3382, 3381, 3442, 3382, 3382, 3442, 3443, + 3442, 3503, 3443, 3443, 3503, 3504, 3503, 3564, 3504, 3504, 3564, 3565, + 3564, 3625, 3565, 3565, 3625, 3626, 3625, 3686, 3626, 3626, 3686, 3687, + 27, 88, 28, 28, 88, 89, 88, 149, 89, 89, 149, 150, + 149, 210, 150, 150, 210, 211, 210, 271, 211, 211, 271, 272, + 271, 332, 272, 272, 332, 333, 332, 393, 333, 333, 393, 394, + 393, 454, 394, 394, 454, 455, 454, 515, 455, 455, 515, 516, + 515, 576, 516, 516, 576, 577, 576, 637, 577, 577, 637, 638, + 637, 698, 638, 638, 698, 699, 698, 759, 699, 699, 759, 760, + 759, 820, 760, 760, 820, 821, 820, 881, 821, 821, 881, 882, + 881, 942, 882, 882, 942, 943, 942, 1003, 943, 943, 1003, 1004, + 1003, 1064, 1004, 1004, 1064, 1065, 1064, 1125, 1065, 1065, 1125, 1126, + 1125, 1186, 1126, 1126, 1186, 1187, 1186, 1247, 1187, 1187, 1247, 1248, + 1247, 1308, 1248, 1248, 1308, 1309, 1308, 1369, 1309, 1309, 1369, 1370, + 1369, 1430, 1370, 1370, 1430, 1431, 1430, 1491, 1431, 1431, 1491, 1492, + 1491, 1552, 1492, 1492, 1552, 1553, 1552, 1613, 1553, 1553, 1613, 1614, + 1613, 1674, 1614, 1614, 1674, 1675, 1674, 1735, 1675, 1675, 1735, 1736, + 1735, 1796, 1736, 1736, 1796, 1797, 1796, 1857, 1797, 1797, 1857, 1858, + 1857, 1918, 1858, 1858, 1918, 1919, 1918, 1979, 1919, 1919, 1979, 1980, + 1979, 2040, 1980, 1980, 2040, 2041, 2040, 2101, 2041, 2041, 2101, 2102, + 2101, 2162, 2102, 2102, 2162, 2163, 2162, 2223, 2163, 2163, 2223, 2224, + 2223, 2284, 2224, 2224, 2284, 2285, 2284, 2345, 2285, 2285, 2345, 2346, + 2345, 2406, 2346, 2346, 2406, 2407, 2406, 2467, 2407, 2407, 2467, 2468, + 2467, 2528, 2468, 2468, 2528, 2529, 2528, 2589, 2529, 2529, 2589, 2590, + 2589, 2650, 2590, 2590, 2650, 2651, 2650, 2711, 2651, 2651, 2711, 2712, + 2711, 2772, 2712, 2712, 2772, 2773, 2772, 2833, 2773, 2773, 2833, 2834, + 2833, 2894, 2834, 2834, 2894, 2895, 2894, 2955, 2895, 2895, 2955, 2956, + 2955, 3016, 2956, 2956, 3016, 3017, 3016, 3077, 3017, 3017, 3077, 3078, + 3077, 3138, 3078, 3078, 3138, 3139, 3138, 3199, 3139, 3139, 3199, 3200, + 3199, 3260, 3200, 3200, 3260, 3261, 3260, 3321, 3261, 3261, 3321, 3322, + 3321, 3382, 3322, 3322, 3382, 3383, 3382, 3443, 3383, 3383, 3443, 3444, + 3443, 3504, 3444, 3444, 3504, 3505, 3504, 3565, 3505, 3505, 3565, 3566, + 3565, 3626, 3566, 3566, 3626, 3627, 3626, 3687, 3627, 3627, 3687, 3688, + 28, 89, 29, 29, 89, 90, 89, 150, 90, 90, 150, 151, + 150, 211, 151, 151, 211, 212, 211, 272, 212, 212, 272, 273, + 272, 333, 273, 273, 333, 334, 333, 394, 334, 334, 394, 395, + 394, 455, 395, 395, 455, 456, 455, 516, 456, 456, 516, 517, + 516, 577, 517, 517, 577, 578, 577, 638, 578, 578, 638, 639, + 638, 699, 639, 639, 699, 700, 699, 760, 700, 700, 760, 761, + 760, 821, 761, 761, 821, 822, 821, 882, 822, 822, 882, 883, + 882, 943, 883, 883, 943, 944, 943, 1004, 944, 944, 1004, 1005, + 1004, 1065, 1005, 1005, 1065, 1066, 1065, 1126, 1066, 1066, 1126, 1127, + 1126, 1187, 1127, 1127, 1187, 1188, 1187, 1248, 1188, 1188, 1248, 1249, + 1248, 1309, 1249, 1249, 1309, 1310, 1309, 1370, 1310, 1310, 1370, 1371, + 1370, 1431, 1371, 1371, 1431, 1432, 1431, 1492, 1432, 1432, 1492, 1493, + 1492, 1553, 1493, 1493, 1553, 1554, 1553, 1614, 1554, 1554, 1614, 1615, + 1614, 1675, 1615, 1615, 1675, 1676, 1675, 1736, 1676, 1676, 1736, 1737, + 1736, 1797, 1737, 1737, 1797, 1798, 1797, 1858, 1798, 1798, 1858, 1859, + 1858, 1919, 1859, 1859, 1919, 1920, 1919, 1980, 1920, 1920, 1980, 1981, + 1980, 2041, 1981, 1981, 2041, 2042, 2041, 2102, 2042, 2042, 2102, 2103, + 2102, 2163, 2103, 2103, 2163, 2164, 2163, 2224, 2164, 2164, 2224, 2225, + 2224, 2285, 2225, 2225, 2285, 2286, 2285, 2346, 2286, 2286, 2346, 2347, + 2346, 2407, 2347, 2347, 2407, 2408, 2407, 2468, 2408, 2408, 2468, 2469, + 2468, 2529, 2469, 2469, 2529, 2530, 2529, 2590, 2530, 2530, 2590, 2591, + 2590, 2651, 2591, 2591, 2651, 2652, 2651, 2712, 2652, 2652, 2712, 2713, + 2712, 2773, 2713, 2713, 2773, 2774, 2773, 2834, 2774, 2774, 2834, 2835, + 2834, 2895, 2835, 2835, 2895, 2896, 2895, 2956, 2896, 2896, 2956, 2957, + 2956, 3017, 2957, 2957, 3017, 3018, 3017, 3078, 3018, 3018, 3078, 3079, + 3078, 3139, 3079, 3079, 3139, 3140, 3139, 3200, 3140, 3140, 3200, 3201, + 3200, 3261, 3201, 3201, 3261, 3262, 3261, 3322, 3262, 3262, 3322, 3323, + 3322, 3383, 3323, 3323, 3383, 3384, 3383, 3444, 3384, 3384, 3444, 3445, + 3444, 3505, 3445, 3445, 3505, 3506, 3505, 3566, 3506, 3506, 3566, 3567, + 3566, 3627, 3567, 3567, 3627, 3628, 3627, 3688, 3628, 3628, 3688, 3689, + 29, 90, 30, 30, 90, 91, 90, 151, 91, 91, 151, 152, + 151, 212, 152, 152, 212, 213, 212, 273, 213, 213, 273, 274, + 273, 334, 274, 274, 334, 335, 334, 395, 335, 335, 395, 396, + 395, 456, 396, 396, 456, 457, 456, 517, 457, 457, 517, 518, + 517, 578, 518, 518, 578, 579, 578, 639, 579, 579, 639, 640, + 639, 700, 640, 640, 700, 701, 700, 761, 701, 701, 761, 762, + 761, 822, 762, 762, 822, 823, 822, 883, 823, 823, 883, 884, + 883, 944, 884, 884, 944, 945, 944, 1005, 945, 945, 1005, 1006, + 1005, 1066, 1006, 1006, 1066, 1067, 1066, 1127, 1067, 1067, 1127, 1128, + 1127, 1188, 1128, 1128, 1188, 1189, 1188, 1249, 1189, 1189, 1249, 1250, + 1249, 1310, 1250, 1250, 1310, 1311, 1310, 1371, 1311, 1311, 1371, 1372, + 1371, 1432, 1372, 1372, 1432, 1433, 1432, 1493, 1433, 1433, 1493, 1494, + 1493, 1554, 1494, 1494, 1554, 1555, 1554, 1615, 1555, 1555, 1615, 1616, + 1615, 1676, 1616, 1616, 1676, 1677, 1676, 1737, 1677, 1677, 1737, 1738, + 1737, 1798, 1738, 1738, 1798, 1799, 1798, 1859, 1799, 1799, 1859, 1860, + 1859, 1920, 1860, 1860, 1920, 1921, 1920, 1981, 1921, 1921, 1981, 1982, + 1981, 2042, 1982, 1982, 2042, 2043, 2042, 2103, 2043, 2043, 2103, 2104, + 2103, 2164, 2104, 2104, 2164, 2165, 2164, 2225, 2165, 2165, 2225, 2226, + 2225, 2286, 2226, 2226, 2286, 2287, 2286, 2347, 2287, 2287, 2347, 2348, + 2347, 2408, 2348, 2348, 2408, 2409, 2408, 2469, 2409, 2409, 2469, 2470, + 2469, 2530, 2470, 2470, 2530, 2531, 2530, 2591, 2531, 2531, 2591, 2592, + 2591, 2652, 2592, 2592, 2652, 2653, 2652, 2713, 2653, 2653, 2713, 2714, + 2713, 2774, 2714, 2714, 2774, 2775, 2774, 2835, 2775, 2775, 2835, 2836, + 2835, 2896, 2836, 2836, 2896, 2897, 2896, 2957, 2897, 2897, 2957, 2958, + 2957, 3018, 2958, 2958, 3018, 3019, 3018, 3079, 3019, 3019, 3079, 3080, + 3079, 3140, 3080, 3080, 3140, 3141, 3140, 3201, 3141, 3141, 3201, 3202, + 3201, 3262, 3202, 3202, 3262, 3263, 3262, 3323, 3263, 3263, 3323, 3324, + 3323, 3384, 3324, 3324, 3384, 3385, 3384, 3445, 3385, 3385, 3445, 3446, + 3445, 3506, 3446, 3446, 3506, 3507, 3506, 3567, 3507, 3507, 3567, 3568, + 3567, 3628, 3568, 3568, 3628, 3629, 3628, 3689, 3629, 3629, 3689, 3690, + 30, 91, 31, 31, 91, 92, 91, 152, 92, 92, 152, 153, + 152, 213, 153, 153, 213, 214, 213, 274, 214, 214, 274, 275, + 274, 335, 275, 275, 335, 336, 335, 396, 336, 336, 396, 397, + 396, 457, 397, 397, 457, 458, 457, 518, 458, 458, 518, 519, + 518, 579, 519, 519, 579, 580, 579, 640, 580, 580, 640, 641, + 640, 701, 641, 641, 701, 702, 701, 762, 702, 702, 762, 763, + 762, 823, 763, 763, 823, 824, 823, 884, 824, 824, 884, 885, + 884, 945, 885, 885, 945, 946, 945, 1006, 946, 946, 1006, 1007, + 1006, 1067, 1007, 1007, 1067, 1068, 1067, 1128, 1068, 1068, 1128, 1129, + 1128, 1189, 1129, 1129, 1189, 1190, 1189, 1250, 1190, 1190, 1250, 1251, + 1250, 1311, 1251, 1251, 1311, 1312, 1311, 1372, 1312, 1312, 1372, 1373, + 1372, 1433, 1373, 1373, 1433, 1434, 1433, 1494, 1434, 1434, 1494, 1495, + 1494, 1555, 1495, 1495, 1555, 1556, 1555, 1616, 1556, 1556, 1616, 1617, + 1616, 1677, 1617, 1617, 1677, 1678, 1677, 1738, 1678, 1678, 1738, 1739, + 1738, 1799, 1739, 1739, 1799, 1800, 1799, 1860, 1800, 1800, 1860, 1861, + 1860, 1921, 1861, 1861, 1921, 1922, 1921, 1982, 1922, 1922, 1982, 1983, + 1982, 2043, 1983, 1983, 2043, 2044, 2043, 2104, 2044, 2044, 2104, 2105, + 2104, 2165, 2105, 2105, 2165, 2166, 2165, 2226, 2166, 2166, 2226, 2227, + 2226, 2287, 2227, 2227, 2287, 2288, 2287, 2348, 2288, 2288, 2348, 2349, + 2348, 2409, 2349, 2349, 2409, 2410, 2409, 2470, 2410, 2410, 2470, 2471, + 2470, 2531, 2471, 2471, 2531, 2532, 2531, 2592, 2532, 2532, 2592, 2593, + 2592, 2653, 2593, 2593, 2653, 2654, 2653, 2714, 2654, 2654, 2714, 2715, + 2714, 2775, 2715, 2715, 2775, 2776, 2775, 2836, 2776, 2776, 2836, 2837, + 2836, 2897, 2837, 2837, 2897, 2898, 2897, 2958, 2898, 2898, 2958, 2959, + 2958, 3019, 2959, 2959, 3019, 3020, 3019, 3080, 3020, 3020, 3080, 3081, + 3080, 3141, 3081, 3081, 3141, 3142, 3141, 3202, 3142, 3142, 3202, 3203, + 3202, 3263, 3203, 3203, 3263, 3264, 3263, 3324, 3264, 3264, 3324, 3325, + 3324, 3385, 3325, 3325, 3385, 3386, 3385, 3446, 3386, 3386, 3446, 3447, + 3446, 3507, 3447, 3447, 3507, 3508, 3507, 3568, 3508, 3508, 3568, 3569, + 3568, 3629, 3569, 3569, 3629, 3630, 3629, 3690, 3630, 3630, 3690, 3691, + 31, 92, 32, 32, 92, 93, 92, 153, 93, 93, 153, 154, + 153, 214, 154, 154, 214, 215, 214, 275, 215, 215, 275, 276, + 275, 336, 276, 276, 336, 337, 336, 397, 337, 337, 397, 398, + 397, 458, 398, 398, 458, 459, 458, 519, 459, 459, 519, 520, + 519, 580, 520, 520, 580, 581, 580, 641, 581, 581, 641, 642, + 641, 702, 642, 642, 702, 703, 702, 763, 703, 703, 763, 764, + 763, 824, 764, 764, 824, 825, 824, 885, 825, 825, 885, 886, + 885, 946, 886, 886, 946, 947, 946, 1007, 947, 947, 1007, 1008, + 1007, 1068, 1008, 1008, 1068, 1069, 1068, 1129, 1069, 1069, 1129, 1130, + 1129, 1190, 1130, 1130, 1190, 1191, 1190, 1251, 1191, 1191, 1251, 1252, + 1251, 1312, 1252, 1252, 1312, 1313, 1312, 1373, 1313, 1313, 1373, 1374, + 1373, 1434, 1374, 1374, 1434, 1435, 1434, 1495, 1435, 1435, 1495, 1496, + 1495, 1556, 1496, 1496, 1556, 1557, 1556, 1617, 1557, 1557, 1617, 1618, + 1617, 1678, 1618, 1618, 1678, 1679, 1678, 1739, 1679, 1679, 1739, 1740, + 1739, 1800, 1740, 1740, 1800, 1801, 1800, 1861, 1801, 1801, 1861, 1862, + 1861, 1922, 1862, 1862, 1922, 1923, 1922, 1983, 1923, 1923, 1983, 1984, + 1983, 2044, 1984, 1984, 2044, 2045, 2044, 2105, 2045, 2045, 2105, 2106, + 2105, 2166, 2106, 2106, 2166, 2167, 2166, 2227, 2167, 2167, 2227, 2228, + 2227, 2288, 2228, 2228, 2288, 2289, 2288, 2349, 2289, 2289, 2349, 2350, + 2349, 2410, 2350, 2350, 2410, 2411, 2410, 2471, 2411, 2411, 2471, 2472, + 2471, 2532, 2472, 2472, 2532, 2533, 2532, 2593, 2533, 2533, 2593, 2594, + 2593, 2654, 2594, 2594, 2654, 2655, 2654, 2715, 2655, 2655, 2715, 2716, + 2715, 2776, 2716, 2716, 2776, 2777, 2776, 2837, 2777, 2777, 2837, 2838, + 2837, 2898, 2838, 2838, 2898, 2899, 2898, 2959, 2899, 2899, 2959, 2960, + 2959, 3020, 2960, 2960, 3020, 3021, 3020, 3081, 3021, 3021, 3081, 3082, + 3081, 3142, 3082, 3082, 3142, 3143, 3142, 3203, 3143, 3143, 3203, 3204, + 3203, 3264, 3204, 3204, 3264, 3265, 3264, 3325, 3265, 3265, 3325, 3326, + 3325, 3386, 3326, 3326, 3386, 3387, 3386, 3447, 3387, 3387, 3447, 3448, + 3447, 3508, 3448, 3448, 3508, 3509, 3508, 3569, 3509, 3509, 3569, 3570, + 3569, 3630, 3570, 3570, 3630, 3631, 3630, 3691, 3631, 3631, 3691, 3692, + 32, 93, 33, 33, 93, 94, 93, 154, 94, 94, 154, 155, + 154, 215, 155, 155, 215, 216, 215, 276, 216, 216, 276, 277, + 276, 337, 277, 277, 337, 338, 337, 398, 338, 338, 398, 399, + 398, 459, 399, 399, 459, 460, 459, 520, 460, 460, 520, 521, + 520, 581, 521, 521, 581, 582, 581, 642, 582, 582, 642, 643, + 642, 703, 643, 643, 703, 704, 703, 764, 704, 704, 764, 765, + 764, 825, 765, 765, 825, 826, 825, 886, 826, 826, 886, 887, + 886, 947, 887, 887, 947, 948, 947, 1008, 948, 948, 1008, 1009, + 1008, 1069, 1009, 1009, 1069, 1070, 1069, 1130, 1070, 1070, 1130, 1131, + 1130, 1191, 1131, 1131, 1191, 1192, 1191, 1252, 1192, 1192, 1252, 1253, + 1252, 1313, 1253, 1253, 1313, 1314, 1313, 1374, 1314, 1314, 1374, 1375, + 1374, 1435, 1375, 1375, 1435, 1436, 1435, 1496, 1436, 1436, 1496, 1497, + 1496, 1557, 1497, 1497, 1557, 1558, 1557, 1618, 1558, 1558, 1618, 1619, + 1618, 1679, 1619, 1619, 1679, 1680, 1679, 1740, 1680, 1680, 1740, 1741, + 1740, 1801, 1741, 1741, 1801, 1802, 1801, 1862, 1802, 1802, 1862, 1863, + 1862, 1923, 1863, 1863, 1923, 1924, 1923, 1984, 1924, 1924, 1984, 1985, + 1984, 2045, 1985, 1985, 2045, 2046, 2045, 2106, 2046, 2046, 2106, 2107, + 2106, 2167, 2107, 2107, 2167, 2168, 2167, 2228, 2168, 2168, 2228, 2229, + 2228, 2289, 2229, 2229, 2289, 2290, 2289, 2350, 2290, 2290, 2350, 2351, + 2350, 2411, 2351, 2351, 2411, 2412, 2411, 2472, 2412, 2412, 2472, 2473, + 2472, 2533, 2473, 2473, 2533, 2534, 2533, 2594, 2534, 2534, 2594, 2595, + 2594, 2655, 2595, 2595, 2655, 2656, 2655, 2716, 2656, 2656, 2716, 2717, + 2716, 2777, 2717, 2717, 2777, 2778, 2777, 2838, 2778, 2778, 2838, 2839, + 2838, 2899, 2839, 2839, 2899, 2900, 2899, 2960, 2900, 2900, 2960, 2961, + 2960, 3021, 2961, 2961, 3021, 3022, 3021, 3082, 3022, 3022, 3082, 3083, + 3082, 3143, 3083, 3083, 3143, 3144, 3143, 3204, 3144, 3144, 3204, 3205, + 3204, 3265, 3205, 3205, 3265, 3266, 3265, 3326, 3266, 3266, 3326, 3327, + 3326, 3387, 3327, 3327, 3387, 3388, 3387, 3448, 3388, 3388, 3448, 3449, + 3448, 3509, 3449, 3449, 3509, 3510, 3509, 3570, 3510, 3510, 3570, 3571, + 3570, 3631, 3571, 3571, 3631, 3632, 3631, 3692, 3632, 3632, 3692, 3693, + 33, 94, 34, 34, 94, 95, 94, 155, 95, 95, 155, 156, + 155, 216, 156, 156, 216, 217, 216, 277, 217, 217, 277, 278, + 277, 338, 278, 278, 338, 339, 338, 399, 339, 339, 399, 400, + 399, 460, 400, 400, 460, 461, 460, 521, 461, 461, 521, 522, + 521, 582, 522, 522, 582, 583, 582, 643, 583, 583, 643, 644, + 643, 704, 644, 644, 704, 705, 704, 765, 705, 705, 765, 766, + 765, 826, 766, 766, 826, 827, 826, 887, 827, 827, 887, 888, + 887, 948, 888, 888, 948, 949, 948, 1009, 949, 949, 1009, 1010, + 1009, 1070, 1010, 1010, 1070, 1071, 1070, 1131, 1071, 1071, 1131, 1132, + 1131, 1192, 1132, 1132, 1192, 1193, 1192, 1253, 1193, 1193, 1253, 1254, + 1253, 1314, 1254, 1254, 1314, 1315, 1314, 1375, 1315, 1315, 1375, 1376, + 1375, 1436, 1376, 1376, 1436, 1437, 1436, 1497, 1437, 1437, 1497, 1498, + 1497, 1558, 1498, 1498, 1558, 1559, 1558, 1619, 1559, 1559, 1619, 1620, + 1619, 1680, 1620, 1620, 1680, 1681, 1680, 1741, 1681, 1681, 1741, 1742, + 1741, 1802, 1742, 1742, 1802, 1803, 1802, 1863, 1803, 1803, 1863, 1864, + 1863, 1924, 1864, 1864, 1924, 1925, 1924, 1985, 1925, 1925, 1985, 1986, + 1985, 2046, 1986, 1986, 2046, 2047, 2046, 2107, 2047, 2047, 2107, 2108, + 2107, 2168, 2108, 2108, 2168, 2169, 2168, 2229, 2169, 2169, 2229, 2230, + 2229, 2290, 2230, 2230, 2290, 2291, 2290, 2351, 2291, 2291, 2351, 2352, + 2351, 2412, 2352, 2352, 2412, 2413, 2412, 2473, 2413, 2413, 2473, 2474, + 2473, 2534, 2474, 2474, 2534, 2535, 2534, 2595, 2535, 2535, 2595, 2596, + 2595, 2656, 2596, 2596, 2656, 2657, 2656, 2717, 2657, 2657, 2717, 2718, + 2717, 2778, 2718, 2718, 2778, 2779, 2778, 2839, 2779, 2779, 2839, 2840, + 2839, 2900, 2840, 2840, 2900, 2901, 2900, 2961, 2901, 2901, 2961, 2962, + 2961, 3022, 2962, 2962, 3022, 3023, 3022, 3083, 3023, 3023, 3083, 3084, + 3083, 3144, 3084, 3084, 3144, 3145, 3144, 3205, 3145, 3145, 3205, 3206, + 3205, 3266, 3206, 3206, 3266, 3267, 3266, 3327, 3267, 3267, 3327, 3328, + 3327, 3388, 3328, 3328, 3388, 3389, 3388, 3449, 3389, 3389, 3449, 3450, + 3449, 3510, 3450, 3450, 3510, 3511, 3510, 3571, 3511, 3511, 3571, 3572, + 3571, 3632, 3572, 3572, 3632, 3633, 3632, 3693, 3633, 3633, 3693, 3694, + 34, 95, 35, 35, 95, 96, 95, 156, 96, 96, 156, 157, + 156, 217, 157, 157, 217, 218, 217, 278, 218, 218, 278, 279, + 278, 339, 279, 279, 339, 340, 339, 400, 340, 340, 400, 401, + 400, 461, 401, 401, 461, 462, 461, 522, 462, 462, 522, 523, + 522, 583, 523, 523, 583, 584, 583, 644, 584, 584, 644, 645, + 644, 705, 645, 645, 705, 706, 705, 766, 706, 706, 766, 767, + 766, 827, 767, 767, 827, 828, 827, 888, 828, 828, 888, 889, + 888, 949, 889, 889, 949, 950, 949, 1010, 950, 950, 1010, 1011, + 1010, 1071, 1011, 1011, 1071, 1072, 1071, 1132, 1072, 1072, 1132, 1133, + 1132, 1193, 1133, 1133, 1193, 1194, 1193, 1254, 1194, 1194, 1254, 1255, + 1254, 1315, 1255, 1255, 1315, 1316, 1315, 1376, 1316, 1316, 1376, 1377, + 1376, 1437, 1377, 1377, 1437, 1438, 1437, 1498, 1438, 1438, 1498, 1499, + 1498, 1559, 1499, 1499, 1559, 1560, 1559, 1620, 1560, 1560, 1620, 1621, + 1620, 1681, 1621, 1621, 1681, 1682, 1681, 1742, 1682, 1682, 1742, 1743, + 1742, 1803, 1743, 1743, 1803, 1804, 1803, 1864, 1804, 1804, 1864, 1865, + 1864, 1925, 1865, 1865, 1925, 1926, 1925, 1986, 1926, 1926, 1986, 1987, + 1986, 2047, 1987, 1987, 2047, 2048, 2047, 2108, 2048, 2048, 2108, 2109, + 2108, 2169, 2109, 2109, 2169, 2170, 2169, 2230, 2170, 2170, 2230, 2231, + 2230, 2291, 2231, 2231, 2291, 2292, 2291, 2352, 2292, 2292, 2352, 2353, + 2352, 2413, 2353, 2353, 2413, 2414, 2413, 2474, 2414, 2414, 2474, 2475, + 2474, 2535, 2475, 2475, 2535, 2536, 2535, 2596, 2536, 2536, 2596, 2597, + 2596, 2657, 2597, 2597, 2657, 2658, 2657, 2718, 2658, 2658, 2718, 2719, + 2718, 2779, 2719, 2719, 2779, 2780, 2779, 2840, 2780, 2780, 2840, 2841, + 2840, 2901, 2841, 2841, 2901, 2902, 2901, 2962, 2902, 2902, 2962, 2963, + 2962, 3023, 2963, 2963, 3023, 3024, 3023, 3084, 3024, 3024, 3084, 3085, + 3084, 3145, 3085, 3085, 3145, 3146, 3145, 3206, 3146, 3146, 3206, 3207, + 3206, 3267, 3207, 3207, 3267, 3268, 3267, 3328, 3268, 3268, 3328, 3329, + 3328, 3389, 3329, 3329, 3389, 3390, 3389, 3450, 3390, 3390, 3450, 3451, + 3450, 3511, 3451, 3451, 3511, 3512, 3511, 3572, 3512, 3512, 3572, 3573, + 3572, 3633, 3573, 3573, 3633, 3634, 3633, 3694, 3634, 3634, 3694, 3695, + 35, 96, 36, 36, 96, 97, 96, 157, 97, 97, 157, 158, + 157, 218, 158, 158, 218, 219, 218, 279, 219, 219, 279, 280, + 279, 340, 280, 280, 340, 341, 340, 401, 341, 341, 401, 402, + 401, 462, 402, 402, 462, 463, 462, 523, 463, 463, 523, 524, + 523, 584, 524, 524, 584, 585, 584, 645, 585, 585, 645, 646, + 645, 706, 646, 646, 706, 707, 706, 767, 707, 707, 767, 768, + 767, 828, 768, 768, 828, 829, 828, 889, 829, 829, 889, 890, + 889, 950, 890, 890, 950, 951, 950, 1011, 951, 951, 1011, 1012, + 1011, 1072, 1012, 1012, 1072, 1073, 1072, 1133, 1073, 1073, 1133, 1134, + 1133, 1194, 1134, 1134, 1194, 1195, 1194, 1255, 1195, 1195, 1255, 1256, + 1255, 1316, 1256, 1256, 1316, 1317, 1316, 1377, 1317, 1317, 1377, 1378, + 1377, 1438, 1378, 1378, 1438, 1439, 1438, 1499, 1439, 1439, 1499, 1500, + 1499, 1560, 1500, 1500, 1560, 1561, 1560, 1621, 1561, 1561, 1621, 1622, + 1621, 1682, 1622, 1622, 1682, 1683, 1682, 1743, 1683, 1683, 1743, 1744, + 1743, 1804, 1744, 1744, 1804, 1805, 1804, 1865, 1805, 1805, 1865, 1866, + 1865, 1926, 1866, 1866, 1926, 1927, 1926, 1987, 1927, 1927, 1987, 1988, + 1987, 2048, 1988, 1988, 2048, 2049, 2048, 2109, 2049, 2049, 2109, 2110, + 2109, 2170, 2110, 2110, 2170, 2171, 2170, 2231, 2171, 2171, 2231, 2232, + 2231, 2292, 2232, 2232, 2292, 2293, 2292, 2353, 2293, 2293, 2353, 2354, + 2353, 2414, 2354, 2354, 2414, 2415, 2414, 2475, 2415, 2415, 2475, 2476, + 2475, 2536, 2476, 2476, 2536, 2537, 2536, 2597, 2537, 2537, 2597, 2598, + 2597, 2658, 2598, 2598, 2658, 2659, 2658, 2719, 2659, 2659, 2719, 2720, + 2719, 2780, 2720, 2720, 2780, 2781, 2780, 2841, 2781, 2781, 2841, 2842, + 2841, 2902, 2842, 2842, 2902, 2903, 2902, 2963, 2903, 2903, 2963, 2964, + 2963, 3024, 2964, 2964, 3024, 3025, 3024, 3085, 3025, 3025, 3085, 3086, + 3085, 3146, 3086, 3086, 3146, 3147, 3146, 3207, 3147, 3147, 3207, 3208, + 3207, 3268, 3208, 3208, 3268, 3269, 3268, 3329, 3269, 3269, 3329, 3330, + 3329, 3390, 3330, 3330, 3390, 3391, 3390, 3451, 3391, 3391, 3451, 3452, + 3451, 3512, 3452, 3452, 3512, 3513, 3512, 3573, 3513, 3513, 3573, 3574, + 3573, 3634, 3574, 3574, 3634, 3635, 3634, 3695, 3635, 3635, 3695, 3696, + 36, 97, 37, 37, 97, 98, 97, 158, 98, 98, 158, 159, + 158, 219, 159, 159, 219, 220, 219, 280, 220, 220, 280, 281, + 280, 341, 281, 281, 341, 342, 341, 402, 342, 342, 402, 403, + 402, 463, 403, 403, 463, 464, 463, 524, 464, 464, 524, 525, + 524, 585, 525, 525, 585, 586, 585, 646, 586, 586, 646, 647, + 646, 707, 647, 647, 707, 708, 707, 768, 708, 708, 768, 769, + 768, 829, 769, 769, 829, 830, 829, 890, 830, 830, 890, 891, + 890, 951, 891, 891, 951, 952, 951, 1012, 952, 952, 1012, 1013, + 1012, 1073, 1013, 1013, 1073, 1074, 1073, 1134, 1074, 1074, 1134, 1135, + 1134, 1195, 1135, 1135, 1195, 1196, 1195, 1256, 1196, 1196, 1256, 1257, + 1256, 1317, 1257, 1257, 1317, 1318, 1317, 1378, 1318, 1318, 1378, 1379, + 1378, 1439, 1379, 1379, 1439, 1440, 1439, 1500, 1440, 1440, 1500, 1501, + 1500, 1561, 1501, 1501, 1561, 1562, 1561, 1622, 1562, 1562, 1622, 1623, + 1622, 1683, 1623, 1623, 1683, 1684, 1683, 1744, 1684, 1684, 1744, 1745, + 1744, 1805, 1745, 1745, 1805, 1806, 1805, 1866, 1806, 1806, 1866, 1867, + 1866, 1927, 1867, 1867, 1927, 1928, 1927, 1988, 1928, 1928, 1988, 1989, + 1988, 2049, 1989, 1989, 2049, 2050, 2049, 2110, 2050, 2050, 2110, 2111, + 2110, 2171, 2111, 2111, 2171, 2172, 2171, 2232, 2172, 2172, 2232, 2233, + 2232, 2293, 2233, 2233, 2293, 2294, 2293, 2354, 2294, 2294, 2354, 2355, + 2354, 2415, 2355, 2355, 2415, 2416, 2415, 2476, 2416, 2416, 2476, 2477, + 2476, 2537, 2477, 2477, 2537, 2538, 2537, 2598, 2538, 2538, 2598, 2599, + 2598, 2659, 2599, 2599, 2659, 2660, 2659, 2720, 2660, 2660, 2720, 2721, + 2720, 2781, 2721, 2721, 2781, 2782, 2781, 2842, 2782, 2782, 2842, 2843, + 2842, 2903, 2843, 2843, 2903, 2904, 2903, 2964, 2904, 2904, 2964, 2965, + 2964, 3025, 2965, 2965, 3025, 3026, 3025, 3086, 3026, 3026, 3086, 3087, + 3086, 3147, 3087, 3087, 3147, 3148, 3147, 3208, 3148, 3148, 3208, 3209, + 3208, 3269, 3209, 3209, 3269, 3270, 3269, 3330, 3270, 3270, 3330, 3331, + 3330, 3391, 3331, 3331, 3391, 3392, 3391, 3452, 3392, 3392, 3452, 3453, + 3452, 3513, 3453, 3453, 3513, 3514, 3513, 3574, 3514, 3514, 3574, 3575, + 3574, 3635, 3575, 3575, 3635, 3636, 3635, 3696, 3636, 3636, 3696, 3697, + 37, 98, 38, 38, 98, 99, 98, 159, 99, 99, 159, 160, + 159, 220, 160, 160, 220, 221, 220, 281, 221, 221, 281, 282, + 281, 342, 282, 282, 342, 343, 342, 403, 343, 343, 403, 404, + 403, 464, 404, 404, 464, 465, 464, 525, 465, 465, 525, 526, + 525, 586, 526, 526, 586, 587, 586, 647, 587, 587, 647, 648, + 647, 708, 648, 648, 708, 709, 708, 769, 709, 709, 769, 770, + 769, 830, 770, 770, 830, 831, 830, 891, 831, 831, 891, 892, + 891, 952, 892, 892, 952, 953, 952, 1013, 953, 953, 1013, 1014, + 1013, 1074, 1014, 1014, 1074, 1075, 1074, 1135, 1075, 1075, 1135, 1136, + 1135, 1196, 1136, 1136, 1196, 1197, 1196, 1257, 1197, 1197, 1257, 1258, + 1257, 1318, 1258, 1258, 1318, 1319, 1318, 1379, 1319, 1319, 1379, 1380, + 1379, 1440, 1380, 1380, 1440, 1441, 1440, 1501, 1441, 1441, 1501, 1502, + 1501, 1562, 1502, 1502, 1562, 1563, 1562, 1623, 1563, 1563, 1623, 1624, + 1623, 1684, 1624, 1624, 1684, 1685, 1684, 1745, 1685, 1685, 1745, 1746, + 1745, 1806, 1746, 1746, 1806, 1807, 1806, 1867, 1807, 1807, 1867, 1868, + 1867, 1928, 1868, 1868, 1928, 1929, 1928, 1989, 1929, 1929, 1989, 1990, + 1989, 2050, 1990, 1990, 2050, 2051, 2050, 2111, 2051, 2051, 2111, 2112, + 2111, 2172, 2112, 2112, 2172, 2173, 2172, 2233, 2173, 2173, 2233, 2234, + 2233, 2294, 2234, 2234, 2294, 2295, 2294, 2355, 2295, 2295, 2355, 2356, + 2355, 2416, 2356, 2356, 2416, 2417, 2416, 2477, 2417, 2417, 2477, 2478, + 2477, 2538, 2478, 2478, 2538, 2539, 2538, 2599, 2539, 2539, 2599, 2600, + 2599, 2660, 2600, 2600, 2660, 2661, 2660, 2721, 2661, 2661, 2721, 2722, + 2721, 2782, 2722, 2722, 2782, 2783, 2782, 2843, 2783, 2783, 2843, 2844, + 2843, 2904, 2844, 2844, 2904, 2905, 2904, 2965, 2905, 2905, 2965, 2966, + 2965, 3026, 2966, 2966, 3026, 3027, 3026, 3087, 3027, 3027, 3087, 3088, + 3087, 3148, 3088, 3088, 3148, 3149, 3148, 3209, 3149, 3149, 3209, 3210, + 3209, 3270, 3210, 3210, 3270, 3271, 3270, 3331, 3271, 3271, 3331, 3332, + 3331, 3392, 3332, 3332, 3392, 3393, 3392, 3453, 3393, 3393, 3453, 3454, + 3453, 3514, 3454, 3454, 3514, 3515, 3514, 3575, 3515, 3515, 3575, 3576, + 3575, 3636, 3576, 3576, 3636, 3637, 3636, 3697, 3637, 3637, 3697, 3698, + 38, 99, 39, 39, 99, 100, 99, 160, 100, 100, 160, 161, + 160, 221, 161, 161, 221, 222, 221, 282, 222, 222, 282, 283, + 282, 343, 283, 283, 343, 344, 343, 404, 344, 344, 404, 405, + 404, 465, 405, 405, 465, 466, 465, 526, 466, 466, 526, 527, + 526, 587, 527, 527, 587, 588, 587, 648, 588, 588, 648, 649, + 648, 709, 649, 649, 709, 710, 709, 770, 710, 710, 770, 771, + 770, 831, 771, 771, 831, 832, 831, 892, 832, 832, 892, 893, + 892, 953, 893, 893, 953, 954, 953, 1014, 954, 954, 1014, 1015, + 1014, 1075, 1015, 1015, 1075, 1076, 1075, 1136, 1076, 1076, 1136, 1137, + 1136, 1197, 1137, 1137, 1197, 1198, 1197, 1258, 1198, 1198, 1258, 1259, + 1258, 1319, 1259, 1259, 1319, 1320, 1319, 1380, 1320, 1320, 1380, 1381, + 1380, 1441, 1381, 1381, 1441, 1442, 1441, 1502, 1442, 1442, 1502, 1503, + 1502, 1563, 1503, 1503, 1563, 1564, 1563, 1624, 1564, 1564, 1624, 1625, + 1624, 1685, 1625, 1625, 1685, 1686, 1685, 1746, 1686, 1686, 1746, 1747, + 1746, 1807, 1747, 1747, 1807, 1808, 1807, 1868, 1808, 1808, 1868, 1869, + 1868, 1929, 1869, 1869, 1929, 1930, 1929, 1990, 1930, 1930, 1990, 1991, + 1990, 2051, 1991, 1991, 2051, 2052, 2051, 2112, 2052, 2052, 2112, 2113, + 2112, 2173, 2113, 2113, 2173, 2174, 2173, 2234, 2174, 2174, 2234, 2235, + 2234, 2295, 2235, 2235, 2295, 2296, 2295, 2356, 2296, 2296, 2356, 2357, + 2356, 2417, 2357, 2357, 2417, 2418, 2417, 2478, 2418, 2418, 2478, 2479, + 2478, 2539, 2479, 2479, 2539, 2540, 2539, 2600, 2540, 2540, 2600, 2601, + 2600, 2661, 2601, 2601, 2661, 2662, 2661, 2722, 2662, 2662, 2722, 2723, + 2722, 2783, 2723, 2723, 2783, 2784, 2783, 2844, 2784, 2784, 2844, 2845, + 2844, 2905, 2845, 2845, 2905, 2906, 2905, 2966, 2906, 2906, 2966, 2967, + 2966, 3027, 2967, 2967, 3027, 3028, 3027, 3088, 3028, 3028, 3088, 3089, + 3088, 3149, 3089, 3089, 3149, 3150, 3149, 3210, 3150, 3150, 3210, 3211, + 3210, 3271, 3211, 3211, 3271, 3272, 3271, 3332, 3272, 3272, 3332, 3333, + 3332, 3393, 3333, 3333, 3393, 3394, 3393, 3454, 3394, 3394, 3454, 3455, + 3454, 3515, 3455, 3455, 3515, 3516, 3515, 3576, 3516, 3516, 3576, 3577, + 3576, 3637, 3577, 3577, 3637, 3638, 3637, 3698, 3638, 3638, 3698, 3699, + 39, 100, 40, 40, 100, 101, 100, 161, 101, 101, 161, 162, + 161, 222, 162, 162, 222, 223, 222, 283, 223, 223, 283, 284, + 283, 344, 284, 284, 344, 345, 344, 405, 345, 345, 405, 406, + 405, 466, 406, 406, 466, 467, 466, 527, 467, 467, 527, 528, + 527, 588, 528, 528, 588, 589, 588, 649, 589, 589, 649, 650, + 649, 710, 650, 650, 710, 711, 710, 771, 711, 711, 771, 772, + 771, 832, 772, 772, 832, 833, 832, 893, 833, 833, 893, 894, + 893, 954, 894, 894, 954, 955, 954, 1015, 955, 955, 1015, 1016, + 1015, 1076, 1016, 1016, 1076, 1077, 1076, 1137, 1077, 1077, 1137, 1138, + 1137, 1198, 1138, 1138, 1198, 1199, 1198, 1259, 1199, 1199, 1259, 1260, + 1259, 1320, 1260, 1260, 1320, 1321, 1320, 1381, 1321, 1321, 1381, 1382, + 1381, 1442, 1382, 1382, 1442, 1443, 1442, 1503, 1443, 1443, 1503, 1504, + 1503, 1564, 1504, 1504, 1564, 1565, 1564, 1625, 1565, 1565, 1625, 1626, + 1625, 1686, 1626, 1626, 1686, 1687, 1686, 1747, 1687, 1687, 1747, 1748, + 1747, 1808, 1748, 1748, 1808, 1809, 1808, 1869, 1809, 1809, 1869, 1870, + 1869, 1930, 1870, 1870, 1930, 1931, 1930, 1991, 1931, 1931, 1991, 1992, + 1991, 2052, 1992, 1992, 2052, 2053, 2052, 2113, 2053, 2053, 2113, 2114, + 2113, 2174, 2114, 2114, 2174, 2175, 2174, 2235, 2175, 2175, 2235, 2236, + 2235, 2296, 2236, 2236, 2296, 2297, 2296, 2357, 2297, 2297, 2357, 2358, + 2357, 2418, 2358, 2358, 2418, 2419, 2418, 2479, 2419, 2419, 2479, 2480, + 2479, 2540, 2480, 2480, 2540, 2541, 2540, 2601, 2541, 2541, 2601, 2602, + 2601, 2662, 2602, 2602, 2662, 2663, 2662, 2723, 2663, 2663, 2723, 2724, + 2723, 2784, 2724, 2724, 2784, 2785, 2784, 2845, 2785, 2785, 2845, 2846, + 2845, 2906, 2846, 2846, 2906, 2907, 2906, 2967, 2907, 2907, 2967, 2968, + 2967, 3028, 2968, 2968, 3028, 3029, 3028, 3089, 3029, 3029, 3089, 3090, + 3089, 3150, 3090, 3090, 3150, 3151, 3150, 3211, 3151, 3151, 3211, 3212, + 3211, 3272, 3212, 3212, 3272, 3273, 3272, 3333, 3273, 3273, 3333, 3334, + 3333, 3394, 3334, 3334, 3394, 3395, 3394, 3455, 3395, 3395, 3455, 3456, + 3455, 3516, 3456, 3456, 3516, 3517, 3516, 3577, 3517, 3517, 3577, 3578, + 3577, 3638, 3578, 3578, 3638, 3639, 3638, 3699, 3639, 3639, 3699, 3700, + 40, 101, 41, 41, 101, 102, 101, 162, 102, 102, 162, 163, + 162, 223, 163, 163, 223, 224, 223, 284, 224, 224, 284, 285, + 284, 345, 285, 285, 345, 346, 345, 406, 346, 346, 406, 407, + 406, 467, 407, 407, 467, 468, 467, 528, 468, 468, 528, 529, + 528, 589, 529, 529, 589, 590, 589, 650, 590, 590, 650, 651, + 650, 711, 651, 651, 711, 712, 711, 772, 712, 712, 772, 773, + 772, 833, 773, 773, 833, 834, 833, 894, 834, 834, 894, 895, + 894, 955, 895, 895, 955, 956, 955, 1016, 956, 956, 1016, 1017, + 1016, 1077, 1017, 1017, 1077, 1078, 1077, 1138, 1078, 1078, 1138, 1139, + 1138, 1199, 1139, 1139, 1199, 1200, 1199, 1260, 1200, 1200, 1260, 1261, + 1260, 1321, 1261, 1261, 1321, 1322, 1321, 1382, 1322, 1322, 1382, 1383, + 1382, 1443, 1383, 1383, 1443, 1444, 1443, 1504, 1444, 1444, 1504, 1505, + 1504, 1565, 1505, 1505, 1565, 1566, 1565, 1626, 1566, 1566, 1626, 1627, + 1626, 1687, 1627, 1627, 1687, 1688, 1687, 1748, 1688, 1688, 1748, 1749, + 1748, 1809, 1749, 1749, 1809, 1810, 1809, 1870, 1810, 1810, 1870, 1871, + 1870, 1931, 1871, 1871, 1931, 1932, 1931, 1992, 1932, 1932, 1992, 1993, + 1992, 2053, 1993, 1993, 2053, 2054, 2053, 2114, 2054, 2054, 2114, 2115, + 2114, 2175, 2115, 2115, 2175, 2176, 2175, 2236, 2176, 2176, 2236, 2237, + 2236, 2297, 2237, 2237, 2297, 2298, 2297, 2358, 2298, 2298, 2358, 2359, + 2358, 2419, 2359, 2359, 2419, 2420, 2419, 2480, 2420, 2420, 2480, 2481, + 2480, 2541, 2481, 2481, 2541, 2542, 2541, 2602, 2542, 2542, 2602, 2603, + 2602, 2663, 2603, 2603, 2663, 2664, 2663, 2724, 2664, 2664, 2724, 2725, + 2724, 2785, 2725, 2725, 2785, 2786, 2785, 2846, 2786, 2786, 2846, 2847, + 2846, 2907, 2847, 2847, 2907, 2908, 2907, 2968, 2908, 2908, 2968, 2969, + 2968, 3029, 2969, 2969, 3029, 3030, 3029, 3090, 3030, 3030, 3090, 3091, + 3090, 3151, 3091, 3091, 3151, 3152, 3151, 3212, 3152, 3152, 3212, 3213, + 3212, 3273, 3213, 3213, 3273, 3274, 3273, 3334, 3274, 3274, 3334, 3335, + 3334, 3395, 3335, 3335, 3395, 3396, 3395, 3456, 3396, 3396, 3456, 3457, + 3456, 3517, 3457, 3457, 3517, 3518, 3517, 3578, 3518, 3518, 3578, 3579, + 3578, 3639, 3579, 3579, 3639, 3640, 3639, 3700, 3640, 3640, 3700, 3701, + 41, 102, 42, 42, 102, 103, 102, 163, 103, 103, 163, 164, + 163, 224, 164, 164, 224, 225, 224, 285, 225, 225, 285, 286, + 285, 346, 286, 286, 346, 347, 346, 407, 347, 347, 407, 408, + 407, 468, 408, 408, 468, 469, 468, 529, 469, 469, 529, 530, + 529, 590, 530, 530, 590, 591, 590, 651, 591, 591, 651, 652, + 651, 712, 652, 652, 712, 713, 712, 773, 713, 713, 773, 774, + 773, 834, 774, 774, 834, 835, 834, 895, 835, 835, 895, 896, + 895, 956, 896, 896, 956, 957, 956, 1017, 957, 957, 1017, 1018, + 1017, 1078, 1018, 1018, 1078, 1079, 1078, 1139, 1079, 1079, 1139, 1140, + 1139, 1200, 1140, 1140, 1200, 1201, 1200, 1261, 1201, 1201, 1261, 1262, + 1261, 1322, 1262, 1262, 1322, 1323, 1322, 1383, 1323, 1323, 1383, 1384, + 1383, 1444, 1384, 1384, 1444, 1445, 1444, 1505, 1445, 1445, 1505, 1506, + 1505, 1566, 1506, 1506, 1566, 1567, 1566, 1627, 1567, 1567, 1627, 1628, + 1627, 1688, 1628, 1628, 1688, 1689, 1688, 1749, 1689, 1689, 1749, 1750, + 1749, 1810, 1750, 1750, 1810, 1811, 1810, 1871, 1811, 1811, 1871, 1872, + 1871, 1932, 1872, 1872, 1932, 1933, 1932, 1993, 1933, 1933, 1993, 1994, + 1993, 2054, 1994, 1994, 2054, 2055, 2054, 2115, 2055, 2055, 2115, 2116, + 2115, 2176, 2116, 2116, 2176, 2177, 2176, 2237, 2177, 2177, 2237, 2238, + 2237, 2298, 2238, 2238, 2298, 2299, 2298, 2359, 2299, 2299, 2359, 2360, + 2359, 2420, 2360, 2360, 2420, 2421, 2420, 2481, 2421, 2421, 2481, 2482, + 2481, 2542, 2482, 2482, 2542, 2543, 2542, 2603, 2543, 2543, 2603, 2604, + 2603, 2664, 2604, 2604, 2664, 2665, 2664, 2725, 2665, 2665, 2725, 2726, + 2725, 2786, 2726, 2726, 2786, 2787, 2786, 2847, 2787, 2787, 2847, 2848, + 2847, 2908, 2848, 2848, 2908, 2909, 2908, 2969, 2909, 2909, 2969, 2970, + 2969, 3030, 2970, 2970, 3030, 3031, 3030, 3091, 3031, 3031, 3091, 3092, + 3091, 3152, 3092, 3092, 3152, 3153, 3152, 3213, 3153, 3153, 3213, 3214, + 3213, 3274, 3214, 3214, 3274, 3275, 3274, 3335, 3275, 3275, 3335, 3336, + 3335, 3396, 3336, 3336, 3396, 3397, 3396, 3457, 3397, 3397, 3457, 3458, + 3457, 3518, 3458, 3458, 3518, 3519, 3518, 3579, 3519, 3519, 3579, 3580, + 3579, 3640, 3580, 3580, 3640, 3641, 3640, 3701, 3641, 3641, 3701, 3702, + 42, 103, 43, 43, 103, 104, 103, 164, 104, 104, 164, 165, + 164, 225, 165, 165, 225, 226, 225, 286, 226, 226, 286, 287, + 286, 347, 287, 287, 347, 348, 347, 408, 348, 348, 408, 409, + 408, 469, 409, 409, 469, 470, 469, 530, 470, 470, 530, 531, + 530, 591, 531, 531, 591, 592, 591, 652, 592, 592, 652, 653, + 652, 713, 653, 653, 713, 714, 713, 774, 714, 714, 774, 775, + 774, 835, 775, 775, 835, 836, 835, 896, 836, 836, 896, 897, + 896, 957, 897, 897, 957, 958, 957, 1018, 958, 958, 1018, 1019, + 1018, 1079, 1019, 1019, 1079, 1080, 1079, 1140, 1080, 1080, 1140, 1141, + 1140, 1201, 1141, 1141, 1201, 1202, 1201, 1262, 1202, 1202, 1262, 1263, + 1262, 1323, 1263, 1263, 1323, 1324, 1323, 1384, 1324, 1324, 1384, 1385, + 1384, 1445, 1385, 1385, 1445, 1446, 1445, 1506, 1446, 1446, 1506, 1507, + 1506, 1567, 1507, 1507, 1567, 1568, 1567, 1628, 1568, 1568, 1628, 1629, + 1628, 1689, 1629, 1629, 1689, 1690, 1689, 1750, 1690, 1690, 1750, 1751, + 1750, 1811, 1751, 1751, 1811, 1812, 1811, 1872, 1812, 1812, 1872, 1873, + 1872, 1933, 1873, 1873, 1933, 1934, 1933, 1994, 1934, 1934, 1994, 1995, + 1994, 2055, 1995, 1995, 2055, 2056, 2055, 2116, 2056, 2056, 2116, 2117, + 2116, 2177, 2117, 2117, 2177, 2178, 2177, 2238, 2178, 2178, 2238, 2239, + 2238, 2299, 2239, 2239, 2299, 2300, 2299, 2360, 2300, 2300, 2360, 2361, + 2360, 2421, 2361, 2361, 2421, 2422, 2421, 2482, 2422, 2422, 2482, 2483, + 2482, 2543, 2483, 2483, 2543, 2544, 2543, 2604, 2544, 2544, 2604, 2605, + 2604, 2665, 2605, 2605, 2665, 2666, 2665, 2726, 2666, 2666, 2726, 2727, + 2726, 2787, 2727, 2727, 2787, 2788, 2787, 2848, 2788, 2788, 2848, 2849, + 2848, 2909, 2849, 2849, 2909, 2910, 2909, 2970, 2910, 2910, 2970, 2971, + 2970, 3031, 2971, 2971, 3031, 3032, 3031, 3092, 3032, 3032, 3092, 3093, + 3092, 3153, 3093, 3093, 3153, 3154, 3153, 3214, 3154, 3154, 3214, 3215, + 3214, 3275, 3215, 3215, 3275, 3276, 3275, 3336, 3276, 3276, 3336, 3337, + 3336, 3397, 3337, 3337, 3397, 3398, 3397, 3458, 3398, 3398, 3458, 3459, + 3458, 3519, 3459, 3459, 3519, 3520, 3519, 3580, 3520, 3520, 3580, 3581, + 3580, 3641, 3581, 3581, 3641, 3642, 3641, 3702, 3642, 3642, 3702, 3703, + 43, 104, 44, 44, 104, 105, 104, 165, 105, 105, 165, 166, + 165, 226, 166, 166, 226, 227, 226, 287, 227, 227, 287, 288, + 287, 348, 288, 288, 348, 349, 348, 409, 349, 349, 409, 410, + 409, 470, 410, 410, 470, 471, 470, 531, 471, 471, 531, 532, + 531, 592, 532, 532, 592, 593, 592, 653, 593, 593, 653, 654, + 653, 714, 654, 654, 714, 715, 714, 775, 715, 715, 775, 776, + 775, 836, 776, 776, 836, 837, 836, 897, 837, 837, 897, 898, + 897, 958, 898, 898, 958, 959, 958, 1019, 959, 959, 1019, 1020, + 1019, 1080, 1020, 1020, 1080, 1081, 1080, 1141, 1081, 1081, 1141, 1142, + 1141, 1202, 1142, 1142, 1202, 1203, 1202, 1263, 1203, 1203, 1263, 1264, + 1263, 1324, 1264, 1264, 1324, 1325, 1324, 1385, 1325, 1325, 1385, 1386, + 1385, 1446, 1386, 1386, 1446, 1447, 1446, 1507, 1447, 1447, 1507, 1508, + 1507, 1568, 1508, 1508, 1568, 1569, 1568, 1629, 1569, 1569, 1629, 1630, + 1629, 1690, 1630, 1630, 1690, 1691, 1690, 1751, 1691, 1691, 1751, 1752, + 1751, 1812, 1752, 1752, 1812, 1813, 1812, 1873, 1813, 1813, 1873, 1874, + 1873, 1934, 1874, 1874, 1934, 1935, 1934, 1995, 1935, 1935, 1995, 1996, + 1995, 2056, 1996, 1996, 2056, 2057, 2056, 2117, 2057, 2057, 2117, 2118, + 2117, 2178, 2118, 2118, 2178, 2179, 2178, 2239, 2179, 2179, 2239, 2240, + 2239, 2300, 2240, 2240, 2300, 2301, 2300, 2361, 2301, 2301, 2361, 2362, + 2361, 2422, 2362, 2362, 2422, 2423, 2422, 2483, 2423, 2423, 2483, 2484, + 2483, 2544, 2484, 2484, 2544, 2545, 2544, 2605, 2545, 2545, 2605, 2606, + 2605, 2666, 2606, 2606, 2666, 2667, 2666, 2727, 2667, 2667, 2727, 2728, + 2727, 2788, 2728, 2728, 2788, 2789, 2788, 2849, 2789, 2789, 2849, 2850, + 2849, 2910, 2850, 2850, 2910, 2911, 2910, 2971, 2911, 2911, 2971, 2972, + 2971, 3032, 2972, 2972, 3032, 3033, 3032, 3093, 3033, 3033, 3093, 3094, + 3093, 3154, 3094, 3094, 3154, 3155, 3154, 3215, 3155, 3155, 3215, 3216, + 3215, 3276, 3216, 3216, 3276, 3277, 3276, 3337, 3277, 3277, 3337, 3338, + 3337, 3398, 3338, 3338, 3398, 3399, 3398, 3459, 3399, 3399, 3459, 3460, + 3459, 3520, 3460, 3460, 3520, 3521, 3520, 3581, 3521, 3521, 3581, 3582, + 3581, 3642, 3582, 3582, 3642, 3643, 3642, 3703, 3643, 3643, 3703, 3704, + 44, 105, 45, 45, 105, 106, 105, 166, 106, 106, 166, 167, + 166, 227, 167, 167, 227, 228, 227, 288, 228, 228, 288, 289, + 288, 349, 289, 289, 349, 350, 349, 410, 350, 350, 410, 411, + 410, 471, 411, 411, 471, 472, 471, 532, 472, 472, 532, 533, + 532, 593, 533, 533, 593, 594, 593, 654, 594, 594, 654, 655, + 654, 715, 655, 655, 715, 716, 715, 776, 716, 716, 776, 777, + 776, 837, 777, 777, 837, 838, 837, 898, 838, 838, 898, 899, + 898, 959, 899, 899, 959, 960, 959, 1020, 960, 960, 1020, 1021, + 1020, 1081, 1021, 1021, 1081, 1082, 1081, 1142, 1082, 1082, 1142, 1143, + 1142, 1203, 1143, 1143, 1203, 1204, 1203, 1264, 1204, 1204, 1264, 1265, + 1264, 1325, 1265, 1265, 1325, 1326, 1325, 1386, 1326, 1326, 1386, 1387, + 1386, 1447, 1387, 1387, 1447, 1448, 1447, 1508, 1448, 1448, 1508, 1509, + 1508, 1569, 1509, 1509, 1569, 1570, 1569, 1630, 1570, 1570, 1630, 1631, + 1630, 1691, 1631, 1631, 1691, 1692, 1691, 1752, 1692, 1692, 1752, 1753, + 1752, 1813, 1753, 1753, 1813, 1814, 1813, 1874, 1814, 1814, 1874, 1875, + 1874, 1935, 1875, 1875, 1935, 1936, 1935, 1996, 1936, 1936, 1996, 1997, + 1996, 2057, 1997, 1997, 2057, 2058, 2057, 2118, 2058, 2058, 2118, 2119, + 2118, 2179, 2119, 2119, 2179, 2180, 2179, 2240, 2180, 2180, 2240, 2241, + 2240, 2301, 2241, 2241, 2301, 2302, 2301, 2362, 2302, 2302, 2362, 2363, + 2362, 2423, 2363, 2363, 2423, 2424, 2423, 2484, 2424, 2424, 2484, 2485, + 2484, 2545, 2485, 2485, 2545, 2546, 2545, 2606, 2546, 2546, 2606, 2607, + 2606, 2667, 2607, 2607, 2667, 2668, 2667, 2728, 2668, 2668, 2728, 2729, + 2728, 2789, 2729, 2729, 2789, 2790, 2789, 2850, 2790, 2790, 2850, 2851, + 2850, 2911, 2851, 2851, 2911, 2912, 2911, 2972, 2912, 2912, 2972, 2973, + 2972, 3033, 2973, 2973, 3033, 3034, 3033, 3094, 3034, 3034, 3094, 3095, + 3094, 3155, 3095, 3095, 3155, 3156, 3155, 3216, 3156, 3156, 3216, 3217, + 3216, 3277, 3217, 3217, 3277, 3278, 3277, 3338, 3278, 3278, 3338, 3339, + 3338, 3399, 3339, 3339, 3399, 3400, 3399, 3460, 3400, 3400, 3460, 3461, + 3460, 3521, 3461, 3461, 3521, 3522, 3521, 3582, 3522, 3522, 3582, 3583, + 3582, 3643, 3583, 3583, 3643, 3644, 3643, 3704, 3644, 3644, 3704, 3705, + 45, 106, 46, 46, 106, 107, 106, 167, 107, 107, 167, 168, + 167, 228, 168, 168, 228, 229, 228, 289, 229, 229, 289, 290, + 289, 350, 290, 290, 350, 351, 350, 411, 351, 351, 411, 412, + 411, 472, 412, 412, 472, 473, 472, 533, 473, 473, 533, 534, + 533, 594, 534, 534, 594, 595, 594, 655, 595, 595, 655, 656, + 655, 716, 656, 656, 716, 717, 716, 777, 717, 717, 777, 778, + 777, 838, 778, 778, 838, 839, 838, 899, 839, 839, 899, 900, + 899, 960, 900, 900, 960, 961, 960, 1021, 961, 961, 1021, 1022, + 1021, 1082, 1022, 1022, 1082, 1083, 1082, 1143, 1083, 1083, 1143, 1144, + 1143, 1204, 1144, 1144, 1204, 1205, 1204, 1265, 1205, 1205, 1265, 1266, + 1265, 1326, 1266, 1266, 1326, 1327, 1326, 1387, 1327, 1327, 1387, 1388, + 1387, 1448, 1388, 1388, 1448, 1449, 1448, 1509, 1449, 1449, 1509, 1510, + 1509, 1570, 1510, 1510, 1570, 1571, 1570, 1631, 1571, 1571, 1631, 1632, + 1631, 1692, 1632, 1632, 1692, 1693, 1692, 1753, 1693, 1693, 1753, 1754, + 1753, 1814, 1754, 1754, 1814, 1815, 1814, 1875, 1815, 1815, 1875, 1876, + 1875, 1936, 1876, 1876, 1936, 1937, 1936, 1997, 1937, 1937, 1997, 1998, + 1997, 2058, 1998, 1998, 2058, 2059, 2058, 2119, 2059, 2059, 2119, 2120, + 2119, 2180, 2120, 2120, 2180, 2181, 2180, 2241, 2181, 2181, 2241, 2242, + 2241, 2302, 2242, 2242, 2302, 2303, 2302, 2363, 2303, 2303, 2363, 2364, + 2363, 2424, 2364, 2364, 2424, 2425, 2424, 2485, 2425, 2425, 2485, 2486, + 2485, 2546, 2486, 2486, 2546, 2547, 2546, 2607, 2547, 2547, 2607, 2608, + 2607, 2668, 2608, 2608, 2668, 2669, 2668, 2729, 2669, 2669, 2729, 2730, + 2729, 2790, 2730, 2730, 2790, 2791, 2790, 2851, 2791, 2791, 2851, 2852, + 2851, 2912, 2852, 2852, 2912, 2913, 2912, 2973, 2913, 2913, 2973, 2974, + 2973, 3034, 2974, 2974, 3034, 3035, 3034, 3095, 3035, 3035, 3095, 3096, + 3095, 3156, 3096, 3096, 3156, 3157, 3156, 3217, 3157, 3157, 3217, 3218, + 3217, 3278, 3218, 3218, 3278, 3279, 3278, 3339, 3279, 3279, 3339, 3340, + 3339, 3400, 3340, 3340, 3400, 3401, 3400, 3461, 3401, 3401, 3461, 3462, + 3461, 3522, 3462, 3462, 3522, 3523, 3522, 3583, 3523, 3523, 3583, 3584, + 3583, 3644, 3584, 3584, 3644, 3645, 3644, 3705, 3645, 3645, 3705, 3706, + 46, 107, 47, 47, 107, 108, 107, 168, 108, 108, 168, 169, + 168, 229, 169, 169, 229, 230, 229, 290, 230, 230, 290, 291, + 290, 351, 291, 291, 351, 352, 351, 412, 352, 352, 412, 413, + 412, 473, 413, 413, 473, 474, 473, 534, 474, 474, 534, 535, + 534, 595, 535, 535, 595, 596, 595, 656, 596, 596, 656, 657, + 656, 717, 657, 657, 717, 718, 717, 778, 718, 718, 778, 779, + 778, 839, 779, 779, 839, 840, 839, 900, 840, 840, 900, 901, + 900, 961, 901, 901, 961, 962, 961, 1022, 962, 962, 1022, 1023, + 1022, 1083, 1023, 1023, 1083, 1084, 1083, 1144, 1084, 1084, 1144, 1145, + 1144, 1205, 1145, 1145, 1205, 1206, 1205, 1266, 1206, 1206, 1266, 1267, + 1266, 1327, 1267, 1267, 1327, 1328, 1327, 1388, 1328, 1328, 1388, 1389, + 1388, 1449, 1389, 1389, 1449, 1450, 1449, 1510, 1450, 1450, 1510, 1511, + 1510, 1571, 1511, 1511, 1571, 1572, 1571, 1632, 1572, 1572, 1632, 1633, + 1632, 1693, 1633, 1633, 1693, 1694, 1693, 1754, 1694, 1694, 1754, 1755, + 1754, 1815, 1755, 1755, 1815, 1816, 1815, 1876, 1816, 1816, 1876, 1877, + 1876, 1937, 1877, 1877, 1937, 1938, 1937, 1998, 1938, 1938, 1998, 1999, + 1998, 2059, 1999, 1999, 2059, 2060, 2059, 2120, 2060, 2060, 2120, 2121, + 2120, 2181, 2121, 2121, 2181, 2182, 2181, 2242, 2182, 2182, 2242, 2243, + 2242, 2303, 2243, 2243, 2303, 2304, 2303, 2364, 2304, 2304, 2364, 2365, + 2364, 2425, 2365, 2365, 2425, 2426, 2425, 2486, 2426, 2426, 2486, 2487, + 2486, 2547, 2487, 2487, 2547, 2548, 2547, 2608, 2548, 2548, 2608, 2609, + 2608, 2669, 2609, 2609, 2669, 2670, 2669, 2730, 2670, 2670, 2730, 2731, + 2730, 2791, 2731, 2731, 2791, 2792, 2791, 2852, 2792, 2792, 2852, 2853, + 2852, 2913, 2853, 2853, 2913, 2914, 2913, 2974, 2914, 2914, 2974, 2975, + 2974, 3035, 2975, 2975, 3035, 3036, 3035, 3096, 3036, 3036, 3096, 3097, + 3096, 3157, 3097, 3097, 3157, 3158, 3157, 3218, 3158, 3158, 3218, 3219, + 3218, 3279, 3219, 3219, 3279, 3280, 3279, 3340, 3280, 3280, 3340, 3341, + 3340, 3401, 3341, 3341, 3401, 3402, 3401, 3462, 3402, 3402, 3462, 3463, + 3462, 3523, 3463, 3463, 3523, 3524, 3523, 3584, 3524, 3524, 3584, 3585, + 3584, 3645, 3585, 3585, 3645, 3646, 3645, 3706, 3646, 3646, 3706, 3707, + 47, 108, 48, 48, 108, 109, 108, 169, 109, 109, 169, 170, + 169, 230, 170, 170, 230, 231, 230, 291, 231, 231, 291, 292, + 291, 352, 292, 292, 352, 353, 352, 413, 353, 353, 413, 414, + 413, 474, 414, 414, 474, 475, 474, 535, 475, 475, 535, 536, + 535, 596, 536, 536, 596, 597, 596, 657, 597, 597, 657, 658, + 657, 718, 658, 658, 718, 719, 718, 779, 719, 719, 779, 780, + 779, 840, 780, 780, 840, 841, 840, 901, 841, 841, 901, 902, + 901, 962, 902, 902, 962, 963, 962, 1023, 963, 963, 1023, 1024, + 1023, 1084, 1024, 1024, 1084, 1085, 1084, 1145, 1085, 1085, 1145, 1146, + 1145, 1206, 1146, 1146, 1206, 1207, 1206, 1267, 1207, 1207, 1267, 1268, + 1267, 1328, 1268, 1268, 1328, 1329, 1328, 1389, 1329, 1329, 1389, 1390, + 1389, 1450, 1390, 1390, 1450, 1451, 1450, 1511, 1451, 1451, 1511, 1512, + 1511, 1572, 1512, 1512, 1572, 1573, 1572, 1633, 1573, 1573, 1633, 1634, + 1633, 1694, 1634, 1634, 1694, 1695, 1694, 1755, 1695, 1695, 1755, 1756, + 1755, 1816, 1756, 1756, 1816, 1817, 1816, 1877, 1817, 1817, 1877, 1878, + 1877, 1938, 1878, 1878, 1938, 1939, 1938, 1999, 1939, 1939, 1999, 2000, + 1999, 2060, 2000, 2000, 2060, 2061, 2060, 2121, 2061, 2061, 2121, 2122, + 2121, 2182, 2122, 2122, 2182, 2183, 2182, 2243, 2183, 2183, 2243, 2244, + 2243, 2304, 2244, 2244, 2304, 2305, 2304, 2365, 2305, 2305, 2365, 2366, + 2365, 2426, 2366, 2366, 2426, 2427, 2426, 2487, 2427, 2427, 2487, 2488, + 2487, 2548, 2488, 2488, 2548, 2549, 2548, 2609, 2549, 2549, 2609, 2610, + 2609, 2670, 2610, 2610, 2670, 2671, 2670, 2731, 2671, 2671, 2731, 2732, + 2731, 2792, 2732, 2732, 2792, 2793, 2792, 2853, 2793, 2793, 2853, 2854, + 2853, 2914, 2854, 2854, 2914, 2915, 2914, 2975, 2915, 2915, 2975, 2976, + 2975, 3036, 2976, 2976, 3036, 3037, 3036, 3097, 3037, 3037, 3097, 3098, + 3097, 3158, 3098, 3098, 3158, 3159, 3158, 3219, 3159, 3159, 3219, 3220, + 3219, 3280, 3220, 3220, 3280, 3281, 3280, 3341, 3281, 3281, 3341, 3342, + 3341, 3402, 3342, 3342, 3402, 3403, 3402, 3463, 3403, 3403, 3463, 3464, + 3463, 3524, 3464, 3464, 3524, 3525, 3524, 3585, 3525, 3525, 3585, 3586, + 3585, 3646, 3586, 3586, 3646, 3647, 3646, 3707, 3647, 3647, 3707, 3708, + 48, 109, 49, 49, 109, 110, 109, 170, 110, 110, 170, 171, + 170, 231, 171, 171, 231, 232, 231, 292, 232, 232, 292, 293, + 292, 353, 293, 293, 353, 354, 353, 414, 354, 354, 414, 415, + 414, 475, 415, 415, 475, 476, 475, 536, 476, 476, 536, 537, + 536, 597, 537, 537, 597, 598, 597, 658, 598, 598, 658, 659, + 658, 719, 659, 659, 719, 720, 719, 780, 720, 720, 780, 781, + 780, 841, 781, 781, 841, 842, 841, 902, 842, 842, 902, 903, + 902, 963, 903, 903, 963, 964, 963, 1024, 964, 964, 1024, 1025, + 1024, 1085, 1025, 1025, 1085, 1086, 1085, 1146, 1086, 1086, 1146, 1147, + 1146, 1207, 1147, 1147, 1207, 1208, 1207, 1268, 1208, 1208, 1268, 1269, + 1268, 1329, 1269, 1269, 1329, 1330, 1329, 1390, 1330, 1330, 1390, 1391, + 1390, 1451, 1391, 1391, 1451, 1452, 1451, 1512, 1452, 1452, 1512, 1513, + 1512, 1573, 1513, 1513, 1573, 1574, 1573, 1634, 1574, 1574, 1634, 1635, + 1634, 1695, 1635, 1635, 1695, 1696, 1695, 1756, 1696, 1696, 1756, 1757, + 1756, 1817, 1757, 1757, 1817, 1818, 1817, 1878, 1818, 1818, 1878, 1879, + 1878, 1939, 1879, 1879, 1939, 1940, 1939, 2000, 1940, 1940, 2000, 2001, + 2000, 2061, 2001, 2001, 2061, 2062, 2061, 2122, 2062, 2062, 2122, 2123, + 2122, 2183, 2123, 2123, 2183, 2184, 2183, 2244, 2184, 2184, 2244, 2245, + 2244, 2305, 2245, 2245, 2305, 2306, 2305, 2366, 2306, 2306, 2366, 2367, + 2366, 2427, 2367, 2367, 2427, 2428, 2427, 2488, 2428, 2428, 2488, 2489, + 2488, 2549, 2489, 2489, 2549, 2550, 2549, 2610, 2550, 2550, 2610, 2611, + 2610, 2671, 2611, 2611, 2671, 2672, 2671, 2732, 2672, 2672, 2732, 2733, + 2732, 2793, 2733, 2733, 2793, 2794, 2793, 2854, 2794, 2794, 2854, 2855, + 2854, 2915, 2855, 2855, 2915, 2916, 2915, 2976, 2916, 2916, 2976, 2977, + 2976, 3037, 2977, 2977, 3037, 3038, 3037, 3098, 3038, 3038, 3098, 3099, + 3098, 3159, 3099, 3099, 3159, 3160, 3159, 3220, 3160, 3160, 3220, 3221, + 3220, 3281, 3221, 3221, 3281, 3282, 3281, 3342, 3282, 3282, 3342, 3343, + 3342, 3403, 3343, 3343, 3403, 3404, 3403, 3464, 3404, 3404, 3464, 3465, + 3464, 3525, 3465, 3465, 3525, 3526, 3525, 3586, 3526, 3526, 3586, 3587, + 3586, 3647, 3587, 3587, 3647, 3648, 3647, 3708, 3648, 3648, 3708, 3709, + 49, 110, 50, 50, 110, 111, 110, 171, 111, 111, 171, 172, + 171, 232, 172, 172, 232, 233, 232, 293, 233, 233, 293, 294, + 293, 354, 294, 294, 354, 355, 354, 415, 355, 355, 415, 416, + 415, 476, 416, 416, 476, 477, 476, 537, 477, 477, 537, 538, + 537, 598, 538, 538, 598, 599, 598, 659, 599, 599, 659, 660, + 659, 720, 660, 660, 720, 721, 720, 781, 721, 721, 781, 782, + 781, 842, 782, 782, 842, 843, 842, 903, 843, 843, 903, 904, + 903, 964, 904, 904, 964, 965, 964, 1025, 965, 965, 1025, 1026, + 1025, 1086, 1026, 1026, 1086, 1087, 1086, 1147, 1087, 1087, 1147, 1148, + 1147, 1208, 1148, 1148, 1208, 1209, 1208, 1269, 1209, 1209, 1269, 1270, + 1269, 1330, 1270, 1270, 1330, 1331, 1330, 1391, 1331, 1331, 1391, 1392, + 1391, 1452, 1392, 1392, 1452, 1453, 1452, 1513, 1453, 1453, 1513, 1514, + 1513, 1574, 1514, 1514, 1574, 1575, 1574, 1635, 1575, 1575, 1635, 1636, + 1635, 1696, 1636, 1636, 1696, 1697, 1696, 1757, 1697, 1697, 1757, 1758, + 1757, 1818, 1758, 1758, 1818, 1819, 1818, 1879, 1819, 1819, 1879, 1880, + 1879, 1940, 1880, 1880, 1940, 1941, 1940, 2001, 1941, 1941, 2001, 2002, + 2001, 2062, 2002, 2002, 2062, 2063, 2062, 2123, 2063, 2063, 2123, 2124, + 2123, 2184, 2124, 2124, 2184, 2185, 2184, 2245, 2185, 2185, 2245, 2246, + 2245, 2306, 2246, 2246, 2306, 2307, 2306, 2367, 2307, 2307, 2367, 2368, + 2367, 2428, 2368, 2368, 2428, 2429, 2428, 2489, 2429, 2429, 2489, 2490, + 2489, 2550, 2490, 2490, 2550, 2551, 2550, 2611, 2551, 2551, 2611, 2612, + 2611, 2672, 2612, 2612, 2672, 2673, 2672, 2733, 2673, 2673, 2733, 2734, + 2733, 2794, 2734, 2734, 2794, 2795, 2794, 2855, 2795, 2795, 2855, 2856, + 2855, 2916, 2856, 2856, 2916, 2917, 2916, 2977, 2917, 2917, 2977, 2978, + 2977, 3038, 2978, 2978, 3038, 3039, 3038, 3099, 3039, 3039, 3099, 3100, + 3099, 3160, 3100, 3100, 3160, 3161, 3160, 3221, 3161, 3161, 3221, 3222, + 3221, 3282, 3222, 3222, 3282, 3283, 3282, 3343, 3283, 3283, 3343, 3344, + 3343, 3404, 3344, 3344, 3404, 3405, 3404, 3465, 3405, 3405, 3465, 3466, + 3465, 3526, 3466, 3466, 3526, 3527, 3526, 3587, 3527, 3527, 3587, 3588, + 3587, 3648, 3588, 3588, 3648, 3649, 3648, 3709, 3649, 3649, 3709, 3710, + 50, 111, 51, 51, 111, 112, 111, 172, 112, 112, 172, 173, + 172, 233, 173, 173, 233, 234, 233, 294, 234, 234, 294, 295, + 294, 355, 295, 295, 355, 356, 355, 416, 356, 356, 416, 417, + 416, 477, 417, 417, 477, 478, 477, 538, 478, 478, 538, 539, + 538, 599, 539, 539, 599, 600, 599, 660, 600, 600, 660, 661, + 660, 721, 661, 661, 721, 722, 721, 782, 722, 722, 782, 783, + 782, 843, 783, 783, 843, 844, 843, 904, 844, 844, 904, 905, + 904, 965, 905, 905, 965, 966, 965, 1026, 966, 966, 1026, 1027, + 1026, 1087, 1027, 1027, 1087, 1088, 1087, 1148, 1088, 1088, 1148, 1149, + 1148, 1209, 1149, 1149, 1209, 1210, 1209, 1270, 1210, 1210, 1270, 1271, + 1270, 1331, 1271, 1271, 1331, 1332, 1331, 1392, 1332, 1332, 1392, 1393, + 1392, 1453, 1393, 1393, 1453, 1454, 1453, 1514, 1454, 1454, 1514, 1515, + 1514, 1575, 1515, 1515, 1575, 1576, 1575, 1636, 1576, 1576, 1636, 1637, + 1636, 1697, 1637, 1637, 1697, 1698, 1697, 1758, 1698, 1698, 1758, 1759, + 1758, 1819, 1759, 1759, 1819, 1820, 1819, 1880, 1820, 1820, 1880, 1881, + 1880, 1941, 1881, 1881, 1941, 1942, 1941, 2002, 1942, 1942, 2002, 2003, + 2002, 2063, 2003, 2003, 2063, 2064, 2063, 2124, 2064, 2064, 2124, 2125, + 2124, 2185, 2125, 2125, 2185, 2186, 2185, 2246, 2186, 2186, 2246, 2247, + 2246, 2307, 2247, 2247, 2307, 2308, 2307, 2368, 2308, 2308, 2368, 2369, + 2368, 2429, 2369, 2369, 2429, 2430, 2429, 2490, 2430, 2430, 2490, 2491, + 2490, 2551, 2491, 2491, 2551, 2552, 2551, 2612, 2552, 2552, 2612, 2613, + 2612, 2673, 2613, 2613, 2673, 2674, 2673, 2734, 2674, 2674, 2734, 2735, + 2734, 2795, 2735, 2735, 2795, 2796, 2795, 2856, 2796, 2796, 2856, 2857, + 2856, 2917, 2857, 2857, 2917, 2918, 2917, 2978, 2918, 2918, 2978, 2979, + 2978, 3039, 2979, 2979, 3039, 3040, 3039, 3100, 3040, 3040, 3100, 3101, + 3100, 3161, 3101, 3101, 3161, 3162, 3161, 3222, 3162, 3162, 3222, 3223, + 3222, 3283, 3223, 3223, 3283, 3284, 3283, 3344, 3284, 3284, 3344, 3345, + 3344, 3405, 3345, 3345, 3405, 3406, 3405, 3466, 3406, 3406, 3466, 3467, + 3466, 3527, 3467, 3467, 3527, 3528, 3527, 3588, 3528, 3528, 3588, 3589, + 3588, 3649, 3589, 3589, 3649, 3650, 3649, 3710, 3650, 3650, 3710, 3711, + 51, 112, 52, 52, 112, 113, 112, 173, 113, 113, 173, 174, + 173, 234, 174, 174, 234, 235, 234, 295, 235, 235, 295, 296, + 295, 356, 296, 296, 356, 357, 356, 417, 357, 357, 417, 418, + 417, 478, 418, 418, 478, 479, 478, 539, 479, 479, 539, 540, + 539, 600, 540, 540, 600, 601, 600, 661, 601, 601, 661, 662, + 661, 722, 662, 662, 722, 723, 722, 783, 723, 723, 783, 784, + 783, 844, 784, 784, 844, 845, 844, 905, 845, 845, 905, 906, + 905, 966, 906, 906, 966, 967, 966, 1027, 967, 967, 1027, 1028, + 1027, 1088, 1028, 1028, 1088, 1089, 1088, 1149, 1089, 1089, 1149, 1150, + 1149, 1210, 1150, 1150, 1210, 1211, 1210, 1271, 1211, 1211, 1271, 1272, + 1271, 1332, 1272, 1272, 1332, 1333, 1332, 1393, 1333, 1333, 1393, 1394, + 1393, 1454, 1394, 1394, 1454, 1455, 1454, 1515, 1455, 1455, 1515, 1516, + 1515, 1576, 1516, 1516, 1576, 1577, 1576, 1637, 1577, 1577, 1637, 1638, + 1637, 1698, 1638, 1638, 1698, 1699, 1698, 1759, 1699, 1699, 1759, 1760, + 1759, 1820, 1760, 1760, 1820, 1821, 1820, 1881, 1821, 1821, 1881, 1882, + 1881, 1942, 1882, 1882, 1942, 1943, 1942, 2003, 1943, 1943, 2003, 2004, + 2003, 2064, 2004, 2004, 2064, 2065, 2064, 2125, 2065, 2065, 2125, 2126, + 2125, 2186, 2126, 2126, 2186, 2187, 2186, 2247, 2187, 2187, 2247, 2248, + 2247, 2308, 2248, 2248, 2308, 2309, 2308, 2369, 2309, 2309, 2369, 2370, + 2369, 2430, 2370, 2370, 2430, 2431, 2430, 2491, 2431, 2431, 2491, 2492, + 2491, 2552, 2492, 2492, 2552, 2553, 2552, 2613, 2553, 2553, 2613, 2614, + 2613, 2674, 2614, 2614, 2674, 2675, 2674, 2735, 2675, 2675, 2735, 2736, + 2735, 2796, 2736, 2736, 2796, 2797, 2796, 2857, 2797, 2797, 2857, 2858, + 2857, 2918, 2858, 2858, 2918, 2919, 2918, 2979, 2919, 2919, 2979, 2980, + 2979, 3040, 2980, 2980, 3040, 3041, 3040, 3101, 3041, 3041, 3101, 3102, + 3101, 3162, 3102, 3102, 3162, 3163, 3162, 3223, 3163, 3163, 3223, 3224, + 3223, 3284, 3224, 3224, 3284, 3285, 3284, 3345, 3285, 3285, 3345, 3346, + 3345, 3406, 3346, 3346, 3406, 3407, 3406, 3467, 3407, 3407, 3467, 3468, + 3467, 3528, 3468, 3468, 3528, 3529, 3528, 3589, 3529, 3529, 3589, 3590, + 3589, 3650, 3590, 3590, 3650, 3651, 3650, 3711, 3651, 3651, 3711, 3712, + 52, 113, 53, 53, 113, 114, 113, 174, 114, 114, 174, 175, + 174, 235, 175, 175, 235, 236, 235, 296, 236, 236, 296, 297, + 296, 357, 297, 297, 357, 358, 357, 418, 358, 358, 418, 419, + 418, 479, 419, 419, 479, 480, 479, 540, 480, 480, 540, 541, + 540, 601, 541, 541, 601, 602, 601, 662, 602, 602, 662, 663, + 662, 723, 663, 663, 723, 724, 723, 784, 724, 724, 784, 785, + 784, 845, 785, 785, 845, 846, 845, 906, 846, 846, 906, 907, + 906, 967, 907, 907, 967, 968, 967, 1028, 968, 968, 1028, 1029, + 1028, 1089, 1029, 1029, 1089, 1090, 1089, 1150, 1090, 1090, 1150, 1151, + 1150, 1211, 1151, 1151, 1211, 1212, 1211, 1272, 1212, 1212, 1272, 1273, + 1272, 1333, 1273, 1273, 1333, 1334, 1333, 1394, 1334, 1334, 1394, 1395, + 1394, 1455, 1395, 1395, 1455, 1456, 1455, 1516, 1456, 1456, 1516, 1517, + 1516, 1577, 1517, 1517, 1577, 1578, 1577, 1638, 1578, 1578, 1638, 1639, + 1638, 1699, 1639, 1639, 1699, 1700, 1699, 1760, 1700, 1700, 1760, 1761, + 1760, 1821, 1761, 1761, 1821, 1822, 1821, 1882, 1822, 1822, 1882, 1883, + 1882, 1943, 1883, 1883, 1943, 1944, 1943, 2004, 1944, 1944, 2004, 2005, + 2004, 2065, 2005, 2005, 2065, 2066, 2065, 2126, 2066, 2066, 2126, 2127, + 2126, 2187, 2127, 2127, 2187, 2188, 2187, 2248, 2188, 2188, 2248, 2249, + 2248, 2309, 2249, 2249, 2309, 2310, 2309, 2370, 2310, 2310, 2370, 2371, + 2370, 2431, 2371, 2371, 2431, 2432, 2431, 2492, 2432, 2432, 2492, 2493, + 2492, 2553, 2493, 2493, 2553, 2554, 2553, 2614, 2554, 2554, 2614, 2615, + 2614, 2675, 2615, 2615, 2675, 2676, 2675, 2736, 2676, 2676, 2736, 2737, + 2736, 2797, 2737, 2737, 2797, 2798, 2797, 2858, 2798, 2798, 2858, 2859, + 2858, 2919, 2859, 2859, 2919, 2920, 2919, 2980, 2920, 2920, 2980, 2981, + 2980, 3041, 2981, 2981, 3041, 3042, 3041, 3102, 3042, 3042, 3102, 3103, + 3102, 3163, 3103, 3103, 3163, 3164, 3163, 3224, 3164, 3164, 3224, 3225, + 3224, 3285, 3225, 3225, 3285, 3286, 3285, 3346, 3286, 3286, 3346, 3347, + 3346, 3407, 3347, 3347, 3407, 3408, 3407, 3468, 3408, 3408, 3468, 3469, + 3468, 3529, 3469, 3469, 3529, 3530, 3529, 3590, 3530, 3530, 3590, 3591, + 3590, 3651, 3591, 3591, 3651, 3652, 3651, 3712, 3652, 3652, 3712, 3713, + 53, 114, 54, 54, 114, 115, 114, 175, 115, 115, 175, 176, + 175, 236, 176, 176, 236, 237, 236, 297, 237, 237, 297, 298, + 297, 358, 298, 298, 358, 359, 358, 419, 359, 359, 419, 420, + 419, 480, 420, 420, 480, 481, 480, 541, 481, 481, 541, 542, + 541, 602, 542, 542, 602, 603, 602, 663, 603, 603, 663, 664, + 663, 724, 664, 664, 724, 725, 724, 785, 725, 725, 785, 786, + 785, 846, 786, 786, 846, 847, 846, 907, 847, 847, 907, 908, + 907, 968, 908, 908, 968, 969, 968, 1029, 969, 969, 1029, 1030, + 1029, 1090, 1030, 1030, 1090, 1091, 1090, 1151, 1091, 1091, 1151, 1152, + 1151, 1212, 1152, 1152, 1212, 1213, 1212, 1273, 1213, 1213, 1273, 1274, + 1273, 1334, 1274, 1274, 1334, 1335, 1334, 1395, 1335, 1335, 1395, 1396, + 1395, 1456, 1396, 1396, 1456, 1457, 1456, 1517, 1457, 1457, 1517, 1518, + 1517, 1578, 1518, 1518, 1578, 1579, 1578, 1639, 1579, 1579, 1639, 1640, + 1639, 1700, 1640, 1640, 1700, 1701, 1700, 1761, 1701, 1701, 1761, 1762, + 1761, 1822, 1762, 1762, 1822, 1823, 1822, 1883, 1823, 1823, 1883, 1884, + 1883, 1944, 1884, 1884, 1944, 1945, 1944, 2005, 1945, 1945, 2005, 2006, + 2005, 2066, 2006, 2006, 2066, 2067, 2066, 2127, 2067, 2067, 2127, 2128, + 2127, 2188, 2128, 2128, 2188, 2189, 2188, 2249, 2189, 2189, 2249, 2250, + 2249, 2310, 2250, 2250, 2310, 2311, 2310, 2371, 2311, 2311, 2371, 2372, + 2371, 2432, 2372, 2372, 2432, 2433, 2432, 2493, 2433, 2433, 2493, 2494, + 2493, 2554, 2494, 2494, 2554, 2555, 2554, 2615, 2555, 2555, 2615, 2616, + 2615, 2676, 2616, 2616, 2676, 2677, 2676, 2737, 2677, 2677, 2737, 2738, + 2737, 2798, 2738, 2738, 2798, 2799, 2798, 2859, 2799, 2799, 2859, 2860, + 2859, 2920, 2860, 2860, 2920, 2921, 2920, 2981, 2921, 2921, 2981, 2982, + 2981, 3042, 2982, 2982, 3042, 3043, 3042, 3103, 3043, 3043, 3103, 3104, + 3103, 3164, 3104, 3104, 3164, 3165, 3164, 3225, 3165, 3165, 3225, 3226, + 3225, 3286, 3226, 3226, 3286, 3287, 3286, 3347, 3287, 3287, 3347, 3348, + 3347, 3408, 3348, 3348, 3408, 3409, 3408, 3469, 3409, 3409, 3469, 3470, + 3469, 3530, 3470, 3470, 3530, 3531, 3530, 3591, 3531, 3531, 3591, 3592, + 3591, 3652, 3592, 3592, 3652, 3653, 3652, 3713, 3653, 3653, 3713, 3714, + 54, 115, 55, 55, 115, 116, 115, 176, 116, 116, 176, 177, + 176, 237, 177, 177, 237, 238, 237, 298, 238, 238, 298, 299, + 298, 359, 299, 299, 359, 360, 359, 420, 360, 360, 420, 421, + 420, 481, 421, 421, 481, 482, 481, 542, 482, 482, 542, 543, + 542, 603, 543, 543, 603, 604, 603, 664, 604, 604, 664, 665, + 664, 725, 665, 665, 725, 726, 725, 786, 726, 726, 786, 787, + 786, 847, 787, 787, 847, 848, 847, 908, 848, 848, 908, 909, + 908, 969, 909, 909, 969, 970, 969, 1030, 970, 970, 1030, 1031, + 1030, 1091, 1031, 1031, 1091, 1092, 1091, 1152, 1092, 1092, 1152, 1153, + 1152, 1213, 1153, 1153, 1213, 1214, 1213, 1274, 1214, 1214, 1274, 1275, + 1274, 1335, 1275, 1275, 1335, 1336, 1335, 1396, 1336, 1336, 1396, 1397, + 1396, 1457, 1397, 1397, 1457, 1458, 1457, 1518, 1458, 1458, 1518, 1519, + 1518, 1579, 1519, 1519, 1579, 1580, 1579, 1640, 1580, 1580, 1640, 1641, + 1640, 1701, 1641, 1641, 1701, 1702, 1701, 1762, 1702, 1702, 1762, 1763, + 1762, 1823, 1763, 1763, 1823, 1824, 1823, 1884, 1824, 1824, 1884, 1885, + 1884, 1945, 1885, 1885, 1945, 1946, 1945, 2006, 1946, 1946, 2006, 2007, + 2006, 2067, 2007, 2007, 2067, 2068, 2067, 2128, 2068, 2068, 2128, 2129, + 2128, 2189, 2129, 2129, 2189, 2190, 2189, 2250, 2190, 2190, 2250, 2251, + 2250, 2311, 2251, 2251, 2311, 2312, 2311, 2372, 2312, 2312, 2372, 2373, + 2372, 2433, 2373, 2373, 2433, 2434, 2433, 2494, 2434, 2434, 2494, 2495, + 2494, 2555, 2495, 2495, 2555, 2556, 2555, 2616, 2556, 2556, 2616, 2617, + 2616, 2677, 2617, 2617, 2677, 2678, 2677, 2738, 2678, 2678, 2738, 2739, + 2738, 2799, 2739, 2739, 2799, 2800, 2799, 2860, 2800, 2800, 2860, 2861, + 2860, 2921, 2861, 2861, 2921, 2922, 2921, 2982, 2922, 2922, 2982, 2983, + 2982, 3043, 2983, 2983, 3043, 3044, 3043, 3104, 3044, 3044, 3104, 3105, + 3104, 3165, 3105, 3105, 3165, 3166, 3165, 3226, 3166, 3166, 3226, 3227, + 3226, 3287, 3227, 3227, 3287, 3288, 3287, 3348, 3288, 3288, 3348, 3349, + 3348, 3409, 3349, 3349, 3409, 3410, 3409, 3470, 3410, 3410, 3470, 3471, + 3470, 3531, 3471, 3471, 3531, 3532, 3531, 3592, 3532, 3532, 3592, 3593, + 3592, 3653, 3593, 3593, 3653, 3654, 3653, 3714, 3654, 3654, 3714, 3715, + 55, 116, 56, 56, 116, 117, 116, 177, 117, 117, 177, 178, + 177, 238, 178, 178, 238, 239, 238, 299, 239, 239, 299, 300, + 299, 360, 300, 300, 360, 361, 360, 421, 361, 361, 421, 422, + 421, 482, 422, 422, 482, 483, 482, 543, 483, 483, 543, 544, + 543, 604, 544, 544, 604, 605, 604, 665, 605, 605, 665, 666, + 665, 726, 666, 666, 726, 727, 726, 787, 727, 727, 787, 788, + 787, 848, 788, 788, 848, 849, 848, 909, 849, 849, 909, 910, + 909, 970, 910, 910, 970, 971, 970, 1031, 971, 971, 1031, 1032, + 1031, 1092, 1032, 1032, 1092, 1093, 1092, 1153, 1093, 1093, 1153, 1154, + 1153, 1214, 1154, 1154, 1214, 1215, 1214, 1275, 1215, 1215, 1275, 1276, + 1275, 1336, 1276, 1276, 1336, 1337, 1336, 1397, 1337, 1337, 1397, 1398, + 1397, 1458, 1398, 1398, 1458, 1459, 1458, 1519, 1459, 1459, 1519, 1520, + 1519, 1580, 1520, 1520, 1580, 1581, 1580, 1641, 1581, 1581, 1641, 1642, + 1641, 1702, 1642, 1642, 1702, 1703, 1702, 1763, 1703, 1703, 1763, 1764, + 1763, 1824, 1764, 1764, 1824, 1825, 1824, 1885, 1825, 1825, 1885, 1886, + 1885, 1946, 1886, 1886, 1946, 1947, 1946, 2007, 1947, 1947, 2007, 2008, + 2007, 2068, 2008, 2008, 2068, 2069, 2068, 2129, 2069, 2069, 2129, 2130, + 2129, 2190, 2130, 2130, 2190, 2191, 2190, 2251, 2191, 2191, 2251, 2252, + 2251, 2312, 2252, 2252, 2312, 2313, 2312, 2373, 2313, 2313, 2373, 2374, + 2373, 2434, 2374, 2374, 2434, 2435, 2434, 2495, 2435, 2435, 2495, 2496, + 2495, 2556, 2496, 2496, 2556, 2557, 2556, 2617, 2557, 2557, 2617, 2618, + 2617, 2678, 2618, 2618, 2678, 2679, 2678, 2739, 2679, 2679, 2739, 2740, + 2739, 2800, 2740, 2740, 2800, 2801, 2800, 2861, 2801, 2801, 2861, 2862, + 2861, 2922, 2862, 2862, 2922, 2923, 2922, 2983, 2923, 2923, 2983, 2984, + 2983, 3044, 2984, 2984, 3044, 3045, 3044, 3105, 3045, 3045, 3105, 3106, + 3105, 3166, 3106, 3106, 3166, 3167, 3166, 3227, 3167, 3167, 3227, 3228, + 3227, 3288, 3228, 3228, 3288, 3289, 3288, 3349, 3289, 3289, 3349, 3350, + 3349, 3410, 3350, 3350, 3410, 3411, 3410, 3471, 3411, 3411, 3471, 3472, + 3471, 3532, 3472, 3472, 3532, 3533, 3532, 3593, 3533, 3533, 3593, 3594, + 3593, 3654, 3594, 3594, 3654, 3655, 3654, 3715, 3655, 3655, 3715, 3716, + 56, 117, 57, 57, 117, 118, 117, 178, 118, 118, 178, 179, + 178, 239, 179, 179, 239, 240, 239, 300, 240, 240, 300, 301, + 300, 361, 301, 301, 361, 362, 361, 422, 362, 362, 422, 423, + 422, 483, 423, 423, 483, 484, 483, 544, 484, 484, 544, 545, + 544, 605, 545, 545, 605, 606, 605, 666, 606, 606, 666, 667, + 666, 727, 667, 667, 727, 728, 727, 788, 728, 728, 788, 789, + 788, 849, 789, 789, 849, 850, 849, 910, 850, 850, 910, 911, + 910, 971, 911, 911, 971, 972, 971, 1032, 972, 972, 1032, 1033, + 1032, 1093, 1033, 1033, 1093, 1094, 1093, 1154, 1094, 1094, 1154, 1155, + 1154, 1215, 1155, 1155, 1215, 1216, 1215, 1276, 1216, 1216, 1276, 1277, + 1276, 1337, 1277, 1277, 1337, 1338, 1337, 1398, 1338, 1338, 1398, 1399, + 1398, 1459, 1399, 1399, 1459, 1460, 1459, 1520, 1460, 1460, 1520, 1521, + 1520, 1581, 1521, 1521, 1581, 1582, 1581, 1642, 1582, 1582, 1642, 1643, + 1642, 1703, 1643, 1643, 1703, 1704, 1703, 1764, 1704, 1704, 1764, 1765, + 1764, 1825, 1765, 1765, 1825, 1826, 1825, 1886, 1826, 1826, 1886, 1887, + 1886, 1947, 1887, 1887, 1947, 1948, 1947, 2008, 1948, 1948, 2008, 2009, + 2008, 2069, 2009, 2009, 2069, 2070, 2069, 2130, 2070, 2070, 2130, 2131, + 2130, 2191, 2131, 2131, 2191, 2192, 2191, 2252, 2192, 2192, 2252, 2253, + 2252, 2313, 2253, 2253, 2313, 2314, 2313, 2374, 2314, 2314, 2374, 2375, + 2374, 2435, 2375, 2375, 2435, 2436, 2435, 2496, 2436, 2436, 2496, 2497, + 2496, 2557, 2497, 2497, 2557, 2558, 2557, 2618, 2558, 2558, 2618, 2619, + 2618, 2679, 2619, 2619, 2679, 2680, 2679, 2740, 2680, 2680, 2740, 2741, + 2740, 2801, 2741, 2741, 2801, 2802, 2801, 2862, 2802, 2802, 2862, 2863, + 2862, 2923, 2863, 2863, 2923, 2924, 2923, 2984, 2924, 2924, 2984, 2985, + 2984, 3045, 2985, 2985, 3045, 3046, 3045, 3106, 3046, 3046, 3106, 3107, + 3106, 3167, 3107, 3107, 3167, 3168, 3167, 3228, 3168, 3168, 3228, 3229, + 3228, 3289, 3229, 3229, 3289, 3290, 3289, 3350, 3290, 3290, 3350, 3351, + 3350, 3411, 3351, 3351, 3411, 3412, 3411, 3472, 3412, 3412, 3472, 3473, + 3472, 3533, 3473, 3473, 3533, 3534, 3533, 3594, 3534, 3534, 3594, 3595, + 3594, 3655, 3595, 3595, 3655, 3656, 3655, 3716, 3656, 3656, 3716, 3717, + 57, 118, 58, 58, 118, 119, 118, 179, 119, 119, 179, 180, + 179, 240, 180, 180, 240, 241, 240, 301, 241, 241, 301, 302, + 301, 362, 302, 302, 362, 363, 362, 423, 363, 363, 423, 424, + 423, 484, 424, 424, 484, 485, 484, 545, 485, 485, 545, 546, + 545, 606, 546, 546, 606, 607, 606, 667, 607, 607, 667, 668, + 667, 728, 668, 668, 728, 729, 728, 789, 729, 729, 789, 790, + 789, 850, 790, 790, 850, 851, 850, 911, 851, 851, 911, 912, + 911, 972, 912, 912, 972, 973, 972, 1033, 973, 973, 1033, 1034, + 1033, 1094, 1034, 1034, 1094, 1095, 1094, 1155, 1095, 1095, 1155, 1156, + 1155, 1216, 1156, 1156, 1216, 1217, 1216, 1277, 1217, 1217, 1277, 1278, + 1277, 1338, 1278, 1278, 1338, 1339, 1338, 1399, 1339, 1339, 1399, 1400, + 1399, 1460, 1400, 1400, 1460, 1461, 1460, 1521, 1461, 1461, 1521, 1522, + 1521, 1582, 1522, 1522, 1582, 1583, 1582, 1643, 1583, 1583, 1643, 1644, + 1643, 1704, 1644, 1644, 1704, 1705, 1704, 1765, 1705, 1705, 1765, 1766, + 1765, 1826, 1766, 1766, 1826, 1827, 1826, 1887, 1827, 1827, 1887, 1888, + 1887, 1948, 1888, 1888, 1948, 1949, 1948, 2009, 1949, 1949, 2009, 2010, + 2009, 2070, 2010, 2010, 2070, 2071, 2070, 2131, 2071, 2071, 2131, 2132, + 2131, 2192, 2132, 2132, 2192, 2193, 2192, 2253, 2193, 2193, 2253, 2254, + 2253, 2314, 2254, 2254, 2314, 2315, 2314, 2375, 2315, 2315, 2375, 2376, + 2375, 2436, 2376, 2376, 2436, 2437, 2436, 2497, 2437, 2437, 2497, 2498, + 2497, 2558, 2498, 2498, 2558, 2559, 2558, 2619, 2559, 2559, 2619, 2620, + 2619, 2680, 2620, 2620, 2680, 2681, 2680, 2741, 2681, 2681, 2741, 2742, + 2741, 2802, 2742, 2742, 2802, 2803, 2802, 2863, 2803, 2803, 2863, 2864, + 2863, 2924, 2864, 2864, 2924, 2925, 2924, 2985, 2925, 2925, 2985, 2986, + 2985, 3046, 2986, 2986, 3046, 3047, 3046, 3107, 3047, 3047, 3107, 3108, + 3107, 3168, 3108, 3108, 3168, 3169, 3168, 3229, 3169, 3169, 3229, 3230, + 3229, 3290, 3230, 3230, 3290, 3291, 3290, 3351, 3291, 3291, 3351, 3352, + 3351, 3412, 3352, 3352, 3412, 3413, 3412, 3473, 3413, 3413, 3473, 3474, + 3473, 3534, 3474, 3474, 3534, 3535, 3534, 3595, 3535, 3535, 3595, 3596, + 3595, 3656, 3596, 3596, 3656, 3657, 3656, 3717, 3657, 3657, 3717, 3718, + 58, 119, 59, 59, 119, 120, 119, 180, 120, 120, 180, 181, + 180, 241, 181, 181, 241, 242, 241, 302, 242, 242, 302, 303, + 302, 363, 303, 303, 363, 364, 363, 424, 364, 364, 424, 425, + 424, 485, 425, 425, 485, 486, 485, 546, 486, 486, 546, 547, + 546, 607, 547, 547, 607, 608, 607, 668, 608, 608, 668, 669, + 668, 729, 669, 669, 729, 730, 729, 790, 730, 730, 790, 791, + 790, 851, 791, 791, 851, 852, 851, 912, 852, 852, 912, 913, + 912, 973, 913, 913, 973, 974, 973, 1034, 974, 974, 1034, 1035, + 1034, 1095, 1035, 1035, 1095, 1096, 1095, 1156, 1096, 1096, 1156, 1157, + 1156, 1217, 1157, 1157, 1217, 1218, 1217, 1278, 1218, 1218, 1278, 1279, + 1278, 1339, 1279, 1279, 1339, 1340, 1339, 1400, 1340, 1340, 1400, 1401, + 1400, 1461, 1401, 1401, 1461, 1462, 1461, 1522, 1462, 1462, 1522, 1523, + 1522, 1583, 1523, 1523, 1583, 1584, 1583, 1644, 1584, 1584, 1644, 1645, + 1644, 1705, 1645, 1645, 1705, 1706, 1705, 1766, 1706, 1706, 1766, 1767, + 1766, 1827, 1767, 1767, 1827, 1828, 1827, 1888, 1828, 1828, 1888, 1889, + 1888, 1949, 1889, 1889, 1949, 1950, 1949, 2010, 1950, 1950, 2010, 2011, + 2010, 2071, 2011, 2011, 2071, 2072, 2071, 2132, 2072, 2072, 2132, 2133, + 2132, 2193, 2133, 2133, 2193, 2194, 2193, 2254, 2194, 2194, 2254, 2255, + 2254, 2315, 2255, 2255, 2315, 2316, 2315, 2376, 2316, 2316, 2376, 2377, + 2376, 2437, 2377, 2377, 2437, 2438, 2437, 2498, 2438, 2438, 2498, 2499, + 2498, 2559, 2499, 2499, 2559, 2560, 2559, 2620, 2560, 2560, 2620, 2621, + 2620, 2681, 2621, 2621, 2681, 2682, 2681, 2742, 2682, 2682, 2742, 2743, + 2742, 2803, 2743, 2743, 2803, 2804, 2803, 2864, 2804, 2804, 2864, 2865, + 2864, 2925, 2865, 2865, 2925, 2926, 2925, 2986, 2926, 2926, 2986, 2987, + 2986, 3047, 2987, 2987, 3047, 3048, 3047, 3108, 3048, 3048, 3108, 3109, + 3108, 3169, 3109, 3109, 3169, 3170, 3169, 3230, 3170, 3170, 3230, 3231, + 3230, 3291, 3231, 3231, 3291, 3292, 3291, 3352, 3292, 3292, 3352, 3353, + 3352, 3413, 3353, 3353, 3413, 3414, 3413, 3474, 3414, 3414, 3474, 3475, + 3474, 3535, 3475, 3475, 3535, 3536, 3535, 3596, 3536, 3536, 3596, 3597, + 3596, 3657, 3597, 3597, 3657, 3658, 3657, 3718, 3658, 3658, 3718, 3719, + 59, 120, 60, 60, 120, 121, 120, 181, 121, 121, 181, 182, + 181, 242, 182, 182, 242, 243, 242, 303, 243, 243, 303, 304, + 303, 364, 304, 304, 364, 365, 364, 425, 365, 365, 425, 426, + 425, 486, 426, 426, 486, 487, 486, 547, 487, 487, 547, 548, + 547, 608, 548, 548, 608, 609, 608, 669, 609, 609, 669, 670, + 669, 730, 670, 670, 730, 731, 730, 791, 731, 731, 791, 792, + 791, 852, 792, 792, 852, 853, 852, 913, 853, 853, 913, 914, + 913, 974, 914, 914, 974, 975, 974, 1035, 975, 975, 1035, 1036, + 1035, 1096, 1036, 1036, 1096, 1097, 1096, 1157, 1097, 1097, 1157, 1158, + 1157, 1218, 1158, 1158, 1218, 1219, 1218, 1279, 1219, 1219, 1279, 1280, + 1279, 1340, 1280, 1280, 1340, 1341, 1340, 1401, 1341, 1341, 1401, 1402, + 1401, 1462, 1402, 1402, 1462, 1463, 1462, 1523, 1463, 1463, 1523, 1524, + 1523, 1584, 1524, 1524, 1584, 1585, 1584, 1645, 1585, 1585, 1645, 1646, + 1645, 1706, 1646, 1646, 1706, 1707, 1706, 1767, 1707, 1707, 1767, 1768, + 1767, 1828, 1768, 1768, 1828, 1829, 1828, 1889, 1829, 1829, 1889, 1890, + 1889, 1950, 1890, 1890, 1950, 1951, 1950, 2011, 1951, 1951, 2011, 2012, + 2011, 2072, 2012, 2012, 2072, 2073, 2072, 2133, 2073, 2073, 2133, 2134, + 2133, 2194, 2134, 2134, 2194, 2195, 2194, 2255, 2195, 2195, 2255, 2256, + 2255, 2316, 2256, 2256, 2316, 2317, 2316, 2377, 2317, 2317, 2377, 2378, + 2377, 2438, 2378, 2378, 2438, 2439, 2438, 2499, 2439, 2439, 2499, 2500, + 2499, 2560, 2500, 2500, 2560, 2561, 2560, 2621, 2561, 2561, 2621, 2622, + 2621, 2682, 2622, 2622, 2682, 2683, 2682, 2743, 2683, 2683, 2743, 2744, + 2743, 2804, 2744, 2744, 2804, 2805, 2804, 2865, 2805, 2805, 2865, 2866, + 2865, 2926, 2866, 2866, 2926, 2927, 2926, 2987, 2927, 2927, 2987, 2988, + 2987, 3048, 2988, 2988, 3048, 3049, 3048, 3109, 3049, 3049, 3109, 3110, + 3109, 3170, 3110, 3110, 3170, 3171, 3170, 3231, 3171, 3171, 3231, 3232, + 3231, 3292, 3232, 3232, 3292, 3293, 3292, 3353, 3293, 3293, 3353, 3354, + 3353, 3414, 3354, 3354, 3414, 3415, 3414, 3475, 3415, 3415, 3475, 3476, + 3475, 3536, 3476, 3476, 3536, 3537, 3536, 3597, 3537, 3537, 3597, 3598, + 3597, 3658, 3598, 3598, 3658, 3659, 3658, 3719, 3659, 3659, 3719, 3720, +}; + +} /* namespace Pdraw */ + +#endif /* USE_GLES2 */ diff --git a/libpdraw/src/pdraw_gles2_hmd_positions_cockpitglasses.cpp b/libpdraw/src/pdraw_gles2_hmd_positions_cockpitglasses.cpp index da98f99..0936148 100644 --- a/libpdraw/src/pdraw_gles2_hmd_positions_cockpitglasses.cpp +++ b/libpdraw/src/pdraw_gles2_hmd_positions_cockpitglasses.cpp @@ -1,1907 +1,1907 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * OpenGL ES 2.0 HMD distortion correction - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * Extracted from FPVToolbox by Frédéric Bertolus - * https://github.com/niavok/fpvtoolbox - * and translated from Java to C++ - */ - -#ifdef USE_GLES2 - -namespace Pdraw { - -extern const float pdraw_gles2HmdPositionsCockpitglasses[7442] = { - -23.5111312866, 23.5111312866, -23.7241382599, 22.9333324432, - -23.9451198578, 22.3487796783, -24.1725063324, 21.7552566528, - -24.4047813416, 21.1508102417, -24.6404800415, 20.5337333679, - -24.8781986237, 19.9025573730, -25.1165828705, 19.2560462952, - -25.3543395996, 18.5931816101, -25.5902252197, 17.9131584167, - -25.8230571747, 17.2153701782, -26.0517005920, 16.4994106293, - -26.2750816345, 15.7650489807, -26.4921798706, 15.0122346878, - -26.7020301819, 14.2410821915, -26.9037227631, 13.4518613815, - -27.0964012146, 12.6449871063, -27.2792663574, 11.8210153580, - -27.4515743256, 10.9806299210, -27.6126346588, 10.1246328354, - -27.7618141174, 9.2539377213, -27.8985328674, 8.3695592880, - -28.0222644806, 7.4726042747, -28.1325454712, 6.5642604828, - -28.2289581299, 5.6457915306, -28.3111438751, 4.7185239792, - -28.3788013458, 3.7838401794, -28.4316806793, 2.8431680202, - -28.4695892334, 1.8979727030, -28.4923896790, 0.9497463703, - -28.5000000000, -0.0000000000, -28.4923896790, -0.9497463703, - -28.4695892334, -1.8979727030, -28.4316806793, -2.8431680202, - -28.3788013458, -3.7838401794, -28.3111438751, -4.7185239792, - -28.2289581299, -5.6457915306, -28.1325454712, -6.5642604828, - -28.0222644806, -7.4726042747, -27.8985328674, -8.3695592880, - -27.7618141174, -9.2539377213, -27.6126346588, -10.1246328354, - -27.4515743256, -10.9806299210, -27.2792663574, -11.8210153580, - -27.0964012146, -12.6449871063, -26.9037227631, -13.4518613815, - -26.7020301819, -14.2410821915, -26.4921798706, -15.0122346878, - -26.2750816345, -15.7650489807, -26.0517005920, -16.4994106293, - -25.8230571747, -17.2153701782, -25.5902252197, -17.9131584167, - -25.3543395996, -18.5931816101, -25.1165828705, -19.2560462952, - -24.8781986237, -19.9025573730, -24.6404800415, -20.5337333679, - -24.4047813416, -21.1508102417, -24.1725063324, -21.7552566528, - -23.9451198578, -22.3487796783, -23.7241382599, -22.9333324432, - -23.5111312866, -23.5111312866, -22.9333324432, 23.7241382599, - -23.1547069550, 23.1547069550, -23.3832664490, 22.5769481659, - -23.6174926758, 21.9887008667, -23.8559188843, 21.3880653381, - -24.0971317291, 20.7733898163, -24.3397712708, 20.1432590485, - -24.5825328827, 19.4964923859, -24.8241615295, 18.8321228027, - -25.0634574890, 18.1494007111, -25.2992763519, 17.4477767944, - -25.5305233002, 16.7268943787, -25.7561588287, 15.9865808487, - -25.9751949310, 15.2268390656, -26.1867027283, 14.4478359222, - -26.3897991180, 13.6498956680, -26.5836582184, 12.8334894180, - -26.7675056458, 11.9992265701, -26.9406242371, 11.1478443146, - -27.1023464203, 10.2802000046, -27.2520580292, 9.3972616196, - -27.3892002106, 8.5000963211, -27.5132656097, 7.5898661613, - -27.6238021851, 6.6678142548, -27.7204093933, 5.7352571487, - -27.8027400970, 4.7935757637, -27.8705005646, 3.8442070484, - -27.9234523773, 2.8886330128, -27.9614086151, 1.9283730984, - -27.9842357635, 0.9649736881, -27.9918537140, -0.0000000000, - -27.9842357635, -0.9649736881, -27.9614086151, -1.9283730984, - -27.9234523773, -2.8886330128, -27.8705005646, -3.8442070484, - -27.8027400970, -4.7935757637, -27.7204093933, -5.7352571487, - -27.6238021851, -6.6678142548, -27.5132656097, -7.5898661613, - -27.3892002106, -8.5000963211, -27.2520580292, -9.3972616196, - -27.1023464203, -10.2802000046, -26.9406242371, -11.1478443146, - -26.7675056458, -11.9992265701, -26.5836582184, -12.8334894180, - -26.3897991180, -13.6498956680, -26.1867027283, -14.4478359222, - -25.9751949310, -15.2268390656, -25.7561588287, -15.9865808487, - -25.5305233002, -16.7268943787, -25.2992763519, -17.4477767944, - -25.0634574890, -18.1494007111, -24.8241615295, -18.8321228027, - -24.5825328827, -19.4964923859, -24.3397712708, -20.1432590485, - -24.0971317291, -20.7733898163, -23.8559188843, -21.3880653381, - -23.6174926758, -21.9887008667, -23.3832664490, -22.5769481659, - -23.1547069550, -23.1547069550, -22.9333324432, -23.7241382599, - -22.3487796783, 23.9451198578, -22.5769481659, 23.3832664490, - -22.8115653992, 22.8115653992, -23.0511646271, 22.2279090881, - -23.2943286896, 21.6304492950, -23.5396957397, 21.0175857544, - -23.7859516144, 20.3879585266, -24.0318355560, 19.7404365540, - -24.2761363983, 19.0741081238, -24.5176963806, 18.3882732391, - -24.7554092407, 17.6824359894, -24.9882202148, 16.9562911987, - -25.2151222229, 16.2097206116, -25.4351654053, 15.4427795410, - -25.6474494934, 14.6556854248, -25.8511238098, 13.8488159180, - -26.0453891754, 13.0226945877, -26.2294998169, 12.1779823303, - -26.4027633667, 11.3154697418, -26.5645332336, 10.4360666275, - -26.7142162323, 9.5407915115, -26.8512763977, 8.6307678223, - -26.9752216339, 7.7072062492, -27.0856151581, 6.7714037895, - -27.1820678711, 5.8247289658, -27.2642498016, 4.8686161041, - -27.3318767548, 3.9045536518, -27.3847141266, 2.9340765476, - -27.4225845337, 1.9587560892, -27.4453582764, 0.9801913500, - -27.4529571533, -0.0000000000, -27.4453582764, -0.9801913500, - -27.4225845337, -1.9587560892, -27.3847141266, -2.9340765476, - -27.3318767548, -3.9045536518, -27.2642498016, -4.8686161041, - -27.1820678711, -5.8247289658, -27.0856151581, -6.7714037895, - -26.9752216339, -7.7072062492, -26.8512763977, -8.6307678223, - -26.7142162323, -9.5407915115, -26.5645332336, -10.4360666275, - -26.4027633667, -11.3154697418, -26.2294998169, -12.1779823303, - -26.0453891754, -13.0226945877, -25.8511238098, -13.8488159180, - -25.6474494934, -14.6556854248, -25.4351654053, -15.4427795410, - -25.2151222229, -16.2097206116, -24.9882202148, -16.9562911987, - -24.7554092407, -17.6824359894, -24.5176963806, -18.3882732391, - -24.2761363983, -19.0741081238, -24.0318355560, -19.7404365540, - -23.7859516144, -20.3879585266, -23.5396957397, -21.0175857544, - -23.2943286896, -21.6304492950, -23.0511646271, -22.2279090881, - -22.8115653992, -22.8115653992, -22.5769481659, -23.3832664490, - -22.3487796783, -23.9451198578, -21.7552566528, 24.1725063324, - -21.9887008667, 23.6174926758, -22.2279090881, 23.0511646271, - -22.4714660645, 22.4714660645, -22.7180061340, 21.8765983582, - -22.9662132263, 21.2650127411, -23.2148227692, 20.6353988647, - -23.4626197815, 19.9866752625, -23.7084350586, 19.3179836273, - -23.9511528015, 18.6286735535, -24.1897048950, 17.9182987213, - -24.4230728149, 17.1866073608, -24.6502914429, 16.4335269928, - -24.8704395294, 15.6591653824, -25.0826492310, 14.8637924194, - -25.2861042023, 14.0478353500, -25.4800300598, 13.2118673325, - -25.6637096405, 12.3566007614, -25.8364715576, 11.4828767776, - -25.9976978302, 10.5916547775, -26.1468143463, 9.6840057373, - -26.2833023071, 8.7611007690, -26.4066886902, 7.8242039680, - -26.5165519714, 6.8746614456, -26.6125202179, 5.9138932228, - -26.6942691803, 4.9433832169, -26.7615280151, 3.9646706581, - -26.8140716553, 2.9793412685, -26.8517265320, 1.9890167713, - -26.8743686676, 0.9953470230, -26.8819255829, -0.0000000000, - -26.8743686676, -0.9953470230, -26.8517265320, -1.9890167713, - -26.8140716553, -2.9793412685, -26.7615280151, -3.9646706581, - -26.6942691803, -4.9433832169, -26.6125202179, -5.9138932228, - -26.5165519714, -6.8746614456, -26.4066886902, -7.8242039680, - -26.2833023071, -8.7611007690, -26.1468143463, -9.6840057373, - -25.9976978302, -10.5916547775, -25.8364715576, -11.4828767776, - -25.6637096405, -12.3566007614, -25.4800300598, -13.2118673325, - -25.2861042023, -14.0478353500, -25.0826492310, -14.8637924194, - -24.8704395294, -15.6591653824, -24.6502914429, -16.4335269928, - -24.4230728149, -17.1866073608, -24.1897048950, -17.9182987213, - -23.9511528015, -18.6286735535, -23.7084350586, -19.3179836273, - -23.4626197815, -19.9866752625, -23.2148227692, -20.6353988647, - -22.9662132263, -21.2650127411, -22.7180061340, -21.8765983582, - -22.4714660645, -22.4714660645, -22.2279090881, -23.0511646271, - -21.9887008667, -23.6174926758, -21.7552566528, -24.1725063324, - -21.1508102417, 24.4047813416, -21.3880653381, 23.8559188843, - -21.6304492950, 23.2943286896, -21.8765983582, 22.7180061340, - -22.1251964569, 22.1251964569, -22.3749809265, 21.5144042969, - -22.6247291565, 20.8843650818, -22.8732719421, 20.2340469360, - -23.1194839478, 19.5626392365, -23.3622894287, 18.8695411682, - -23.6006641388, 18.1543560028, -23.8336238861, 17.4168796539, - -24.0602378845, 16.6570873260, -24.2796230316, 15.8751382828, - -24.4909420013, 15.0713491440, -24.6934051514, 14.2461948395, - -24.8862724304, 13.4003000259, -25.0688495636, 12.5344247818, - -25.2404918671, 11.6494579315, -25.4006023407, 10.7464084625, - -25.5486316681, 9.8263969421, -25.6840744019, 8.8906412125, - -25.8064804077, 7.9404559135, -25.9154434204, 6.9772343636, - -26.0106010437, 6.0024461746, -26.0916442871, 5.0176239014, - -26.1583118439, 4.0243558884, -26.2103881836, 3.0242755413, - -26.2477054596, 2.0190541744, -26.2701416016, 1.0103900433, - -26.2776298523, -0.0000000000, -26.2701416016, -1.0103900433, - -26.2477054596, -2.0190541744, -26.2103881836, -3.0242755413, - -26.1583118439, -4.0243558884, -26.0916442871, -5.0176239014, - -26.0106010437, -6.0024461746, -25.9154434204, -6.9772343636, - -25.8064804077, -7.9404559135, -25.6840744019, -8.8906412125, - -25.5486316681, -9.8263969421, -25.4006023407, -10.7464084625, - -25.2404918671, -11.6494579315, -25.0688495636, -12.5344247818, - -24.8862724304, -13.4003000259, -24.6934051514, -14.2461948395, - -24.4909420013, -15.0713491440, -24.2796230316, -15.8751382828, - -24.0602378845, -16.6570873260, -23.8336238861, -17.4168796539, - -23.6006641388, -18.1543560028, -23.3622894287, -18.8695411682, - -23.1194839478, -19.5626392365, -22.8732719421, -20.2340469360, - -22.6247291565, -20.8843650818, -22.3749809265, -21.5144042969, - -22.1251964569, -22.1251964569, -21.8765983582, -22.7180061340, - -21.6304492950, -23.2943286896, -21.3880653381, -23.8559188843, - -21.1508102417, -24.4047813416, -20.5337333679, 24.6404800415, - -20.7733898163, 24.0971317291, -21.0175857544, 23.5396957397, - -21.2650127411, 22.9662132263, -21.5144042969, 22.3749809265, - -21.7645435333, 21.7645435333, -22.0142593384, 21.1336898804, - -22.2624244690, 20.4814300537, -22.5079574585, 19.8070030212, - -22.7498264313, 19.1098537445, -22.9870414734, 18.3896331787, - -23.2186603546, 17.6461811066, -23.4437885284, 16.8795280457, - -23.6615715027, 16.0898685455, -23.8712100983, 15.2775745392, - -24.0719413757, 14.4431657791, -24.2630558014, 13.5873117447, - -24.4438858032, 12.7108211517, -24.6138114929, 11.8146295547, - -24.7722568512, 10.8997936249, -24.9186954498, 9.9674777985, - -25.0526409149, 9.0189504623, -25.1736602783, 8.0555715561, - -25.2813606262, 7.0787811279, -25.3753986359, 6.0900959969, - -25.4554748535, 5.0910949707, -25.5213375092, 4.0834140778, - -25.5727767944, 3.0687332153, -25.6096363068, 2.0487709045, - -25.6317958832, 1.0252718925, -25.6391906738, -0.0000000000, - -25.6317958832, -1.0252718925, -25.6096363068, -2.0487709045, - -25.5727767944, -3.0687332153, -25.5213375092, -4.0834140778, - -25.4554748535, -5.0910949707, -25.3753986359, -6.0900959969, - -25.2813606262, -7.0787811279, -25.1736602783, -8.0555715561, - -25.0526409149, -9.0189504623, -24.9186954498, -9.9674777985, - -24.7722568512, -10.8997936249, -24.6138114929, -11.8146295547, - -24.4438858032, -12.7108211517, -24.2630558014, -13.5873117447, - -24.0719413757, -14.4431657791, -23.8712100983, -15.2775745392, - -23.6615715027, -16.0898685455, -23.4437885284, -16.8795280457, - -23.2186603546, -17.6461811066, -22.9870414734, -18.3896331787, - -22.7498264313, -19.1098537445, -22.5079574585, -19.8070030212, - -22.2624244690, -20.4814300537, -22.0142593384, -21.1336898804, - -21.7645435333, -21.7645435333, -21.5144042969, -22.3749809265, - -21.2650127411, -22.9662132263, -21.0175857544, -23.5396957397, - -20.7733898163, -24.0971317291, -20.5337333679, -24.6404800415, - -19.9025573730, 24.8781986237, -20.1432590485, 24.3397712708, - -20.3879585266, 23.7859516144, -20.6353988647, 23.2148227692, - -20.8843650818, 22.6247291565, -21.1336898804, 22.0142593384, - -21.3822460175, 21.3822460175, -21.6289520264, 20.7277450562, - -21.8727722168, 20.0500411987, -22.1127147675, 19.3486251831, - -22.3478298187, 18.6231899261, -22.5772113800, 17.8736248016, - -22.7999992371, 17.1000003815, -23.0153789520, 16.3025608063, - -23.2225780487, 15.4817190170, -23.4208679199, 14.6380424500, - -23.6095619202, 13.7722444534, -23.7880249023, 12.8851795197, - -23.9556579590, 11.9778289795, -24.1119098663, 11.0512914658, - -24.2562732697, 10.1067810059, -24.3882865906, 9.1456069946, - -24.5075283051, 8.1691761017, -24.6136226654, 7.1789731979, - -24.7062416077, 6.1765604019, -24.7850971222, 5.1635618210, - -24.8499469757, 4.1416578293, -24.9005908966, 3.1125738621, - -24.9368743896, 2.0780727863, -24.9586887360, 1.0399453640, - -24.9659690857, -0.0000000000, -24.9586887360, -1.0399453640, - -24.9368743896, -2.0780727863, -24.9005908966, -3.1125738621, - -24.8499469757, -4.1416578293, -24.7850971222, -5.1635618210, - -24.7062416077, -6.1765604019, -24.6136226654, -7.1789731979, - -24.5075283051, -8.1691761017, -24.3882865906, -9.1456069946, - -24.2562732697, -10.1067810059, -24.1119098663, -11.0512914658, - -23.9556579590, -11.9778289795, -23.7880249023, -12.8851795197, - -23.6095619202, -13.7722444534, -23.4208679199, -14.6380424500, - -23.2225780487, -15.4817190170, -23.0153789520, -16.3025608063, - -22.7999992371, -17.1000003815, -22.5772113800, -17.8736248016, - -22.3478298187, -18.6231899261, -22.1127147675, -19.3486251831, - -21.8727722168, -20.0500411987, -21.6289520264, -20.7277450562, - -21.3822460175, -21.3822460175, -21.1336898804, -22.0142593384, - -20.8843650818, -22.6247291565, -20.6353988647, -23.2148227692, - -20.3879585266, -23.7859516144, -20.1432590485, -24.3397712708, - -19.9025573730, -24.8781986237, -19.2560462952, 25.1165828705, - -19.4964923859, 24.5825328827, -19.7404365540, 24.0318355560, - -19.9866752625, 23.4626197815, -20.2340469360, 22.8732719421, - -20.4814300537, 22.2624244690, -20.7277450562, 21.6289520264, - -20.9719581604, 20.9719581604, -21.2130737305, 20.2907657623, - -21.4501399994, 19.5849094391, -21.6822490692, 18.8541297913, - -21.9085330963, 18.0983524323, -22.1281681061, 17.3176956177, - -22.3403701782, 16.5124473572, -22.5444011688, 15.6830615997, - -22.7395629883, 14.8301496506, -22.9251995087, 13.9544687271, - -23.1006965637, 13.0569162369, -23.2654857635, 12.1385145187, - -23.4190368652, 11.2004089355, -23.5608654022, 10.2438545227, - -23.6905231476, 9.2702045441, -23.8076114655, 8.2809085846, - -23.9117717743, 7.2774958611, -24.0026855469, 6.2615699768, - -24.0800762177, 5.2347993851, -24.1437149048, 4.1989068985, - -24.1934070587, 3.1556618214, -24.2290077209, 2.1068701744, - -24.2504119873, 1.0543657541, -24.2575531006, -0.0000000000, - -24.2504119873, -1.0543657541, -24.2290077209, -2.1068701744, - -24.1934070587, -3.1556618214, -24.1437149048, -4.1989068985, - -24.0800762177, -5.2347993851, -24.0026855469, -6.2615699768, - -23.9117717743, -7.2774958611, -23.8076114655, -8.2809085846, - -23.6905231476, -9.2702045441, -23.5608654022, -10.2438545227, - -23.4190368652, -11.2004089355, -23.2654857635, -12.1385145187, - -23.1006965637, -13.0569162369, -22.9251995087, -13.9544687271, - -22.7395629883, -14.8301496506, -22.5444011688, -15.6830615997, - -22.3403701782, -16.5124473572, -22.1281681061, -17.3176956177, - -21.9085330963, -18.0983524323, -21.6822490692, -18.8541297913, - -21.4501399994, -19.5849094391, -21.2130737305, -20.2907657623, - -20.9719581604, -20.9719581604, -20.7277450562, -21.6289520264, - -20.4814300537, -22.2624244690, -20.2340469360, -22.8732719421, - -19.9866752625, -23.4626197815, -19.7404365540, -24.0318355560, - -19.4964923859, -24.5825328827, -19.2560462952, -25.1165828705, - -18.5931816101, 25.3543395996, -18.8321228027, 24.8241615295, - -19.0741081238, 24.2761363983, -19.3179836273, 23.7084350586, - -19.5626392365, 23.1194839478, -19.8070030212, 22.5079574585, - -20.0500411987, 21.8727722168, -20.2907657623, 21.2130737305, - -20.5282230377, 20.5282230377, -20.7615051270, 19.8178005219, - -20.9897422791, 19.0815830231, -21.2121028900, 18.3195438385, - -21.4278011322, 17.5318374634, -21.6360874176, 16.7187938690, - -21.8362541199, 15.8809118271, -22.0276317596, 15.0188398361, - -22.2095966339, 14.1333789825, -22.3815593719, 13.2254667282, - -22.5429763794, 12.2961692810, -22.6933403015, 11.3466701508, - -22.8321857452, 10.3782663345, -22.9590892792, 9.3923549652, - -23.0736675262, 8.3904247284, -23.1755733490, 7.3740458488, - -23.2645053864, 6.3448653221, -23.3402004242, 5.3045911789, - -23.4024372101, 4.2549886703, -23.4510307312, 3.1978678703, - -23.4858417511, 2.1350765228, -23.5067691803, 1.0684895515, - -23.5137519836, -0.0000000000, -23.5067691803, -1.0684895515, - -23.4858417511, -2.1350765228, -23.4510307312, -3.1978678703, - -23.4024372101, -4.2549886703, -23.3402004242, -5.3045911789, - -23.2645053864, -6.3448653221, -23.1755733490, -7.3740458488, - -23.0736675262, -8.3904247284, -22.9590892792, -9.3923549652, - -22.8321857452, -10.3782663345, -22.6933403015, -11.3466701508, - -22.5429763794, -12.2961692810, -22.3815593719, -13.2254667282, - -22.2095966339, -14.1333789825, -22.0276317596, -15.0188398361, - -21.8362541199, -15.8809118271, -21.6360874176, -16.7187938690, - -21.4278011322, -17.5318374634, -21.2121028900, -18.3195438385, - -20.9897422791, -19.0815830231, -20.7615051270, -19.8178005219, - -20.5282230377, -20.5282230377, -20.2907657623, -21.2130737305, - -20.0500411987, -21.8727722168, -19.8070030212, -22.5079574585, - -19.5626392365, -23.1194839478, -19.3179836273, -23.7084350586, - -19.0741081238, -24.2761363983, -18.8321228027, -24.8241615295, - -18.5931816101, -25.3543395996, -17.9131584167, 25.5902252197, - -18.1494007111, 25.0634574890, -18.3882732391, 24.5176963806, - -18.6286735535, 23.9511528015, -18.8695411682, 23.3622894287, - -19.1098537445, 22.7498264313, -19.3486251831, 22.1127147675, - -19.5849094391, 21.4501399994, -19.8178005219, 20.7615051270, - -20.0464286804, 20.0464286804, -20.2699642181, 19.3047275543, - -20.4876136780, 18.5364131927, -20.6986255646, 17.7416801453, - -20.9022865295, 16.9208984375, -21.0979213715, 16.0746059418, - -21.2848892212, 15.2034921646, -21.4625949860, 14.3083972931, - -21.6304779053, 13.3902959824, -21.7880172729, 12.4502954483, - -21.9347286224, 11.4896192551, -22.0701675415, 10.5096044540, - -22.1939334869, 9.5116853714, -22.3056526184, 8.4973917007, - -22.4050025940, 7.4683341980, -22.4916915894, 6.4261975288, - -22.5654678345, 5.3727307320, -22.6261196136, 4.3097372055, - -22.6734752655, 3.2390677929, -22.7073955536, 2.1626091003, - -22.7277870178, 1.0822755098, -22.7345905304, -0.0000000000, - -22.7277870178, -1.0822755098, -22.7073955536, -2.1626091003, - -22.6734752655, -3.2390677929, -22.6261196136, -4.3097372055, - -22.5654678345, -5.3727307320, -22.4916915894, -6.4261975288, - -22.4050025940, -7.4683341980, -22.3056526184, -8.4973917007, - -22.1939334869, -9.5116853714, -22.0701675415, -10.5096044540, - -21.9347286224, -11.4896192551, -21.7880172729, -12.4502954483, - -21.6304779053, -13.3902959824, -21.4625949860, -14.3083972931, - -21.2848892212, -15.2034921646, -21.0979213715, -16.0746059418, - -20.9022865295, -16.9208984375, -20.6986255646, -17.7416801453, - -20.4876136780, -18.5364131927, -20.2699642181, -19.3047275543, - -20.0464286804, -20.0464286804, -19.8178005219, -20.7615051270, - -19.5849094391, -21.4501399994, -19.3486251831, -22.1127147675, - -19.1098537445, -22.7498264313, -18.8695411682, -23.3622894287, - -18.6286735535, -23.9511528015, -18.3882732391, -24.5176963806, - -18.1494007111, -25.0634574890, -17.9131584167, -25.5902252197, - -17.2153701782, 25.8230571747, -17.4477767944, 25.2992763519, - -17.6824359894, 24.7554092407, -17.9182987213, 24.1897048950, - -18.1543560028, 23.6006641388, -18.3896331787, 22.9870414734, - -18.6231899261, 22.3478298187, -18.8541297913, 21.6822490692, - -19.0815830231, 20.9897422791, -19.3047275543, 20.2699642181, - -19.5227680206, 19.5227680206, -19.7349548340, 18.7482070923, - -19.9405689240, 17.9465122223, -20.1389274597, 17.1180896759, - -20.3293914795, 16.2635135651, -20.5113525391, 15.3835144043, - -20.6842403412, 14.4789676666, -20.8475208282, 13.5508880615, - -21.0006980896, 12.6004190445, -21.1433124542, 11.6288223267, - -21.2749423981, 10.6374711990, -21.3952007294, 9.6278400421, - -21.5037364960, 8.6014947891, -21.6002407074, 7.5600843430, - -21.6844348907, 6.5053300858, -21.7560806274, 5.4390201569, - -21.8149738312, 4.3629951477, -21.8609542847, 3.2791430950, - -21.8938865662, 2.1893887520, -21.9136848450, 1.0956841707, - -21.9202899933, -0.0000000000, -21.9136848450, -1.0956841707, - -21.8938865662, -2.1893887520, -21.8609542847, -3.2791430950, - -21.8149738312, -4.3629951477, -21.7560806274, -5.4390201569, - -21.6844348907, -6.5053300858, -21.6002407074, -7.5600843430, - -21.5037364960, -8.6014947891, -21.3952007294, -9.6278400421, - -21.2749423981, -10.6374711990, -21.1433124542, -11.6288223267, - -21.0006980896, -12.6004190445, -20.8475208282, -13.5508880615, - -20.6842403412, -14.4789676666, -20.5113525391, -15.3835144043, - -20.3293914795, -16.2635135651, -20.1389274597, -17.1180896759, - -19.9405689240, -17.9465122223, -19.7349548340, -18.7482070923, - -19.5227680206, -19.5227680206, -19.3047275543, -20.2699642181, - -19.0815830231, -20.9897422791, -18.8541297913, -21.6822490692, - -18.6231899261, -22.3478298187, -18.3896331787, -22.9870414734, - -18.1543560028, -23.6006641388, -17.9182987213, -24.1897048950, - -17.6824359894, -24.7554092407, -17.4477767944, -25.2992763519, - -17.2153701782, -25.8230571747, -16.4994106293, 26.0517005920, - -16.7268943787, 25.5305233002, -16.9562911987, 24.9882202148, - -17.1866073608, 24.4230728149, -17.4168796539, 23.8336238861, - -17.6461811066, 23.2186603546, -17.8736248016, 22.5772113800, - -18.0983524323, 21.9085330963, -18.3195438385, 21.2121028900, - -18.5364131927, 20.4876136780, -18.7482070923, 19.7349548340, - -18.9542121887, 18.9542121887, -19.1537456512, 18.1456527710, - -19.3461608887, 17.3097229004, -19.5308494568, 16.4470310211, - -19.7072315216, 15.5583400726, -19.8747653961, 14.6445646286, - -20.0329475403, 13.7067537308, -20.1813049316, 12.7460880280, - -20.3194007874, 11.7638635635, -20.4468326569, 10.7614917755, - -20.5632362366, 9.7404804230, -20.6682758331, 8.7024316788, - -20.7616577148, 7.6490316391, -20.8431167603, 6.5820369720, - -20.9124298096, 5.5032711029, -20.9694004059, 4.4146108627, - -21.0138759613, 3.3179802895, -21.0457305908, 2.2153401375, - -21.0648784637, 1.1086778641, -21.0712661743, -0.0000000000, - -21.0648784637, -1.1086778641, -21.0457305908, -2.2153401375, - -21.0138759613, -3.3179802895, -20.9694004059, -4.4146108627, - -20.9124298096, -5.5032711029, -20.8431167603, -6.5820369720, - -20.7616577148, -7.6490316391, -20.6682758331, -8.7024316788, - -20.5632362366, -9.7404804230, -20.4468326569, -10.7614917755, - -20.3194007874, -11.7638635635, -20.1813049316, -12.7460880280, - -20.0329475403, -13.7067537308, -19.8747653961, -14.6445646286, - -19.7072315216, -15.5583400726, -19.5308494568, -16.4470310211, - -19.3461608887, -17.3097229004, -19.1537456512, -18.1456527710, - -18.9542121887, -18.9542121887, -18.7482070923, -19.7349548340, - -18.5364131927, -20.4876136780, -18.3195438385, -21.2121028900, - -18.0983524323, -21.9085330963, -17.8736248016, -22.5772113800, - -17.6461811066, -23.2186603546, -17.4168796539, -23.8336238861, - -17.1866073608, -24.4230728149, -16.9562911987, -24.9882202148, - -16.7268943787, -25.5305233002, -16.4994106293, -26.0517005920, - -15.7650489807, 26.2750816345, -15.9865808487, 25.7561588287, - -16.2097206116, 25.2151222229, -16.4335269928, 24.6502914429, - -16.6570873260, 24.0602378845, -16.8795280457, 23.4437885284, - -17.1000003815, 22.7999992371, -17.3176956177, 22.1281681061, - -17.5318374634, 21.4278011322, -17.7416801453, 20.6986255646, - -17.9465122223, 19.9405689240, -18.1456527710, 19.1537456512, - -18.3384609222, 18.3384609222, -18.5243206024, 17.4951934814, - -18.7026557922, 16.6245822906, -18.8729190826, 15.7274322510, - -19.0345954895, 14.8046855927, -19.1872081757, 13.8574275970, - -19.3303070068, 12.8868713379, -19.4634819031, 11.8943500519, - -19.5863494873, 10.8813056946, -19.6985645294, 9.8492822647, - -19.7998123169, 8.7999162674, -19.8898086548, 7.7349257469, - -19.9683074951, 6.6561026573, -20.0350952148, 5.5653042793, - -20.0899868011, 4.4644412994, -20.1328353882, 3.3554723263, - -20.1635227203, 2.2403914928, -20.1819686890, 1.1212204695, - -20.1881237030, -0.0000000000, -20.1819686890, -1.1212204695, - -20.1635227203, -2.2403914928, -20.1328353882, -3.3554723263, - -20.0899868011, -4.4644412994, -20.0350952148, -5.5653042793, - -19.9683074951, -6.6561026573, -19.8898086548, -7.7349257469, - -19.7998123169, -8.7999162674, -19.6985645294, -9.8492822647, - -19.5863494873, -10.8813056946, -19.4634819031, -11.8943500519, - -19.3303070068, -12.8868713379, -19.1872081757, -13.8574275970, - -19.0345954895, -14.8046855927, -18.8729190826, -15.7274322510, - -18.7026557922, -16.6245822906, -18.5243206024, -17.4951934814, - -18.3384609222, -18.3384609222, -18.1456527710, -19.1537456512, - -17.9465122223, -19.9405689240, -17.7416801453, -20.6986255646, - -17.5318374634, -21.4278011322, -17.3176956177, -22.1281681061, - -17.1000003815, -22.7999992371, -16.8795280457, -23.4437885284, - -16.6570873260, -24.0602378845, -16.4335269928, -24.6502914429, - -16.2097206116, -25.2151222229, -15.9865808487, -25.7561588287, - -15.7650489807, -26.2750816345, -15.0122346878, 26.4921798706, - -15.2268390656, 25.9751949310, -15.4427795410, 25.4351654053, - -15.6591653824, 24.8704395294, -15.8751382828, 24.2796230316, - -16.0898685455, 23.6615715027, -16.3025608063, 23.0153789520, - -16.5124473572, 22.3403701782, -16.7187938690, 21.6360874176, - -16.9208984375, 20.9022865295, -17.1180896759, 20.1389274597, - -17.3097229004, 19.3461608887, -17.4951934814, 18.5243206024, - -17.6739177704, 17.6739177704, -17.8453540802, 16.7956275940, - -18.0089836121, 15.8902797699, -18.1643218994, 14.9588537216, - -18.3109169006, 14.0024662018, -18.4483470917, 13.0223627090, - -18.5762195587, 12.0199069977, -18.6941757202, 10.9965744019, - -18.8018894196, 9.9539413452, -18.8990612030, 8.8936758041, - -18.9854259491, 7.8175282478, -19.0607490540, 6.7273230553, - -19.1248283386, 5.6249494553, -19.1774902344, 4.5123505592, - -19.2185974121, 3.3915169239, -19.2480354309, 2.2644748688, - -19.2657318115, 1.1332782507, -19.2716350555, -0.0000000000, - -19.2657318115, -1.1332782507, -19.2480354309, -2.2644748688, - -19.2185974121, -3.3915169239, -19.1774902344, -4.5123505592, - -19.1248283386, -5.6249494553, -19.0607490540, -6.7273230553, - -18.9854259491, -7.8175282478, -18.8990612030, -8.8936758041, - -18.8018894196, -9.9539413452, -18.6941757202, -10.9965744019, - -18.5762195587, -12.0199069977, -18.4483470917, -13.0223627090, - -18.3109169006, -14.0024662018, -18.1643218994, -14.9588537216, - -18.0089836121, -15.8902797699, -17.8453540802, -16.7956275940, - -17.6739177704, -17.6739177704, -17.4951934814, -18.5243206024, - -17.3097229004, -19.3461608887, -17.1180896759, -20.1389274597, - -16.9208984375, -20.9022865295, -16.7187938690, -21.6360874176, - -16.5124473572, -22.3403701782, -16.3025608063, -23.0153789520, - -16.0898685455, -23.6615715027, -15.8751382828, -24.2796230316, - -15.6591653824, -24.8704395294, -15.4427795410, -25.4351654053, - -15.2268390656, -25.9751949310, -15.0122346878, -26.4921798706, - -14.2410821915, 26.7020301819, -14.4478359222, 26.1867027283, - -14.6556854248, 25.6474494934, -14.8637924194, 25.0826492310, - -15.0713491440, 24.4909420013, -15.2775745392, 23.8712100983, - -15.4817190170, 23.2225780487, -15.6830615997, 22.5444011688, - -15.8809118271, 21.8362541199, -16.0746059418, 21.0979213715, - -16.2635135651, 20.3293914795, -16.4470310211, 19.5308494568, - -16.6245822906, 18.7026557922, -16.7956275940, 17.8453540802, - -16.9596481323, 16.9596481323, -17.1161594391, 16.0464000702, - -17.2647075653, 15.1066188812, -17.4048633575, 14.1414518356, - -17.5362319946, 13.1521739960, -17.6584434509, 12.1401796341, - -17.7711601257, 11.1069755554, -17.8740749359, 10.0541667938, - -17.9669055939, 8.9834527969, -18.0494022369, 7.8966135979, - -18.1213474274, 6.7955055237, -18.1825466156, 5.6820459366, - -18.2328395844, 4.5582098961, -18.2720947266, 3.4260177612, - -18.3002071381, 2.2875258923, -18.3171043396, 1.1448190212, - -18.3227405548, -0.0000000000, -18.3171043396, -1.1448190212, - -18.3002071381, -2.2875258923, -18.2720947266, -3.4260177612, - -18.2328395844, -4.5582098961, -18.1825466156, -5.6820459366, - -18.1213474274, -6.7955055237, -18.0494022369, -7.8966135979, - -17.9669055939, -8.9834527969, -17.8740749359, -10.0541667938, - -17.7711601257, -11.1069755554, -17.6584434509, -12.1401796341, - -17.5362319946, -13.1521739960, -17.4048633575, -14.1414518356, - -17.2647075653, -15.1066188812, -17.1161594391, -16.0464000702, - -16.9596481323, -16.9596481323, -16.7956275940, -17.8453540802, - -16.6245822906, -18.7026557922, -16.4470310211, -19.5308494568, - -16.2635135651, -20.3293914795, -16.0746059418, -21.0979213715, - -15.8809118271, -21.8362541199, -15.6830615997, -22.5444011688, - -15.4817190170, -23.2225780487, -15.2775745392, -23.8712100983, - -15.0713491440, -24.4909420013, -14.8637924194, -25.0826492310, - -14.6556854248, -25.6474494934, -14.4478359222, -26.1867027283, - -14.2410821915, -26.7020301819, -13.4518613815, 26.9037227631, - -13.6498956680, 26.3897991180, -13.8488159180, 25.8511238098, - -14.0478353500, 25.2861042023, -14.2461948395, 24.6934051514, - -14.4431657791, 24.0719413757, -14.6380424500, 23.4208679199, - -14.8301496506, 22.7395629883, -15.0188398361, 22.0276317596, - -15.2034921646, 21.2848892212, -15.3835144043, 20.5113525391, - -15.5583400726, 19.7072315216, -15.7274322510, 18.8729190826, - -15.8902797699, 18.0089836121, -16.0464000702, 17.1161594391, - -16.1953392029, 16.1953392029, -16.3366680145, 15.2475576401, - -16.4699878693, 14.2739896774, -16.5949268341, 13.2759418488, - -16.7111396790, 12.2548351288, -16.8183078766, 11.2122049332, - -16.9161434174, 10.1496858597, -17.0043830872, 9.0690040588, - -17.0827941895, 7.9719705582, -17.1511688232, 6.8604674339, - -17.2093257904, 5.7364420891, -17.2571182251, 4.6018981934, - -17.2944164276, 3.4588835239, -17.3211288452, 2.3094837666, - -17.3371829987, 1.1558121443, -17.3425388336, -0.0000000000, - -17.3371829987, -1.1558121443, -17.3211288452, -2.3094837666, - -17.2944164276, -3.4588835239, -17.2571182251, -4.6018981934, - -17.2093257904, -5.7364420891, -17.1511688232, -6.8604674339, - -17.0827941895, -7.9719705582, -17.0043830872, -9.0690040588, - -16.9161434174, -10.1496858597, -16.8183078766, -11.2122049332, - -16.7111396790, -12.2548351288, -16.5949268341, -13.2759418488, - -16.4699878693, -14.2739896774, -16.3366680145, -15.2475576401, - -16.1953392029, -16.1953392029, -16.0464000702, -17.1161594391, - -15.8902797699, -18.0089836121, -15.7274322510, -18.8729190826, - -15.5583400726, -19.7072315216, -15.3835144043, -20.5113525391, - -15.2034921646, -21.2848892212, -15.0188398361, -22.0276317596, - -14.8301496506, -22.7395629883, -14.6380424500, -23.4208679199, - -14.4431657791, -24.0719413757, -14.2461948395, -24.6934051514, - -14.0478353500, -25.2861042023, -13.8488159180, -25.8511238098, - -13.6498956680, -26.3897991180, -13.4518613815, -26.9037227631, - -12.6449871063, 27.0964012146, -12.8334894180, 26.5836582184, - -13.0226945877, 26.0453891754, -13.2118673325, 25.4800300598, - -13.4003000259, 24.8862724304, -13.5873117447, 24.2630558014, - -13.7722444534, 23.6095619202, -13.9544687271, 22.9251995087, - -14.1333789825, 22.2095966339, -14.3083972931, 21.4625949860, - -14.4789676666, 20.6842403412, -14.6445646286, 19.8747653961, - -14.8046855927, 19.0345954895, -14.9588537216, 18.1643218994, - -15.1066188812, 17.2647075653, -15.2475576401, 16.3366680145, - -15.3812685013, 15.3812685013, -15.5073804855, 14.3997106552, - -15.6255445480, 13.3933238983, -15.7354402542, 12.3635606766, - -15.8367710114, 11.3119792938, -15.9292659760, 10.2402420044, - -16.0126800537, 9.1501035690, -16.0867977142, 8.0433988571, - -16.1514225006, 6.9220380783, -16.2063865662, 5.7879953384, - -16.2515525818, 4.6433005333, -16.2868003845, 3.4900286198, - -16.3120422363, 2.3302917480, -16.3272132874, 1.1662294865, - -16.3322734833, -0.0000000000, -16.3272132874, -1.1662294865, - -16.3120422363, -2.3302917480, -16.2868003845, -3.4900286198, - -16.2515525818, -4.6433005333, -16.2063865662, -5.7879953384, - -16.1514225006, -6.9220380783, -16.0867977142, -8.0433988571, - -16.0126800537, -9.1501035690, -15.9292659760, -10.2402420044, - -15.8367710114, -11.3119792938, -15.7354402542, -12.3635606766, - -15.6255445480, -13.3933238983, -15.5073804855, -14.3997106552, - -15.3812685013, -15.3812685013, -15.2475576401, -16.3366680145, - -15.1066188812, -17.2647075653, -14.9588537216, -18.1643218994, - -14.8046855927, -19.0345954895, -14.6445646286, -19.8747653961, - -14.4789676666, -20.6842403412, -14.3083972931, -21.4625949860, - -14.1333789825, -22.2095966339, -13.9544687271, -22.9251995087, - -13.7722444534, -23.6095619202, -13.5873117447, -24.2630558014, - -13.4003000259, -24.8862724304, -13.2118673325, -25.4800300598, - -13.0226945877, -26.0453891754, -12.8334894180, -26.5836582184, - -12.6449871063, -27.0964012146, -11.8210153580, 27.2792663574, - -11.9992265701, 26.7675056458, -12.1779823303, 26.2294998169, - -12.3566007614, 25.6637096405, -12.5344247818, 25.0688495636, - -12.7108211517, 24.4438858032, -12.8851795197, 23.7880249023, - -13.0569162369, 23.1006965637, -13.2254667282, 22.3815593719, - -13.3902959824, 21.6304779053, -13.5508880615, 20.8475208282, - -13.7067537308, 20.0329475403, -13.8574275970, 19.1872081757, - -14.0024662018, 18.3109169006, -14.1414518356, 17.4048633575, - -14.2739896774, 16.4699878693, -14.3997106552, 15.5073804855, - -14.5182666779, 14.5182666779, -14.6293354034, 13.5040016174, - -14.7326173782, 12.4660615921, -14.8278398514, 11.4060306549, - -14.9147500992, 10.3255958557, -14.9931211472, 9.2265357971, - -15.0627498627, 8.1107110977, -15.1234579086, 6.9800572395, - -15.1750888824, 5.8365726471, -15.2175111771, 4.6823110580, - -15.2506179810, 3.5193734169, -15.2743263245, 2.3498964310, - -15.2885742188, 1.1760442257, -15.2933282852, -0.0000000000, - -15.2885742188, -1.1760442257, -15.2743263245, -2.3498964310, - -15.2506179810, -3.5193734169, -15.2175111771, -4.6823110580, - -15.1750888824, -5.8365726471, -15.1234579086, -6.9800572395, - -15.0627498627, -8.1107110977, -14.9931211472, -9.2265357971, - -14.9147500992, -10.3255958557, -14.8278398514, -11.4060306549, - -14.7326173782, -12.4660615921, -14.6293354034, -13.5040016174, - -14.5182666779, -14.5182666779, -14.3997106552, -15.5073804855, - -14.2739896774, -16.4699878693, -14.1414518356, -17.4048633575, - -14.0024662018, -18.3109169006, -13.8574275970, -19.1872081757, - -13.7067537308, -20.0329475403, -13.5508880615, -20.8475208282, - -13.3902959824, -21.6304779053, -13.2254667282, -22.3815593719, - -13.0569162369, -23.1006965637, -12.8851795197, -23.7880249023, - -12.7108211517, -24.4438858032, -12.5344247818, -25.0688495636, - -12.3566007614, -25.6637096405, -12.1779823303, -26.2294998169, - -11.9992265701, -26.7675056458, -11.8210153580, -27.2792663574, - -10.9806299210, 27.4515743256, -11.1478443146, 26.9406242371, - -11.3154697418, 26.4027633667, -11.4828767776, 25.8364715576, - -11.6494579315, 25.2404918671, -11.8146295547, 24.6138114929, - -11.9778289795, 23.9556579590, -12.1385145187, 23.2654857635, - -12.2961692810, 22.5429763794, -12.4502954483, 21.7880172729, - -12.6004190445, 21.0006980896, -12.7460880280, 20.1813049316, - -12.8868713379, 19.3303070068, -13.0223627090, 18.4483470917, - -13.1521739960, 17.5362319946, -13.2759418488, 16.5949268341, - -13.3933238983, 15.6255445480, -13.5040016174, 14.6293354034, - -13.6076755524, 13.6076755524, -13.7040710449, 12.5620651245, - -13.7929334641, 11.4941110611, -13.8740310669, 10.4055233002, - -13.9471549988, 9.2981033325, -14.0121173859, 8.1737356186, - -14.0687532425, 7.0343766212, -14.1169185638, 5.8820490837, - -14.1564912796, 4.7188305855, -14.1873731613, 3.5468432903, - -14.2094869614, 2.3682479858, -14.2227773666, 1.1852314472, - -14.2272119522, -0.0000000000, -14.2227773666, -1.1852314472, - -14.2094869614, -2.3682479858, -14.1873731613, -3.5468432903, - -14.1564912796, -4.7188305855, -14.1169185638, -5.8820490837, - -14.0687532425, -7.0343766212, -14.0121173859, -8.1737356186, - -13.9471549988, -9.2981033325, -13.8740310669, -10.4055233002, - -13.7929334641, -11.4941110611, -13.7040710449, -12.5620651245, - -13.6076755524, -13.6076755524, -13.5040016174, -14.6293354034, - -13.3933238983, -15.6255445480, -13.2759418488, -16.5949268341, - -13.1521739960, -17.5362319946, -13.0223627090, -18.4483470917, - -12.8868713379, -19.3303070068, -12.7460880280, -20.1813049316, - -12.6004190445, -21.0006980896, -12.4502954483, -21.7880172729, - -12.2961692810, -22.5429763794, -12.1385145187, -23.2654857635, - -11.9778289795, -23.9556579590, -11.8146295547, -24.6138114929, - -11.6494579315, -25.2404918671, -11.4828767776, -25.8364715576, - -11.3154697418, -26.4027633667, -11.1478443146, -26.9406242371, - -10.9806299210, -27.4515743256, -10.1246328354, 27.6126346588, - -10.2802000046, 27.1023464203, -10.4360666275, 26.5645332336, - -10.5916547775, 25.9976978302, -10.7464084625, 25.4006023407, - -10.8997936249, 24.7722568512, -11.0512914658, 24.1119098663, - -11.2004089355, 23.4190368652, -11.3466701508, 22.6933403015, - -11.4896192551, 21.9347286224, -11.6288223267, 21.1433124542, - -11.7638635635, 20.3194007874, -11.8943500519, 19.4634819031, - -12.0199069977, 18.5762195587, -12.1401796341, 17.6584434509, - -12.2548351288, 16.7111396790, -12.3635606766, 15.7354402542, - -12.4660615921, 14.7326173782, -12.5620651245, 13.7040710449, - -12.6513185501, 12.6513185501, -12.7335901260, 11.5759906769, - -12.8086662292, 10.4798183441, -12.8763561249, 9.3646221161, - -12.9364862442, 8.2323093414, -12.9889059067, 7.0848579407, - -13.0334835052, 5.9243106842, -13.0701084137, 4.7527666092, - -13.0986881256, 3.5723693371, -13.1191530228, 2.3853003979, - -13.1314516068, 1.1937683821, -13.1355552673, -0.0000000000, - -13.1314516068, -1.1937683821, -13.1191530228, -2.3853003979, - -13.0986881256, -3.5723693371, -13.0701084137, -4.7527666092, - -13.0334835052, -5.9243106842, -12.9889059067, -7.0848579407, - -12.9364862442, -8.2323093414, -12.8763561249, -9.3646221161, - -12.8086662292, -10.4798183441, -12.7335901260, -11.5759906769, - -12.6513185501, -12.6513185501, -12.5620651245, -13.7040710449, - -12.4660615921, -14.7326173782, -12.3635606766, -15.7354402542, - -12.2548351288, -16.7111396790, -12.1401796341, -17.6584434509, - -12.0199069977, -18.5762195587, -11.8943500519, -19.4634819031, - -11.7638635635, -20.3194007874, -11.6288223267, -21.1433124542, - -11.4896192551, -21.9347286224, -11.3466701508, -22.6933403015, - -11.2004089355, -23.4190368652, -11.0512914658, -24.1119098663, - -10.8997936249, -24.7722568512, -10.7464084625, -25.4006023407, - -10.5916547775, -25.9976978302, -10.4360666275, -26.5645332336, - -10.2802000046, -27.1023464203, -10.1246328354, -27.6126346588, - -9.2539377213, 27.7618141174, -9.3972616196, 27.2520580292, - -9.5407915115, 26.7142162323, -9.6840057373, 26.1468143463, - -9.8263969421, 25.5486316681, -9.9674777985, 24.9186954498, - -10.1067810059, 24.2562732697, -10.2438545227, 23.5608654022, - -10.3782663345, 22.8321857452, -10.5096044540, 22.0701675415, - -10.6374711990, 21.2749423981, -10.7614917755, 20.4468326569, - -10.8813056946, 19.5863494873, -10.9965744019, 18.6941757202, - -11.1069755554, 17.7711601257, -11.2122049332, 16.8183078766, - -11.3119792938, 15.8367710114, -11.4060306549, 14.8278398514, - -11.4941110611, 13.7929334641, -11.5759906769, 12.7335901260, - -11.6514587402, 11.6514587402, -11.7203216553, 10.5482892990, - -11.7824039459, 9.4259233475, -11.8375511169, 8.2862854004, - -11.8856229782, 7.1313738823, -11.9265022278, 5.9632511139, - -11.9600868225, 4.7840347290, -11.9862937927, 3.5958881378, - -12.0050592422, 2.4010119438, -12.0163364410, 1.2016336918, - -12.0200986862, -0.0000000000, -12.0163364410, -1.2016336918, - -12.0050592422, -2.4010119438, -11.9862937927, -3.5958881378, - -11.9600868225, -4.7840347290, -11.9265022278, -5.9632511139, - -11.8856229782, -7.1313738823, -11.8375511169, -8.2862854004, - -11.7824039459, -9.4259233475, -11.7203216553, -10.5482892990, - -11.6514587402, -11.6514587402, -11.5759906769, -12.7335901260, - -11.4941110611, -13.7929334641, -11.4060306549, -14.8278398514, - -11.3119792938, -15.8367710114, -11.2122049332, -16.8183078766, - -11.1069755554, -17.7711601257, -10.9965744019, -18.6941757202, - -10.8813056946, -19.5863494873, -10.7614917755, -20.4468326569, - -10.6374711990, -21.2749423981, -10.5096044540, -22.0701675415, - -10.3782663345, -22.8321857452, -10.2438545227, -23.5608654022, - -10.1067810059, -24.2562732697, -9.9674777985, -24.9186954498, - -9.8263969421, -25.5486316681, -9.6840057373, -26.1468143463, - -9.5407915115, -26.7142162323, -9.3972616196, -27.2520580292, - -9.2539377213, -27.7618141174, -8.3695592880, 27.8985328674, - -8.5000963211, 27.3892002106, -8.6307678223, 26.8512763977, - -8.7611007690, 26.2833023071, -8.8906412125, 25.6840744019, - -9.0189504623, 25.0526409149, -9.1456069946, 24.3882865906, - -9.2702045441, 23.6905231476, -9.3923549652, 22.9590892792, - -9.5116853714, 22.1939334869, -9.6278400421, 21.3952007294, - -9.7404804230, 20.5632362366, -9.8492822647, 19.6985645294, - -9.9539413452, 18.8018894196, -10.0541667938, 17.8740749359, - -10.1496858597, 16.9161434174, -10.2402420044, 15.9292659760, - -10.3255958557, 14.9147500992, -10.4055233002, 13.8740310669, - -10.4798183441, 12.8086662292, -10.5482892990, 11.7203216553, - -10.6107635498, 10.6107635498, -10.6670837402, 9.4818515778, - -10.7171087265, 8.3355283737, -10.7607145309, 7.1738095284, - -10.7977933884, 5.9987745285, -10.8282556534, 4.8125581741, - -10.8520250320, 3.6173417568, -10.8690452576, 2.4153432846, - -10.8792734146, 1.2088081837, -10.8826856613, -0.0000000000, - -10.8792734146, -1.2088081837, -10.8690452576, -2.4153432846, - -10.8520250320, -3.6173417568, -10.8282556534, -4.8125581741, - -10.7977933884, -5.9987745285, -10.7607145309, -7.1738095284, - -10.7171087265, -8.3355283737, -10.6670837402, -9.4818515778, - -10.6107635498, -10.6107635498, -10.5482892990, -11.7203216553, - -10.4798183441, -12.8086662292, -10.4055233002, -13.8740310669, - -10.3255958557, -14.9147500992, -10.2402420044, -15.9292659760, - -10.1496858597, -16.9161434174, -10.0541667938, -17.8740749359, - -9.9539413452, -18.8018894196, -9.8492822647, -19.6985645294, - -9.7404804230, -20.5632362366, -9.6278400421, -21.3952007294, - -9.5116853714, -22.1939334869, -9.3923549652, -22.9590892792, - -9.2702045441, -23.6905231476, -9.1456069946, -24.3882865906, - -9.0189504623, -25.0526409149, -8.8906412125, -25.6840744019, - -8.7611007690, -26.2833023071, -8.6307678223, -26.8512763977, - -8.5000963211, -27.3892002106, -8.3695592880, -27.8985328674, - -7.4726042747, 28.0222644806, -7.5898661613, 27.5132656097, - -7.7072062492, 26.9752216339, -7.8242039680, 26.4066886902, - -7.9404559135, 25.8064804077, -8.0555715561, 25.1736602783, - -8.1691761017, 24.5075283051, -8.2809085846, 23.8076114655, - -8.3904247284, 23.0736675262, -8.4973917007, 22.3056526184, - -8.6014947891, 21.5037364960, -8.7024316788, 20.6682758331, - -8.7999162674, 19.7998123169, -8.8936758041, 18.8990612030, - -8.9834527969, 17.9669055939, -9.0690040588, 17.0043830872, - -9.1501035690, 16.0126800537, -9.2265357971, 14.9931211472, - -9.2981033325, 13.9471549988, -9.3646221161, 12.8763561249, - -9.4259233475, 11.7824039459, -9.4818515778, 10.6670837402, - -9.5322685242, 9.5322685242, -9.5770473480, 8.3799161911, - -9.6160793304, 7.2120594978, -9.6492681503, 6.0307927132, - -9.6765327454, 4.8382663727, -9.6978073120, 3.6366777420, - -9.7130403519, 2.4282600880, -9.7221946716, 1.2152743340, - -9.7252483368, -0.0000000000, -9.7221946716, -1.2152743340, - -9.7130403519, -2.4282600880, -9.6978073120, -3.6366777420, - -9.6765327454, -4.8382663727, -9.6492681503, -6.0307927132, - -9.6160793304, -7.2120594978, -9.5770473480, -8.3799161911, - -9.5322685242, -9.5322685242, -9.4818515778, -10.6670837402, - -9.4259233475, -11.7824039459, -9.3646221161, -12.8763561249, - -9.2981033325, -13.9471549988, -9.2265357971, -14.9931211472, - -9.1501035690, -16.0126800537, -9.0690040588, -17.0043830872, - -8.9834527969, -17.9669055939, -8.8936758041, -18.8990612030, - -8.7999162674, -19.7998123169, -8.7024316788, -20.6682758331, - -8.6014947891, -21.5037364960, -8.4973917007, -22.3056526184, - -8.3904247284, -23.0736675262, -8.2809085846, -23.8076114655, - -8.1691761017, -24.5075283051, -8.0555715561, -25.1736602783, - -7.9404559135, -25.8064804077, -7.8242039680, -26.4066886902, - -7.7072062492, -26.9752216339, -7.5898661613, -27.5132656097, - -7.4726042747, -28.0222644806, -6.5642604828, 28.1325454712, - -6.6678142548, 27.6238021851, -6.7714037895, 27.0856151581, - -6.8746614456, 26.5165519714, -6.9772343636, 25.9154434204, - -7.0787811279, 25.2813606262, -7.1789731979, 24.6136226654, - -7.2774958611, 23.9117717743, -7.3740458488, 23.1755733490, - -7.4683341980, 22.4050025940, -7.5600843430, 21.6002407074, - -7.6490316391, 20.7616577148, -7.7349257469, 19.8898086548, - -7.8175282478, 18.9854259491, -7.8966135979, 18.0494022369, - -7.9719705582, 17.0827941895, -8.0433988571, 16.0867977142, - -8.1107110977, 15.0627498627, -8.1737356186, 14.0121173859, - -8.2323093414, 12.9364862442, -8.2862854004, 11.8375511169, - -8.3355283737, 10.7171087265, -8.3799161911, 9.5770473480, - -8.4193401337, 8.4193401337, -8.4537019730, 7.2460298538, - -8.4829187393, 6.0592274666, -8.5069198608, 4.8610973358, - -8.5256481171, 3.6538491249, -8.5390567780, 2.4397306442, - -8.5471153259, 1.2210165262, -8.5498037338, -0.0000000000, - -8.5471153259, -1.2210165262, -8.5390567780, -2.4397306442, - -8.5256481171, -3.6538491249, -8.5069198608, -4.8610973358, - -8.4829187393, -6.0592274666, -8.4537019730, -7.2460298538, - -8.4193401337, -8.4193401337, -8.3799161911, -9.5770473480, - -8.3355283737, -10.7171087265, -8.2862854004, -11.8375511169, - -8.2323093414, -12.9364862442, -8.1737356186, -14.0121173859, - -8.1107110977, -15.0627498627, -8.0433988571, -16.0867977142, - -7.9719705582, -17.0827941895, -7.8966135979, -18.0494022369, - -7.8175282478, -18.9854259491, -7.7349257469, -19.8898086548, - -7.6490316391, -20.7616577148, -7.5600843430, -21.6002407074, - -7.4683341980, -22.4050025940, -7.3740458488, -23.1755733490, - -7.2774958611, -23.9117717743, -7.1789731979, -24.6136226654, - -7.0787811279, -25.2813606262, -6.9772343636, -25.9154434204, - -6.8746614456, -26.5165519714, -6.7714037895, -27.0856151581, - -6.6678142548, -27.6238021851, -6.5642604828, -28.1325454712, - -5.6457915306, 28.2289581299, -5.7352571487, 27.7204093933, - -5.8247289658, 27.1820678711, -5.9138932228, 26.6125202179, - -6.0024461746, 26.0106010437, -6.0900959969, 25.3753986359, - -6.1765604019, 24.7062416077, -6.2615699768, 24.0026855469, - -6.3448653221, 23.2645053864, -6.4261975288, 22.4916915894, - -6.5053300858, 21.6844348907, -6.5820369720, 20.8431167603, - -6.6561026573, 19.9683074951, -6.7273230553, 19.0607490540, - -6.7955055237, 18.1213474274, -6.8604674339, 17.1511688232, - -6.9220380783, 16.1514225006, -6.9800572395, 15.1234579086, - -7.0343766212, 14.0687532425, -7.0848579407, 12.9889059067, - -7.1313738823, 11.8856229782, -7.1738095284, 10.7607145309, - -7.2120594978, 9.6160793304, -7.2460298538, 8.4537019730, - -7.2756385803, 7.2756385803, -7.3008131981, 6.0840110779, - -7.3214931488, 4.8809957504, -7.3376293182, 3.6688146591, - -7.3491826057, 2.4497275352, -7.3561258316, 1.2260209322, - -7.3584418297, -0.0000000000, -7.3561258316, -1.2260209322, - -7.3491826057, -2.4497275352, -7.3376293182, -3.6688146591, - -7.3214931488, -4.8809957504, -7.3008131981, -6.0840110779, - -7.2756385803, -7.2756385803, -7.2460298538, -8.4537019730, - -7.2120594978, -9.6160793304, -7.1738095284, -10.7607145309, - -7.1313738823, -11.8856229782, -7.0848579407, -12.9889059067, - -7.0343766212, -14.0687532425, -6.9800572395, -15.1234579086, - -6.9220380783, -16.1514225006, -6.8604674339, -17.1511688232, - -6.7955055237, -18.1213474274, -6.7273230553, -19.0607490540, - -6.6561026573, -19.9683074951, -6.5820369720, -20.8431167603, - -6.5053300858, -21.6844348907, -6.4261975288, -22.4916915894, - -6.3448653221, -23.2645053864, -6.2615699768, -24.0026855469, - -6.1765604019, -24.7062416077, -6.0900959969, -25.3753986359, - -6.0024461746, -26.0106010437, -5.9138932228, -26.6125202179, - -5.8247289658, -27.1820678711, -5.7352571487, -27.7204093933, - -5.6457915306, -28.2289581299, -4.7185239792, 28.3111438751, - -4.7935757637, 27.8027400970, -4.8686161041, 27.2642498016, - -4.9433832169, 26.6942691803, -5.0176239014, 26.0916442871, - -5.0910949707, 25.4554748535, -5.1635618210, 24.7850971222, - -5.2347993851, 24.0800762177, -5.3045911789, 23.3402004242, - -5.3727307320, 22.5654678345, -5.4390201569, 21.7560806274, - -5.5032711029, 20.9124298096, -5.5653042793, 20.0350952148, - -5.6249494553, 19.1248283386, -5.6820459366, 18.1825466156, - -5.7364420891, 17.2093257904, -5.7879953384, 16.2063865662, - -5.8365726471, 15.1750888824, -5.8820490837, 14.1169185638, - -5.9243106842, 13.0334835052, -5.9632511139, 11.9265022278, - -5.9987745285, 10.7977933884, -6.0307927132, 9.6492681503, - -6.0592274666, 8.4829187393, -6.0840110779, 7.3008131981, - -6.1050825119, 6.1050825119, -6.1223917007, 4.8979134560, - -6.1358976364, 3.6815385818, -6.1455674171, 2.4582269192, - -6.1513786316, 1.2302757502, -6.1533174515, -0.0000000000, - -6.1513786316, -1.2302757502, -6.1455674171, -2.4582269192, - -6.1358976364, -3.6815385818, -6.1223917007, -4.8979134560, - -6.1050825119, -6.1050825119, -6.0840110779, -7.3008131981, - -6.0592274666, -8.4829187393, -6.0307927132, -9.6492681503, - -5.9987745285, -10.7977933884, -5.9632511139, -11.9265022278, - -5.9243106842, -13.0334835052, -5.8820490837, -14.1169185638, - -5.8365726471, -15.1750888824, -5.7879953384, -16.2063865662, - -5.7364420891, -17.2093257904, -5.6820459366, -18.1825466156, - -5.6249494553, -19.1248283386, -5.5653042793, -20.0350952148, - -5.5032711029, -20.9124298096, -5.4390201569, -21.7560806274, - -5.3727307320, -22.5654678345, -5.3045911789, -23.3402004242, - -5.2347993851, -24.0800762177, -5.1635618210, -24.7850971222, - -5.0910949707, -25.4554748535, -5.0176239014, -26.0916442871, - -4.9433832169, -26.6942691803, -4.8686161041, -27.2642498016, - -4.7935757637, -27.8027400970, -4.7185239792, -28.3111438751, - -3.7838401794, 28.3788013458, -3.8442070484, 27.8705005646, - -3.9045536518, 27.3318767548, -3.9646706581, 26.7615280151, - -4.0243558884, 26.1583118439, -4.0834140778, 25.5213375092, - -4.1416578293, 24.8499469757, -4.1989068985, 24.1437149048, - -4.2549886703, 23.4024372101, -4.3097372055, 22.6261196136, - -4.3629951477, 21.8149738312, -4.4146108627, 20.9694004059, - -4.4644412994, 20.0899868011, -4.5123505592, 19.1774902344, - -4.5582098961, 18.2328395844, -4.6018981934, 17.2571182251, - -4.6433005333, 16.2515525818, -4.6823110580, 15.2175111771, - -4.7188305855, 14.1564912796, -4.7527666092, 13.0701084137, - -4.7840347290, 11.9600868225, -4.8125581741, 10.8282556534, - -4.8382663727, 9.6765327454, -4.8610973358, 8.5069198608, - -4.8809957504, 7.3214931488, -4.8979134560, 6.1223917007, - -4.9118108749, 4.9118108749, -4.9226536751, 3.6919903755, - -4.9304175377, 2.4652087688, -4.9350829124, 1.2337707281, - -4.9366393089, -0.0000000000, -4.9350829124, -1.2337707281, - -4.9304175377, -2.4652087688, -4.9226536751, -3.6919903755, - -4.9118108749, -4.9118108749, -4.8979134560, -6.1223917007, - -4.8809957504, -7.3214931488, -4.8610973358, -8.5069198608, - -4.8382663727, -9.6765327454, -4.8125581741, -10.8282556534, - -4.7840347290, -11.9600868225, -4.7527666092, -13.0701084137, - -4.7188305855, -14.1564912796, -4.6823110580, -15.2175111771, - -4.6433005333, -16.2515525818, -4.6018981934, -17.2571182251, - -4.5582098961, -18.2328395844, -4.5123505592, -19.1774902344, - -4.4644412994, -20.0899868011, -4.4146108627, -20.9694004059, - -4.3629951477, -21.8149738312, -4.3097372055, -22.6261196136, - -4.2549886703, -23.4024372101, -4.1989068985, -24.1437149048, - -4.1416578293, -24.8499469757, -4.0834140778, -25.5213375092, - -4.0243558884, -26.1583118439, -3.9646706581, -26.7615280151, - -3.9045536518, -27.3318767548, -3.8442070484, -27.8705005646, - -3.7838401794, -28.3788013458, -2.8431680202, 28.4316806793, - -2.8886330128, 27.9234523773, -2.9340765476, 27.3847141266, - -2.9793412685, 26.8140716553, -3.0242755413, 26.2103881836, - -3.0687332153, 25.5727767944, -3.1125738621, 24.9005908966, - -3.1556618214, 24.1934070587, -3.1978678703, 23.4510307312, - -3.2390677929, 22.6734752655, -3.2791430950, 21.8609542847, - -3.3179802895, 21.0138759613, -3.3554723263, 20.1328353882, - -3.3915169239, 19.2185974121, -3.4260177612, 18.2720947266, - -3.4588835239, 17.2944164276, -3.4900286198, 16.2868003845, - -3.5193734169, 15.2506179810, -3.5468432903, 14.1873731613, - -3.5723693371, 13.0986881256, -3.5958881378, 11.9862937927, - -3.6173417568, 10.8520250320, -3.6366777420, 9.6978073120, - -3.6538491249, 8.5256481171, -3.6688146591, 7.3376293182, - -3.6815385818, 6.1358976364, -3.6919903755, 4.9226536751, - -3.7001452446, 3.7001452446, -3.7059841156, 2.4706559181, - -3.7094929218, 1.2364976406, -3.7106633186, -0.0000000000, - -3.7094929218, -1.2364976406, -3.7059841156, -2.4706559181, - -3.7001452446, -3.7001452446, -3.6919903755, -4.9226536751, - -3.6815385818, -6.1358976364, -3.6688146591, -7.3376293182, - -3.6538491249, -8.5256481171, -3.6366777420, -9.6978073120, - -3.6173417568, -10.8520250320, -3.5958881378, -11.9862937927, - -3.5723693371, -13.0986881256, -3.5468432903, -14.1873731613, - -3.5193734169, -15.2506179810, -3.4900286198, -16.2868003845, - -3.4588835239, -17.2944164276, -3.4260177612, -18.2720947266, - -3.3915169239, -19.2185974121, -3.3554723263, -20.1328353882, - -3.3179802895, -21.0138759613, -3.2791430950, -21.8609542847, - -3.2390677929, -22.6734752655, -3.1978678703, -23.4510307312, - -3.1556618214, -24.1934070587, -3.1125738621, -24.9005908966, - -3.0687332153, -25.5727767944, -3.0242755413, -26.2103881836, - -2.9793412685, -26.8140716553, -2.9340765476, -27.3847141266, - -2.8886330128, -27.9234523773, -2.8431680202, -28.4316806793, - -1.8979727030, 28.4695892334, -1.9283730984, 27.9614086151, - -1.9587560892, 27.4225845337, -1.9890167713, 26.8517265320, - -2.0190541744, 26.2477054596, -2.0487709045, 25.6096363068, - -2.0780727863, 24.9368743896, -2.1068701744, 24.2290077209, - -2.1350765228, 23.4858417511, -2.1626091003, 22.7073955536, - -2.1893887520, 21.8938865662, -2.2153401375, 21.0457305908, - -2.2403914928, 20.1635227203, -2.2644748688, 19.2480354309, - -2.2875258923, 18.3002071381, -2.3094837666, 17.3211288452, - -2.3302917480, 16.3120422363, -2.3498964310, 15.2743263245, - -2.3682479858, 14.2094869614, -2.3853003979, 13.1191530228, - -2.4010119438, 12.0050592422, -2.4153432846, 10.8690452576, - -2.4282600880, 9.7130403519, -2.4397306442, 8.5390567780, - -2.4497275352, 7.3491826057, -2.4582269192, 6.1455674171, - -2.4652087688, 4.9304175377, -2.4706559181, 3.7059841156, - -2.4745562077, 2.4745562077, -2.4768998623, 1.2384499311, - -2.4776818752, -0.0000000000, -2.4768998623, -1.2384499311, - -2.4745562077, -2.4745562077, -2.4706559181, -3.7059841156, - -2.4652087688, -4.9304175377, -2.4582269192, -6.1455674171, - -2.4497275352, -7.3491826057, -2.4397306442, -8.5390567780, - -2.4282600880, -9.7130403519, -2.4153432846, -10.8690452576, - -2.4010119438, -12.0050592422, -2.3853003979, -13.1191530228, - -2.3682479858, -14.2094869614, -2.3498964310, -15.2743263245, - -2.3302917480, -16.3120422363, -2.3094837666, -17.3211288452, - -2.2875258923, -18.3002071381, -2.2644748688, -19.2480354309, - -2.2403914928, -20.1635227203, -2.2153401375, -21.0457305908, - -2.1893887520, -21.8938865662, -2.1626091003, -22.7073955536, - -2.1350765228, -23.4858417511, -2.1068701744, -24.2290077209, - -2.0780727863, -24.9368743896, -2.0487709045, -25.6096363068, - -2.0190541744, -26.2477054596, -1.9890167713, -26.8517265320, - -1.9587560892, -27.4225845337, -1.9283730984, -27.9614086151, - -1.8979727030, -28.4695892334, -0.9497463703, 28.4923896790, - -0.9649736881, 27.9842357635, -0.9801913500, 27.4453582764, - -0.9953470230, 26.8743686676, -1.0103900433, 26.2701416016, - -1.0252718925, 25.6317958832, -1.0399453640, 24.9586887360, - -1.0543657541, 24.2504119873, -1.0684895515, 23.5067691803, - -1.0822755098, 22.7277870178, -1.0956841707, 21.9136848450, - -1.1086778641, 21.0648784637, -1.1212204695, 20.1819686890, - -1.1332782507, 19.2657318115, -1.1448190212, 18.3171043396, - -1.1558121443, 17.3371829987, -1.1662294865, 16.3272132874, - -1.1760442257, 15.2885742188, -1.1852314472, 14.2227773666, - -1.1937683821, 13.1314516068, -1.2016336918, 12.0163364410, - -1.2088081837, 10.8792734146, -1.2152743340, 9.7221946716, - -1.2210165262, 8.5471153259, -1.2260209322, 7.3561258316, - -1.2302757502, 6.1513786316, -1.2337707281, 4.9350829124, - -1.2364976406, 3.7094929218, -1.2384499311, 2.4768998623, - -1.2396231890, 1.2396231890, -1.2400146723, -0.0000000000, - -1.2396231890, -1.2396231890, -1.2384499311, -2.4768998623, - -1.2364976406, -3.7094929218, -1.2337707281, -4.9350829124, - -1.2302757502, -6.1513786316, -1.2260209322, -7.3561258316, - -1.2210165262, -8.5471153259, -1.2152743340, -9.7221946716, - -1.2088081837, -10.8792734146, -1.2016336918, -12.0163364410, - -1.1937683821, -13.1314516068, -1.1852314472, -14.2227773666, - -1.1760442257, -15.2885742188, -1.1662294865, -16.3272132874, - -1.1558121443, -17.3371829987, -1.1448190212, -18.3171043396, - -1.1332782507, -19.2657318115, -1.1212204695, -20.1819686890, - -1.1086778641, -21.0648784637, -1.0956841707, -21.9136848450, - -1.0822755098, -22.7277870178, -1.0684895515, -23.5067691803, - -1.0543657541, -24.2504119873, -1.0399453640, -24.9586887360, - -1.0252718925, -25.6317958832, -1.0103900433, -26.2701416016, - -0.9953470230, -26.8743686676, -0.9801913500, -27.4453582764, - -0.9649736881, -27.9842357635, -0.9497463703, -28.4923896790, - 0.0000000000, 28.5000000000, 0.0000000000, 27.9918537140, - 0.0000000000, 27.4529571533, 0.0000000000, 26.8819255829, - 0.0000000000, 26.2776298523, 0.0000000000, 25.6391906738, - 0.0000000000, 24.9659690857, 0.0000000000, 24.2575531006, - 0.0000000000, 23.5137519836, 0.0000000000, 22.7345905304, - 0.0000000000, 21.9202899933, 0.0000000000, 21.0712661743, - 0.0000000000, 20.1881237030, 0.0000000000, 19.2716350555, - 0.0000000000, 18.3227405548, 0.0000000000, 17.3425388336, - 0.0000000000, 16.3322734833, 0.0000000000, 15.2933282852, - 0.0000000000, 14.2272119522, 0.0000000000, 13.1355552673, - 0.0000000000, 12.0200986862, 0.0000000000, 10.8826856613, - 0.0000000000, 9.7252483368, 0.0000000000, 8.5498037338, - 0.0000000000, 7.3584418297, 0.0000000000, 6.1533174515, - 0.0000000000, 4.9366393089, 0.0000000000, 3.7106633186, - 0.0000000000, 2.4776818752, 0.0000000000, 1.2400146723, - 0.0000000000, -0.0000000000, 0.0000000000, -1.2400146723, - 0.0000000000, -2.4776818752, 0.0000000000, -3.7106633186, - 0.0000000000, -4.9366393089, 0.0000000000, -6.1533174515, - 0.0000000000, -7.3584418297, 0.0000000000, -8.5498037338, - 0.0000000000, -9.7252483368, 0.0000000000, -10.8826856613, - 0.0000000000, -12.0200986862, 0.0000000000, -13.1355552673, - 0.0000000000, -14.2272119522, 0.0000000000, -15.2933282852, - 0.0000000000, -16.3322734833, 0.0000000000, -17.3425388336, - 0.0000000000, -18.3227405548, 0.0000000000, -19.2716350555, - 0.0000000000, -20.1881237030, 0.0000000000, -21.0712661743, - 0.0000000000, -21.9202899933, 0.0000000000, -22.7345905304, - 0.0000000000, -23.5137519836, 0.0000000000, -24.2575531006, - 0.0000000000, -24.9659690857, 0.0000000000, -25.6391906738, - 0.0000000000, -26.2776298523, 0.0000000000, -26.8819255829, - 0.0000000000, -27.4529571533, 0.0000000000, -27.9918537140, - 0.0000000000, -28.5000000000, 0.9497463703, 28.4923896790, - 0.9649736881, 27.9842357635, 0.9801913500, 27.4453582764, - 0.9953470230, 26.8743686676, 1.0103900433, 26.2701416016, - 1.0252718925, 25.6317958832, 1.0399453640, 24.9586887360, - 1.0543657541, 24.2504119873, 1.0684895515, 23.5067691803, - 1.0822755098, 22.7277870178, 1.0956841707, 21.9136848450, - 1.1086778641, 21.0648784637, 1.1212204695, 20.1819686890, - 1.1332782507, 19.2657318115, 1.1448190212, 18.3171043396, - 1.1558121443, 17.3371829987, 1.1662294865, 16.3272132874, - 1.1760442257, 15.2885742188, 1.1852314472, 14.2227773666, - 1.1937683821, 13.1314516068, 1.2016336918, 12.0163364410, - 1.2088081837, 10.8792734146, 1.2152743340, 9.7221946716, - 1.2210165262, 8.5471153259, 1.2260209322, 7.3561258316, - 1.2302757502, 6.1513786316, 1.2337707281, 4.9350829124, - 1.2364976406, 3.7094929218, 1.2384499311, 2.4768998623, - 1.2396231890, 1.2396231890, 1.2400146723, -0.0000000000, - 1.2396231890, -1.2396231890, 1.2384499311, -2.4768998623, - 1.2364976406, -3.7094929218, 1.2337707281, -4.9350829124, - 1.2302757502, -6.1513786316, 1.2260209322, -7.3561258316, - 1.2210165262, -8.5471153259, 1.2152743340, -9.7221946716, - 1.2088081837, -10.8792734146, 1.2016336918, -12.0163364410, - 1.1937683821, -13.1314516068, 1.1852314472, -14.2227773666, - 1.1760442257, -15.2885742188, 1.1662294865, -16.3272132874, - 1.1558121443, -17.3371829987, 1.1448190212, -18.3171043396, - 1.1332782507, -19.2657318115, 1.1212204695, -20.1819686890, - 1.1086778641, -21.0648784637, 1.0956841707, -21.9136848450, - 1.0822755098, -22.7277870178, 1.0684895515, -23.5067691803, - 1.0543657541, -24.2504119873, 1.0399453640, -24.9586887360, - 1.0252718925, -25.6317958832, 1.0103900433, -26.2701416016, - 0.9953470230, -26.8743686676, 0.9801913500, -27.4453582764, - 0.9649736881, -27.9842357635, 0.9497463703, -28.4923896790, - 1.8979727030, 28.4695892334, 1.9283730984, 27.9614086151, - 1.9587560892, 27.4225845337, 1.9890167713, 26.8517265320, - 2.0190541744, 26.2477054596, 2.0487709045, 25.6096363068, - 2.0780727863, 24.9368743896, 2.1068701744, 24.2290077209, - 2.1350765228, 23.4858417511, 2.1626091003, 22.7073955536, - 2.1893887520, 21.8938865662, 2.2153401375, 21.0457305908, - 2.2403914928, 20.1635227203, 2.2644748688, 19.2480354309, - 2.2875258923, 18.3002071381, 2.3094837666, 17.3211288452, - 2.3302917480, 16.3120422363, 2.3498964310, 15.2743263245, - 2.3682479858, 14.2094869614, 2.3853003979, 13.1191530228, - 2.4010119438, 12.0050592422, 2.4153432846, 10.8690452576, - 2.4282600880, 9.7130403519, 2.4397306442, 8.5390567780, - 2.4497275352, 7.3491826057, 2.4582269192, 6.1455674171, - 2.4652087688, 4.9304175377, 2.4706559181, 3.7059841156, - 2.4745562077, 2.4745562077, 2.4768998623, 1.2384499311, - 2.4776818752, -0.0000000000, 2.4768998623, -1.2384499311, - 2.4745562077, -2.4745562077, 2.4706559181, -3.7059841156, - 2.4652087688, -4.9304175377, 2.4582269192, -6.1455674171, - 2.4497275352, -7.3491826057, 2.4397306442, -8.5390567780, - 2.4282600880, -9.7130403519, 2.4153432846, -10.8690452576, - 2.4010119438, -12.0050592422, 2.3853003979, -13.1191530228, - 2.3682479858, -14.2094869614, 2.3498964310, -15.2743263245, - 2.3302917480, -16.3120422363, 2.3094837666, -17.3211288452, - 2.2875258923, -18.3002071381, 2.2644748688, -19.2480354309, - 2.2403914928, -20.1635227203, 2.2153401375, -21.0457305908, - 2.1893887520, -21.8938865662, 2.1626091003, -22.7073955536, - 2.1350765228, -23.4858417511, 2.1068701744, -24.2290077209, - 2.0780727863, -24.9368743896, 2.0487709045, -25.6096363068, - 2.0190541744, -26.2477054596, 1.9890167713, -26.8517265320, - 1.9587560892, -27.4225845337, 1.9283730984, -27.9614086151, - 1.8979727030, -28.4695892334, 2.8431680202, 28.4316806793, - 2.8886330128, 27.9234523773, 2.9340765476, 27.3847141266, - 2.9793412685, 26.8140716553, 3.0242755413, 26.2103881836, - 3.0687332153, 25.5727767944, 3.1125738621, 24.9005908966, - 3.1556618214, 24.1934070587, 3.1978678703, 23.4510307312, - 3.2390677929, 22.6734752655, 3.2791430950, 21.8609542847, - 3.3179802895, 21.0138759613, 3.3554723263, 20.1328353882, - 3.3915169239, 19.2185974121, 3.4260177612, 18.2720947266, - 3.4588835239, 17.2944164276, 3.4900286198, 16.2868003845, - 3.5193734169, 15.2506179810, 3.5468432903, 14.1873731613, - 3.5723693371, 13.0986881256, 3.5958881378, 11.9862937927, - 3.6173417568, 10.8520250320, 3.6366777420, 9.6978073120, - 3.6538491249, 8.5256481171, 3.6688146591, 7.3376293182, - 3.6815385818, 6.1358976364, 3.6919903755, 4.9226536751, - 3.7001452446, 3.7001452446, 3.7059841156, 2.4706559181, - 3.7094929218, 1.2364976406, 3.7106633186, -0.0000000000, - 3.7094929218, -1.2364976406, 3.7059841156, -2.4706559181, - 3.7001452446, -3.7001452446, 3.6919903755, -4.9226536751, - 3.6815385818, -6.1358976364, 3.6688146591, -7.3376293182, - 3.6538491249, -8.5256481171, 3.6366777420, -9.6978073120, - 3.6173417568, -10.8520250320, 3.5958881378, -11.9862937927, - 3.5723693371, -13.0986881256, 3.5468432903, -14.1873731613, - 3.5193734169, -15.2506179810, 3.4900286198, -16.2868003845, - 3.4588835239, -17.2944164276, 3.4260177612, -18.2720947266, - 3.3915169239, -19.2185974121, 3.3554723263, -20.1328353882, - 3.3179802895, -21.0138759613, 3.2791430950, -21.8609542847, - 3.2390677929, -22.6734752655, 3.1978678703, -23.4510307312, - 3.1556618214, -24.1934070587, 3.1125738621, -24.9005908966, - 3.0687332153, -25.5727767944, 3.0242755413, -26.2103881836, - 2.9793412685, -26.8140716553, 2.9340765476, -27.3847141266, - 2.8886330128, -27.9234523773, 2.8431680202, -28.4316806793, - 3.7838401794, 28.3788013458, 3.8442070484, 27.8705005646, - 3.9045536518, 27.3318767548, 3.9646706581, 26.7615280151, - 4.0243558884, 26.1583118439, 4.0834140778, 25.5213375092, - 4.1416578293, 24.8499469757, 4.1989068985, 24.1437149048, - 4.2549886703, 23.4024372101, 4.3097372055, 22.6261196136, - 4.3629951477, 21.8149738312, 4.4146108627, 20.9694004059, - 4.4644412994, 20.0899868011, 4.5123505592, 19.1774902344, - 4.5582098961, 18.2328395844, 4.6018981934, 17.2571182251, - 4.6433005333, 16.2515525818, 4.6823110580, 15.2175111771, - 4.7188305855, 14.1564912796, 4.7527666092, 13.0701084137, - 4.7840347290, 11.9600868225, 4.8125581741, 10.8282556534, - 4.8382663727, 9.6765327454, 4.8610973358, 8.5069198608, - 4.8809957504, 7.3214931488, 4.8979134560, 6.1223917007, - 4.9118108749, 4.9118108749, 4.9226536751, 3.6919903755, - 4.9304175377, 2.4652087688, 4.9350829124, 1.2337707281, - 4.9366393089, -0.0000000000, 4.9350829124, -1.2337707281, - 4.9304175377, -2.4652087688, 4.9226536751, -3.6919903755, - 4.9118108749, -4.9118108749, 4.8979134560, -6.1223917007, - 4.8809957504, -7.3214931488, 4.8610973358, -8.5069198608, - 4.8382663727, -9.6765327454, 4.8125581741, -10.8282556534, - 4.7840347290, -11.9600868225, 4.7527666092, -13.0701084137, - 4.7188305855, -14.1564912796, 4.6823110580, -15.2175111771, - 4.6433005333, -16.2515525818, 4.6018981934, -17.2571182251, - 4.5582098961, -18.2328395844, 4.5123505592, -19.1774902344, - 4.4644412994, -20.0899868011, 4.4146108627, -20.9694004059, - 4.3629951477, -21.8149738312, 4.3097372055, -22.6261196136, - 4.2549886703, -23.4024372101, 4.1989068985, -24.1437149048, - 4.1416578293, -24.8499469757, 4.0834140778, -25.5213375092, - 4.0243558884, -26.1583118439, 3.9646706581, -26.7615280151, - 3.9045536518, -27.3318767548, 3.8442070484, -27.8705005646, - 3.7838401794, -28.3788013458, 4.7185239792, 28.3111438751, - 4.7935757637, 27.8027400970, 4.8686161041, 27.2642498016, - 4.9433832169, 26.6942691803, 5.0176239014, 26.0916442871, - 5.0910949707, 25.4554748535, 5.1635618210, 24.7850971222, - 5.2347993851, 24.0800762177, 5.3045911789, 23.3402004242, - 5.3727307320, 22.5654678345, 5.4390201569, 21.7560806274, - 5.5032711029, 20.9124298096, 5.5653042793, 20.0350952148, - 5.6249494553, 19.1248283386, 5.6820459366, 18.1825466156, - 5.7364420891, 17.2093257904, 5.7879953384, 16.2063865662, - 5.8365726471, 15.1750888824, 5.8820490837, 14.1169185638, - 5.9243106842, 13.0334835052, 5.9632511139, 11.9265022278, - 5.9987745285, 10.7977933884, 6.0307927132, 9.6492681503, - 6.0592274666, 8.4829187393, 6.0840110779, 7.3008131981, - 6.1050825119, 6.1050825119, 6.1223917007, 4.8979134560, - 6.1358976364, 3.6815385818, 6.1455674171, 2.4582269192, - 6.1513786316, 1.2302757502, 6.1533174515, -0.0000000000, - 6.1513786316, -1.2302757502, 6.1455674171, -2.4582269192, - 6.1358976364, -3.6815385818, 6.1223917007, -4.8979134560, - 6.1050825119, -6.1050825119, 6.0840110779, -7.3008131981, - 6.0592274666, -8.4829187393, 6.0307927132, -9.6492681503, - 5.9987745285, -10.7977933884, 5.9632511139, -11.9265022278, - 5.9243106842, -13.0334835052, 5.8820490837, -14.1169185638, - 5.8365726471, -15.1750888824, 5.7879953384, -16.2063865662, - 5.7364420891, -17.2093257904, 5.6820459366, -18.1825466156, - 5.6249494553, -19.1248283386, 5.5653042793, -20.0350952148, - 5.5032711029, -20.9124298096, 5.4390201569, -21.7560806274, - 5.3727307320, -22.5654678345, 5.3045911789, -23.3402004242, - 5.2347993851, -24.0800762177, 5.1635618210, -24.7850971222, - 5.0910949707, -25.4554748535, 5.0176239014, -26.0916442871, - 4.9433832169, -26.6942691803, 4.8686161041, -27.2642498016, - 4.7935757637, -27.8027400970, 4.7185239792, -28.3111438751, - 5.6457915306, 28.2289581299, 5.7352571487, 27.7204093933, - 5.8247289658, 27.1820678711, 5.9138932228, 26.6125202179, - 6.0024461746, 26.0106010437, 6.0900959969, 25.3753986359, - 6.1765604019, 24.7062416077, 6.2615699768, 24.0026855469, - 6.3448653221, 23.2645053864, 6.4261975288, 22.4916915894, - 6.5053300858, 21.6844348907, 6.5820369720, 20.8431167603, - 6.6561026573, 19.9683074951, 6.7273230553, 19.0607490540, - 6.7955055237, 18.1213474274, 6.8604674339, 17.1511688232, - 6.9220380783, 16.1514225006, 6.9800572395, 15.1234579086, - 7.0343766212, 14.0687532425, 7.0848579407, 12.9889059067, - 7.1313738823, 11.8856229782, 7.1738095284, 10.7607145309, - 7.2120594978, 9.6160793304, 7.2460298538, 8.4537019730, - 7.2756385803, 7.2756385803, 7.3008131981, 6.0840110779, - 7.3214931488, 4.8809957504, 7.3376293182, 3.6688146591, - 7.3491826057, 2.4497275352, 7.3561258316, 1.2260209322, - 7.3584418297, -0.0000000000, 7.3561258316, -1.2260209322, - 7.3491826057, -2.4497275352, 7.3376293182, -3.6688146591, - 7.3214931488, -4.8809957504, 7.3008131981, -6.0840110779, - 7.2756385803, -7.2756385803, 7.2460298538, -8.4537019730, - 7.2120594978, -9.6160793304, 7.1738095284, -10.7607145309, - 7.1313738823, -11.8856229782, 7.0848579407, -12.9889059067, - 7.0343766212, -14.0687532425, 6.9800572395, -15.1234579086, - 6.9220380783, -16.1514225006, 6.8604674339, -17.1511688232, - 6.7955055237, -18.1213474274, 6.7273230553, -19.0607490540, - 6.6561026573, -19.9683074951, 6.5820369720, -20.8431167603, - 6.5053300858, -21.6844348907, 6.4261975288, -22.4916915894, - 6.3448653221, -23.2645053864, 6.2615699768, -24.0026855469, - 6.1765604019, -24.7062416077, 6.0900959969, -25.3753986359, - 6.0024461746, -26.0106010437, 5.9138932228, -26.6125202179, - 5.8247289658, -27.1820678711, 5.7352571487, -27.7204093933, - 5.6457915306, -28.2289581299, 6.5642604828, 28.1325454712, - 6.6678142548, 27.6238021851, 6.7714037895, 27.0856151581, - 6.8746614456, 26.5165519714, 6.9772343636, 25.9154434204, - 7.0787811279, 25.2813606262, 7.1789731979, 24.6136226654, - 7.2774958611, 23.9117717743, 7.3740458488, 23.1755733490, - 7.4683341980, 22.4050025940, 7.5600843430, 21.6002407074, - 7.6490316391, 20.7616577148, 7.7349257469, 19.8898086548, - 7.8175282478, 18.9854259491, 7.8966135979, 18.0494022369, - 7.9719705582, 17.0827941895, 8.0433988571, 16.0867977142, - 8.1107110977, 15.0627498627, 8.1737356186, 14.0121173859, - 8.2323093414, 12.9364862442, 8.2862854004, 11.8375511169, - 8.3355283737, 10.7171087265, 8.3799161911, 9.5770473480, - 8.4193401337, 8.4193401337, 8.4537019730, 7.2460298538, - 8.4829187393, 6.0592274666, 8.5069198608, 4.8610973358, - 8.5256481171, 3.6538491249, 8.5390567780, 2.4397306442, - 8.5471153259, 1.2210165262, 8.5498037338, -0.0000000000, - 8.5471153259, -1.2210165262, 8.5390567780, -2.4397306442, - 8.5256481171, -3.6538491249, 8.5069198608, -4.8610973358, - 8.4829187393, -6.0592274666, 8.4537019730, -7.2460298538, - 8.4193401337, -8.4193401337, 8.3799161911, -9.5770473480, - 8.3355283737, -10.7171087265, 8.2862854004, -11.8375511169, - 8.2323093414, -12.9364862442, 8.1737356186, -14.0121173859, - 8.1107110977, -15.0627498627, 8.0433988571, -16.0867977142, - 7.9719705582, -17.0827941895, 7.8966135979, -18.0494022369, - 7.8175282478, -18.9854259491, 7.7349257469, -19.8898086548, - 7.6490316391, -20.7616577148, 7.5600843430, -21.6002407074, - 7.4683341980, -22.4050025940, 7.3740458488, -23.1755733490, - 7.2774958611, -23.9117717743, 7.1789731979, -24.6136226654, - 7.0787811279, -25.2813606262, 6.9772343636, -25.9154434204, - 6.8746614456, -26.5165519714, 6.7714037895, -27.0856151581, - 6.6678142548, -27.6238021851, 6.5642604828, -28.1325454712, - 7.4726042747, 28.0222644806, 7.5898661613, 27.5132656097, - 7.7072062492, 26.9752216339, 7.8242039680, 26.4066886902, - 7.9404559135, 25.8064804077, 8.0555715561, 25.1736602783, - 8.1691761017, 24.5075283051, 8.2809085846, 23.8076114655, - 8.3904247284, 23.0736675262, 8.4973917007, 22.3056526184, - 8.6014947891, 21.5037364960, 8.7024316788, 20.6682758331, - 8.7999162674, 19.7998123169, 8.8936758041, 18.8990612030, - 8.9834527969, 17.9669055939, 9.0690040588, 17.0043830872, - 9.1501035690, 16.0126800537, 9.2265357971, 14.9931211472, - 9.2981033325, 13.9471549988, 9.3646221161, 12.8763561249, - 9.4259233475, 11.7824039459, 9.4818515778, 10.6670837402, - 9.5322685242, 9.5322685242, 9.5770473480, 8.3799161911, - 9.6160793304, 7.2120594978, 9.6492681503, 6.0307927132, - 9.6765327454, 4.8382663727, 9.6978073120, 3.6366777420, - 9.7130403519, 2.4282600880, 9.7221946716, 1.2152743340, - 9.7252483368, -0.0000000000, 9.7221946716, -1.2152743340, - 9.7130403519, -2.4282600880, 9.6978073120, -3.6366777420, - 9.6765327454, -4.8382663727, 9.6492681503, -6.0307927132, - 9.6160793304, -7.2120594978, 9.5770473480, -8.3799161911, - 9.5322685242, -9.5322685242, 9.4818515778, -10.6670837402, - 9.4259233475, -11.7824039459, 9.3646221161, -12.8763561249, - 9.2981033325, -13.9471549988, 9.2265357971, -14.9931211472, - 9.1501035690, -16.0126800537, 9.0690040588, -17.0043830872, - 8.9834527969, -17.9669055939, 8.8936758041, -18.8990612030, - 8.7999162674, -19.7998123169, 8.7024316788, -20.6682758331, - 8.6014947891, -21.5037364960, 8.4973917007, -22.3056526184, - 8.3904247284, -23.0736675262, 8.2809085846, -23.8076114655, - 8.1691761017, -24.5075283051, 8.0555715561, -25.1736602783, - 7.9404559135, -25.8064804077, 7.8242039680, -26.4066886902, - 7.7072062492, -26.9752216339, 7.5898661613, -27.5132656097, - 7.4726042747, -28.0222644806, 8.3695592880, 27.8985328674, - 8.5000963211, 27.3892002106, 8.6307678223, 26.8512763977, - 8.7611007690, 26.2833023071, 8.8906412125, 25.6840744019, - 9.0189504623, 25.0526409149, 9.1456069946, 24.3882865906, - 9.2702045441, 23.6905231476, 9.3923549652, 22.9590892792, - 9.5116853714, 22.1939334869, 9.6278400421, 21.3952007294, - 9.7404804230, 20.5632362366, 9.8492822647, 19.6985645294, - 9.9539413452, 18.8018894196, 10.0541667938, 17.8740749359, - 10.1496858597, 16.9161434174, 10.2402420044, 15.9292659760, - 10.3255958557, 14.9147500992, 10.4055233002, 13.8740310669, - 10.4798183441, 12.8086662292, 10.5482892990, 11.7203216553, - 10.6107635498, 10.6107635498, 10.6670837402, 9.4818515778, - 10.7171087265, 8.3355283737, 10.7607145309, 7.1738095284, - 10.7977933884, 5.9987745285, 10.8282556534, 4.8125581741, - 10.8520250320, 3.6173417568, 10.8690452576, 2.4153432846, - 10.8792734146, 1.2088081837, 10.8826856613, -0.0000000000, - 10.8792734146, -1.2088081837, 10.8690452576, -2.4153432846, - 10.8520250320, -3.6173417568, 10.8282556534, -4.8125581741, - 10.7977933884, -5.9987745285, 10.7607145309, -7.1738095284, - 10.7171087265, -8.3355283737, 10.6670837402, -9.4818515778, - 10.6107635498, -10.6107635498, 10.5482892990, -11.7203216553, - 10.4798183441, -12.8086662292, 10.4055233002, -13.8740310669, - 10.3255958557, -14.9147500992, 10.2402420044, -15.9292659760, - 10.1496858597, -16.9161434174, 10.0541667938, -17.8740749359, - 9.9539413452, -18.8018894196, 9.8492822647, -19.6985645294, - 9.7404804230, -20.5632362366, 9.6278400421, -21.3952007294, - 9.5116853714, -22.1939334869, 9.3923549652, -22.9590892792, - 9.2702045441, -23.6905231476, 9.1456069946, -24.3882865906, - 9.0189504623, -25.0526409149, 8.8906412125, -25.6840744019, - 8.7611007690, -26.2833023071, 8.6307678223, -26.8512763977, - 8.5000963211, -27.3892002106, 8.3695592880, -27.8985328674, - 9.2539377213, 27.7618141174, 9.3972616196, 27.2520580292, - 9.5407915115, 26.7142162323, 9.6840057373, 26.1468143463, - 9.8263969421, 25.5486316681, 9.9674777985, 24.9186954498, - 10.1067810059, 24.2562732697, 10.2438545227, 23.5608654022, - 10.3782663345, 22.8321857452, 10.5096044540, 22.0701675415, - 10.6374711990, 21.2749423981, 10.7614917755, 20.4468326569, - 10.8813056946, 19.5863494873, 10.9965744019, 18.6941757202, - 11.1069755554, 17.7711601257, 11.2122049332, 16.8183078766, - 11.3119792938, 15.8367710114, 11.4060306549, 14.8278398514, - 11.4941110611, 13.7929334641, 11.5759906769, 12.7335901260, - 11.6514587402, 11.6514587402, 11.7203216553, 10.5482892990, - 11.7824039459, 9.4259233475, 11.8375511169, 8.2862854004, - 11.8856229782, 7.1313738823, 11.9265022278, 5.9632511139, - 11.9600868225, 4.7840347290, 11.9862937927, 3.5958881378, - 12.0050592422, 2.4010119438, 12.0163364410, 1.2016336918, - 12.0200986862, -0.0000000000, 12.0163364410, -1.2016336918, - 12.0050592422, -2.4010119438, 11.9862937927, -3.5958881378, - 11.9600868225, -4.7840347290, 11.9265022278, -5.9632511139, - 11.8856229782, -7.1313738823, 11.8375511169, -8.2862854004, - 11.7824039459, -9.4259233475, 11.7203216553, -10.5482892990, - 11.6514587402, -11.6514587402, 11.5759906769, -12.7335901260, - 11.4941110611, -13.7929334641, 11.4060306549, -14.8278398514, - 11.3119792938, -15.8367710114, 11.2122049332, -16.8183078766, - 11.1069755554, -17.7711601257, 10.9965744019, -18.6941757202, - 10.8813056946, -19.5863494873, 10.7614917755, -20.4468326569, - 10.6374711990, -21.2749423981, 10.5096044540, -22.0701675415, - 10.3782663345, -22.8321857452, 10.2438545227, -23.5608654022, - 10.1067810059, -24.2562732697, 9.9674777985, -24.9186954498, - 9.8263969421, -25.5486316681, 9.6840057373, -26.1468143463, - 9.5407915115, -26.7142162323, 9.3972616196, -27.2520580292, - 9.2539377213, -27.7618141174, 10.1246328354, 27.6126346588, - 10.2802000046, 27.1023464203, 10.4360666275, 26.5645332336, - 10.5916547775, 25.9976978302, 10.7464084625, 25.4006023407, - 10.8997936249, 24.7722568512, 11.0512914658, 24.1119098663, - 11.2004089355, 23.4190368652, 11.3466701508, 22.6933403015, - 11.4896192551, 21.9347286224, 11.6288223267, 21.1433124542, - 11.7638635635, 20.3194007874, 11.8943500519, 19.4634819031, - 12.0199069977, 18.5762195587, 12.1401796341, 17.6584434509, - 12.2548351288, 16.7111396790, 12.3635606766, 15.7354402542, - 12.4660615921, 14.7326173782, 12.5620651245, 13.7040710449, - 12.6513185501, 12.6513185501, 12.7335901260, 11.5759906769, - 12.8086662292, 10.4798183441, 12.8763561249, 9.3646221161, - 12.9364862442, 8.2323093414, 12.9889059067, 7.0848579407, - 13.0334835052, 5.9243106842, 13.0701084137, 4.7527666092, - 13.0986881256, 3.5723693371, 13.1191530228, 2.3853003979, - 13.1314516068, 1.1937683821, 13.1355552673, -0.0000000000, - 13.1314516068, -1.1937683821, 13.1191530228, -2.3853003979, - 13.0986881256, -3.5723693371, 13.0701084137, -4.7527666092, - 13.0334835052, -5.9243106842, 12.9889059067, -7.0848579407, - 12.9364862442, -8.2323093414, 12.8763561249, -9.3646221161, - 12.8086662292, -10.4798183441, 12.7335901260, -11.5759906769, - 12.6513185501, -12.6513185501, 12.5620651245, -13.7040710449, - 12.4660615921, -14.7326173782, 12.3635606766, -15.7354402542, - 12.2548351288, -16.7111396790, 12.1401796341, -17.6584434509, - 12.0199069977, -18.5762195587, 11.8943500519, -19.4634819031, - 11.7638635635, -20.3194007874, 11.6288223267, -21.1433124542, - 11.4896192551, -21.9347286224, 11.3466701508, -22.6933403015, - 11.2004089355, -23.4190368652, 11.0512914658, -24.1119098663, - 10.8997936249, -24.7722568512, 10.7464084625, -25.4006023407, - 10.5916547775, -25.9976978302, 10.4360666275, -26.5645332336, - 10.2802000046, -27.1023464203, 10.1246328354, -27.6126346588, - 10.9806299210, 27.4515743256, 11.1478443146, 26.9406242371, - 11.3154697418, 26.4027633667, 11.4828767776, 25.8364715576, - 11.6494579315, 25.2404918671, 11.8146295547, 24.6138114929, - 11.9778289795, 23.9556579590, 12.1385145187, 23.2654857635, - 12.2961692810, 22.5429763794, 12.4502954483, 21.7880172729, - 12.6004190445, 21.0006980896, 12.7460880280, 20.1813049316, - 12.8868713379, 19.3303070068, 13.0223627090, 18.4483470917, - 13.1521739960, 17.5362319946, 13.2759418488, 16.5949268341, - 13.3933238983, 15.6255445480, 13.5040016174, 14.6293354034, - 13.6076755524, 13.6076755524, 13.7040710449, 12.5620651245, - 13.7929334641, 11.4941110611, 13.8740310669, 10.4055233002, - 13.9471549988, 9.2981033325, 14.0121173859, 8.1737356186, - 14.0687532425, 7.0343766212, 14.1169185638, 5.8820490837, - 14.1564912796, 4.7188305855, 14.1873731613, 3.5468432903, - 14.2094869614, 2.3682479858, 14.2227773666, 1.1852314472, - 14.2272119522, -0.0000000000, 14.2227773666, -1.1852314472, - 14.2094869614, -2.3682479858, 14.1873731613, -3.5468432903, - 14.1564912796, -4.7188305855, 14.1169185638, -5.8820490837, - 14.0687532425, -7.0343766212, 14.0121173859, -8.1737356186, - 13.9471549988, -9.2981033325, 13.8740310669, -10.4055233002, - 13.7929334641, -11.4941110611, 13.7040710449, -12.5620651245, - 13.6076755524, -13.6076755524, 13.5040016174, -14.6293354034, - 13.3933238983, -15.6255445480, 13.2759418488, -16.5949268341, - 13.1521739960, -17.5362319946, 13.0223627090, -18.4483470917, - 12.8868713379, -19.3303070068, 12.7460880280, -20.1813049316, - 12.6004190445, -21.0006980896, 12.4502954483, -21.7880172729, - 12.2961692810, -22.5429763794, 12.1385145187, -23.2654857635, - 11.9778289795, -23.9556579590, 11.8146295547, -24.6138114929, - 11.6494579315, -25.2404918671, 11.4828767776, -25.8364715576, - 11.3154697418, -26.4027633667, 11.1478443146, -26.9406242371, - 10.9806299210, -27.4515743256, 11.8210153580, 27.2792663574, - 11.9992265701, 26.7675056458, 12.1779823303, 26.2294998169, - 12.3566007614, 25.6637096405, 12.5344247818, 25.0688495636, - 12.7108211517, 24.4438858032, 12.8851795197, 23.7880249023, - 13.0569162369, 23.1006965637, 13.2254667282, 22.3815593719, - 13.3902959824, 21.6304779053, 13.5508880615, 20.8475208282, - 13.7067537308, 20.0329475403, 13.8574275970, 19.1872081757, - 14.0024662018, 18.3109169006, 14.1414518356, 17.4048633575, - 14.2739896774, 16.4699878693, 14.3997106552, 15.5073804855, - 14.5182666779, 14.5182666779, 14.6293354034, 13.5040016174, - 14.7326173782, 12.4660615921, 14.8278398514, 11.4060306549, - 14.9147500992, 10.3255958557, 14.9931211472, 9.2265357971, - 15.0627498627, 8.1107110977, 15.1234579086, 6.9800572395, - 15.1750888824, 5.8365726471, 15.2175111771, 4.6823110580, - 15.2506179810, 3.5193734169, 15.2743263245, 2.3498964310, - 15.2885742188, 1.1760442257, 15.2933282852, -0.0000000000, - 15.2885742188, -1.1760442257, 15.2743263245, -2.3498964310, - 15.2506179810, -3.5193734169, 15.2175111771, -4.6823110580, - 15.1750888824, -5.8365726471, 15.1234579086, -6.9800572395, - 15.0627498627, -8.1107110977, 14.9931211472, -9.2265357971, - 14.9147500992, -10.3255958557, 14.8278398514, -11.4060306549, - 14.7326173782, -12.4660615921, 14.6293354034, -13.5040016174, - 14.5182666779, -14.5182666779, 14.3997106552, -15.5073804855, - 14.2739896774, -16.4699878693, 14.1414518356, -17.4048633575, - 14.0024662018, -18.3109169006, 13.8574275970, -19.1872081757, - 13.7067537308, -20.0329475403, 13.5508880615, -20.8475208282, - 13.3902959824, -21.6304779053, 13.2254667282, -22.3815593719, - 13.0569162369, -23.1006965637, 12.8851795197, -23.7880249023, - 12.7108211517, -24.4438858032, 12.5344247818, -25.0688495636, - 12.3566007614, -25.6637096405, 12.1779823303, -26.2294998169, - 11.9992265701, -26.7675056458, 11.8210153580, -27.2792663574, - 12.6449871063, 27.0964012146, 12.8334894180, 26.5836582184, - 13.0226945877, 26.0453891754, 13.2118673325, 25.4800300598, - 13.4003000259, 24.8862724304, 13.5873117447, 24.2630558014, - 13.7722444534, 23.6095619202, 13.9544687271, 22.9251995087, - 14.1333789825, 22.2095966339, 14.3083972931, 21.4625949860, - 14.4789676666, 20.6842403412, 14.6445646286, 19.8747653961, - 14.8046855927, 19.0345954895, 14.9588537216, 18.1643218994, - 15.1066188812, 17.2647075653, 15.2475576401, 16.3366680145, - 15.3812685013, 15.3812685013, 15.5073804855, 14.3997106552, - 15.6255445480, 13.3933238983, 15.7354402542, 12.3635606766, - 15.8367710114, 11.3119792938, 15.9292659760, 10.2402420044, - 16.0126800537, 9.1501035690, 16.0867977142, 8.0433988571, - 16.1514225006, 6.9220380783, 16.2063865662, 5.7879953384, - 16.2515525818, 4.6433005333, 16.2868003845, 3.4900286198, - 16.3120422363, 2.3302917480, 16.3272132874, 1.1662294865, - 16.3322734833, -0.0000000000, 16.3272132874, -1.1662294865, - 16.3120422363, -2.3302917480, 16.2868003845, -3.4900286198, - 16.2515525818, -4.6433005333, 16.2063865662, -5.7879953384, - 16.1514225006, -6.9220380783, 16.0867977142, -8.0433988571, - 16.0126800537, -9.1501035690, 15.9292659760, -10.2402420044, - 15.8367710114, -11.3119792938, 15.7354402542, -12.3635606766, - 15.6255445480, -13.3933238983, 15.5073804855, -14.3997106552, - 15.3812685013, -15.3812685013, 15.2475576401, -16.3366680145, - 15.1066188812, -17.2647075653, 14.9588537216, -18.1643218994, - 14.8046855927, -19.0345954895, 14.6445646286, -19.8747653961, - 14.4789676666, -20.6842403412, 14.3083972931, -21.4625949860, - 14.1333789825, -22.2095966339, 13.9544687271, -22.9251995087, - 13.7722444534, -23.6095619202, 13.5873117447, -24.2630558014, - 13.4003000259, -24.8862724304, 13.2118673325, -25.4800300598, - 13.0226945877, -26.0453891754, 12.8334894180, -26.5836582184, - 12.6449871063, -27.0964012146, 13.4518613815, 26.9037227631, - 13.6498956680, 26.3897991180, 13.8488159180, 25.8511238098, - 14.0478353500, 25.2861042023, 14.2461948395, 24.6934051514, - 14.4431657791, 24.0719413757, 14.6380424500, 23.4208679199, - 14.8301496506, 22.7395629883, 15.0188398361, 22.0276317596, - 15.2034921646, 21.2848892212, 15.3835144043, 20.5113525391, - 15.5583400726, 19.7072315216, 15.7274322510, 18.8729190826, - 15.8902797699, 18.0089836121, 16.0464000702, 17.1161594391, - 16.1953392029, 16.1953392029, 16.3366680145, 15.2475576401, - 16.4699878693, 14.2739896774, 16.5949268341, 13.2759418488, - 16.7111396790, 12.2548351288, 16.8183078766, 11.2122049332, - 16.9161434174, 10.1496858597, 17.0043830872, 9.0690040588, - 17.0827941895, 7.9719705582, 17.1511688232, 6.8604674339, - 17.2093257904, 5.7364420891, 17.2571182251, 4.6018981934, - 17.2944164276, 3.4588835239, 17.3211288452, 2.3094837666, - 17.3371829987, 1.1558121443, 17.3425388336, -0.0000000000, - 17.3371829987, -1.1558121443, 17.3211288452, -2.3094837666, - 17.2944164276, -3.4588835239, 17.2571182251, -4.6018981934, - 17.2093257904, -5.7364420891, 17.1511688232, -6.8604674339, - 17.0827941895, -7.9719705582, 17.0043830872, -9.0690040588, - 16.9161434174, -10.1496858597, 16.8183078766, -11.2122049332, - 16.7111396790, -12.2548351288, 16.5949268341, -13.2759418488, - 16.4699878693, -14.2739896774, 16.3366680145, -15.2475576401, - 16.1953392029, -16.1953392029, 16.0464000702, -17.1161594391, - 15.8902797699, -18.0089836121, 15.7274322510, -18.8729190826, - 15.5583400726, -19.7072315216, 15.3835144043, -20.5113525391, - 15.2034921646, -21.2848892212, 15.0188398361, -22.0276317596, - 14.8301496506, -22.7395629883, 14.6380424500, -23.4208679199, - 14.4431657791, -24.0719413757, 14.2461948395, -24.6934051514, - 14.0478353500, -25.2861042023, 13.8488159180, -25.8511238098, - 13.6498956680, -26.3897991180, 13.4518613815, -26.9037227631, - 14.2410821915, 26.7020301819, 14.4478359222, 26.1867027283, - 14.6556854248, 25.6474494934, 14.8637924194, 25.0826492310, - 15.0713491440, 24.4909420013, 15.2775745392, 23.8712100983, - 15.4817190170, 23.2225780487, 15.6830615997, 22.5444011688, - 15.8809118271, 21.8362541199, 16.0746059418, 21.0979213715, - 16.2635135651, 20.3293914795, 16.4470310211, 19.5308494568, - 16.6245822906, 18.7026557922, 16.7956275940, 17.8453540802, - 16.9596481323, 16.9596481323, 17.1161594391, 16.0464000702, - 17.2647075653, 15.1066188812, 17.4048633575, 14.1414518356, - 17.5362319946, 13.1521739960, 17.6584434509, 12.1401796341, - 17.7711601257, 11.1069755554, 17.8740749359, 10.0541667938, - 17.9669055939, 8.9834527969, 18.0494022369, 7.8966135979, - 18.1213474274, 6.7955055237, 18.1825466156, 5.6820459366, - 18.2328395844, 4.5582098961, 18.2720947266, 3.4260177612, - 18.3002071381, 2.2875258923, 18.3171043396, 1.1448190212, - 18.3227405548, -0.0000000000, 18.3171043396, -1.1448190212, - 18.3002071381, -2.2875258923, 18.2720947266, -3.4260177612, - 18.2328395844, -4.5582098961, 18.1825466156, -5.6820459366, - 18.1213474274, -6.7955055237, 18.0494022369, -7.8966135979, - 17.9669055939, -8.9834527969, 17.8740749359, -10.0541667938, - 17.7711601257, -11.1069755554, 17.6584434509, -12.1401796341, - 17.5362319946, -13.1521739960, 17.4048633575, -14.1414518356, - 17.2647075653, -15.1066188812, 17.1161594391, -16.0464000702, - 16.9596481323, -16.9596481323, 16.7956275940, -17.8453540802, - 16.6245822906, -18.7026557922, 16.4470310211, -19.5308494568, - 16.2635135651, -20.3293914795, 16.0746059418, -21.0979213715, - 15.8809118271, -21.8362541199, 15.6830615997, -22.5444011688, - 15.4817190170, -23.2225780487, 15.2775745392, -23.8712100983, - 15.0713491440, -24.4909420013, 14.8637924194, -25.0826492310, - 14.6556854248, -25.6474494934, 14.4478359222, -26.1867027283, - 14.2410821915, -26.7020301819, 15.0122346878, 26.4921798706, - 15.2268390656, 25.9751949310, 15.4427795410, 25.4351654053, - 15.6591653824, 24.8704395294, 15.8751382828, 24.2796230316, - 16.0898685455, 23.6615715027, 16.3025608063, 23.0153789520, - 16.5124473572, 22.3403701782, 16.7187938690, 21.6360874176, - 16.9208984375, 20.9022865295, 17.1180896759, 20.1389274597, - 17.3097229004, 19.3461608887, 17.4951934814, 18.5243206024, - 17.6739177704, 17.6739177704, 17.8453540802, 16.7956275940, - 18.0089836121, 15.8902797699, 18.1643218994, 14.9588537216, - 18.3109169006, 14.0024662018, 18.4483470917, 13.0223627090, - 18.5762195587, 12.0199069977, 18.6941757202, 10.9965744019, - 18.8018894196, 9.9539413452, 18.8990612030, 8.8936758041, - 18.9854259491, 7.8175282478, 19.0607490540, 6.7273230553, - 19.1248283386, 5.6249494553, 19.1774902344, 4.5123505592, - 19.2185974121, 3.3915169239, 19.2480354309, 2.2644748688, - 19.2657318115, 1.1332782507, 19.2716350555, -0.0000000000, - 19.2657318115, -1.1332782507, 19.2480354309, -2.2644748688, - 19.2185974121, -3.3915169239, 19.1774902344, -4.5123505592, - 19.1248283386, -5.6249494553, 19.0607490540, -6.7273230553, - 18.9854259491, -7.8175282478, 18.8990612030, -8.8936758041, - 18.8018894196, -9.9539413452, 18.6941757202, -10.9965744019, - 18.5762195587, -12.0199069977, 18.4483470917, -13.0223627090, - 18.3109169006, -14.0024662018, 18.1643218994, -14.9588537216, - 18.0089836121, -15.8902797699, 17.8453540802, -16.7956275940, - 17.6739177704, -17.6739177704, 17.4951934814, -18.5243206024, - 17.3097229004, -19.3461608887, 17.1180896759, -20.1389274597, - 16.9208984375, -20.9022865295, 16.7187938690, -21.6360874176, - 16.5124473572, -22.3403701782, 16.3025608063, -23.0153789520, - 16.0898685455, -23.6615715027, 15.8751382828, -24.2796230316, - 15.6591653824, -24.8704395294, 15.4427795410, -25.4351654053, - 15.2268390656, -25.9751949310, 15.0122346878, -26.4921798706, - 15.7650489807, 26.2750816345, 15.9865808487, 25.7561588287, - 16.2097206116, 25.2151222229, 16.4335269928, 24.6502914429, - 16.6570873260, 24.0602378845, 16.8795280457, 23.4437885284, - 17.1000003815, 22.7999992371, 17.3176956177, 22.1281681061, - 17.5318374634, 21.4278011322, 17.7416801453, 20.6986255646, - 17.9465122223, 19.9405689240, 18.1456527710, 19.1537456512, - 18.3384609222, 18.3384609222, 18.5243206024, 17.4951934814, - 18.7026557922, 16.6245822906, 18.8729190826, 15.7274322510, - 19.0345954895, 14.8046855927, 19.1872081757, 13.8574275970, - 19.3303070068, 12.8868713379, 19.4634819031, 11.8943500519, - 19.5863494873, 10.8813056946, 19.6985645294, 9.8492822647, - 19.7998123169, 8.7999162674, 19.8898086548, 7.7349257469, - 19.9683074951, 6.6561026573, 20.0350952148, 5.5653042793, - 20.0899868011, 4.4644412994, 20.1328353882, 3.3554723263, - 20.1635227203, 2.2403914928, 20.1819686890, 1.1212204695, - 20.1881237030, -0.0000000000, 20.1819686890, -1.1212204695, - 20.1635227203, -2.2403914928, 20.1328353882, -3.3554723263, - 20.0899868011, -4.4644412994, 20.0350952148, -5.5653042793, - 19.9683074951, -6.6561026573, 19.8898086548, -7.7349257469, - 19.7998123169, -8.7999162674, 19.6985645294, -9.8492822647, - 19.5863494873, -10.8813056946, 19.4634819031, -11.8943500519, - 19.3303070068, -12.8868713379, 19.1872081757, -13.8574275970, - 19.0345954895, -14.8046855927, 18.8729190826, -15.7274322510, - 18.7026557922, -16.6245822906, 18.5243206024, -17.4951934814, - 18.3384609222, -18.3384609222, 18.1456527710, -19.1537456512, - 17.9465122223, -19.9405689240, 17.7416801453, -20.6986255646, - 17.5318374634, -21.4278011322, 17.3176956177, -22.1281681061, - 17.1000003815, -22.7999992371, 16.8795280457, -23.4437885284, - 16.6570873260, -24.0602378845, 16.4335269928, -24.6502914429, - 16.2097206116, -25.2151222229, 15.9865808487, -25.7561588287, - 15.7650489807, -26.2750816345, 16.4994106293, 26.0517005920, - 16.7268943787, 25.5305233002, 16.9562911987, 24.9882202148, - 17.1866073608, 24.4230728149, 17.4168796539, 23.8336238861, - 17.6461811066, 23.2186603546, 17.8736248016, 22.5772113800, - 18.0983524323, 21.9085330963, 18.3195438385, 21.2121028900, - 18.5364131927, 20.4876136780, 18.7482070923, 19.7349548340, - 18.9542121887, 18.9542121887, 19.1537456512, 18.1456527710, - 19.3461608887, 17.3097229004, 19.5308494568, 16.4470310211, - 19.7072315216, 15.5583400726, 19.8747653961, 14.6445646286, - 20.0329475403, 13.7067537308, 20.1813049316, 12.7460880280, - 20.3194007874, 11.7638635635, 20.4468326569, 10.7614917755, - 20.5632362366, 9.7404804230, 20.6682758331, 8.7024316788, - 20.7616577148, 7.6490316391, 20.8431167603, 6.5820369720, - 20.9124298096, 5.5032711029, 20.9694004059, 4.4146108627, - 21.0138759613, 3.3179802895, 21.0457305908, 2.2153401375, - 21.0648784637, 1.1086778641, 21.0712661743, -0.0000000000, - 21.0648784637, -1.1086778641, 21.0457305908, -2.2153401375, - 21.0138759613, -3.3179802895, 20.9694004059, -4.4146108627, - 20.9124298096, -5.5032711029, 20.8431167603, -6.5820369720, - 20.7616577148, -7.6490316391, 20.6682758331, -8.7024316788, - 20.5632362366, -9.7404804230, 20.4468326569, -10.7614917755, - 20.3194007874, -11.7638635635, 20.1813049316, -12.7460880280, - 20.0329475403, -13.7067537308, 19.8747653961, -14.6445646286, - 19.7072315216, -15.5583400726, 19.5308494568, -16.4470310211, - 19.3461608887, -17.3097229004, 19.1537456512, -18.1456527710, - 18.9542121887, -18.9542121887, 18.7482070923, -19.7349548340, - 18.5364131927, -20.4876136780, 18.3195438385, -21.2121028900, - 18.0983524323, -21.9085330963, 17.8736248016, -22.5772113800, - 17.6461811066, -23.2186603546, 17.4168796539, -23.8336238861, - 17.1866073608, -24.4230728149, 16.9562911987, -24.9882202148, - 16.7268943787, -25.5305233002, 16.4994106293, -26.0517005920, - 17.2153701782, 25.8230571747, 17.4477767944, 25.2992763519, - 17.6824359894, 24.7554092407, 17.9182987213, 24.1897048950, - 18.1543560028, 23.6006641388, 18.3896331787, 22.9870414734, - 18.6231899261, 22.3478298187, 18.8541297913, 21.6822490692, - 19.0815830231, 20.9897422791, 19.3047275543, 20.2699642181, - 19.5227680206, 19.5227680206, 19.7349548340, 18.7482070923, - 19.9405689240, 17.9465122223, 20.1389274597, 17.1180896759, - 20.3293914795, 16.2635135651, 20.5113525391, 15.3835144043, - 20.6842403412, 14.4789676666, 20.8475208282, 13.5508880615, - 21.0006980896, 12.6004190445, 21.1433124542, 11.6288223267, - 21.2749423981, 10.6374711990, 21.3952007294, 9.6278400421, - 21.5037364960, 8.6014947891, 21.6002407074, 7.5600843430, - 21.6844348907, 6.5053300858, 21.7560806274, 5.4390201569, - 21.8149738312, 4.3629951477, 21.8609542847, 3.2791430950, - 21.8938865662, 2.1893887520, 21.9136848450, 1.0956841707, - 21.9202899933, -0.0000000000, 21.9136848450, -1.0956841707, - 21.8938865662, -2.1893887520, 21.8609542847, -3.2791430950, - 21.8149738312, -4.3629951477, 21.7560806274, -5.4390201569, - 21.6844348907, -6.5053300858, 21.6002407074, -7.5600843430, - 21.5037364960, -8.6014947891, 21.3952007294, -9.6278400421, - 21.2749423981, -10.6374711990, 21.1433124542, -11.6288223267, - 21.0006980896, -12.6004190445, 20.8475208282, -13.5508880615, - 20.6842403412, -14.4789676666, 20.5113525391, -15.3835144043, - 20.3293914795, -16.2635135651, 20.1389274597, -17.1180896759, - 19.9405689240, -17.9465122223, 19.7349548340, -18.7482070923, - 19.5227680206, -19.5227680206, 19.3047275543, -20.2699642181, - 19.0815830231, -20.9897422791, 18.8541297913, -21.6822490692, - 18.6231899261, -22.3478298187, 18.3896331787, -22.9870414734, - 18.1543560028, -23.6006641388, 17.9182987213, -24.1897048950, - 17.6824359894, -24.7554092407, 17.4477767944, -25.2992763519, - 17.2153701782, -25.8230571747, 17.9131584167, 25.5902252197, - 18.1494007111, 25.0634574890, 18.3882732391, 24.5176963806, - 18.6286735535, 23.9511528015, 18.8695411682, 23.3622894287, - 19.1098537445, 22.7498264313, 19.3486251831, 22.1127147675, - 19.5849094391, 21.4501399994, 19.8178005219, 20.7615051270, - 20.0464286804, 20.0464286804, 20.2699642181, 19.3047275543, - 20.4876136780, 18.5364131927, 20.6986255646, 17.7416801453, - 20.9022865295, 16.9208984375, 21.0979213715, 16.0746059418, - 21.2848892212, 15.2034921646, 21.4625949860, 14.3083972931, - 21.6304779053, 13.3902959824, 21.7880172729, 12.4502954483, - 21.9347286224, 11.4896192551, 22.0701675415, 10.5096044540, - 22.1939334869, 9.5116853714, 22.3056526184, 8.4973917007, - 22.4050025940, 7.4683341980, 22.4916915894, 6.4261975288, - 22.5654678345, 5.3727307320, 22.6261196136, 4.3097372055, - 22.6734752655, 3.2390677929, 22.7073955536, 2.1626091003, - 22.7277870178, 1.0822755098, 22.7345905304, -0.0000000000, - 22.7277870178, -1.0822755098, 22.7073955536, -2.1626091003, - 22.6734752655, -3.2390677929, 22.6261196136, -4.3097372055, - 22.5654678345, -5.3727307320, 22.4916915894, -6.4261975288, - 22.4050025940, -7.4683341980, 22.3056526184, -8.4973917007, - 22.1939334869, -9.5116853714, 22.0701675415, -10.5096044540, - 21.9347286224, -11.4896192551, 21.7880172729, -12.4502954483, - 21.6304779053, -13.3902959824, 21.4625949860, -14.3083972931, - 21.2848892212, -15.2034921646, 21.0979213715, -16.0746059418, - 20.9022865295, -16.9208984375, 20.6986255646, -17.7416801453, - 20.4876136780, -18.5364131927, 20.2699642181, -19.3047275543, - 20.0464286804, -20.0464286804, 19.8178005219, -20.7615051270, - 19.5849094391, -21.4501399994, 19.3486251831, -22.1127147675, - 19.1098537445, -22.7498264313, 18.8695411682, -23.3622894287, - 18.6286735535, -23.9511528015, 18.3882732391, -24.5176963806, - 18.1494007111, -25.0634574890, 17.9131584167, -25.5902252197, - 18.5931816101, 25.3543395996, 18.8321228027, 24.8241615295, - 19.0741081238, 24.2761363983, 19.3179836273, 23.7084350586, - 19.5626392365, 23.1194839478, 19.8070030212, 22.5079574585, - 20.0500411987, 21.8727722168, 20.2907657623, 21.2130737305, - 20.5282230377, 20.5282230377, 20.7615051270, 19.8178005219, - 20.9897422791, 19.0815830231, 21.2121028900, 18.3195438385, - 21.4278011322, 17.5318374634, 21.6360874176, 16.7187938690, - 21.8362541199, 15.8809118271, 22.0276317596, 15.0188398361, - 22.2095966339, 14.1333789825, 22.3815593719, 13.2254667282, - 22.5429763794, 12.2961692810, 22.6933403015, 11.3466701508, - 22.8321857452, 10.3782663345, 22.9590892792, 9.3923549652, - 23.0736675262, 8.3904247284, 23.1755733490, 7.3740458488, - 23.2645053864, 6.3448653221, 23.3402004242, 5.3045911789, - 23.4024372101, 4.2549886703, 23.4510307312, 3.1978678703, - 23.4858417511, 2.1350765228, 23.5067691803, 1.0684895515, - 23.5137519836, -0.0000000000, 23.5067691803, -1.0684895515, - 23.4858417511, -2.1350765228, 23.4510307312, -3.1978678703, - 23.4024372101, -4.2549886703, 23.3402004242, -5.3045911789, - 23.2645053864, -6.3448653221, 23.1755733490, -7.3740458488, - 23.0736675262, -8.3904247284, 22.9590892792, -9.3923549652, - 22.8321857452, -10.3782663345, 22.6933403015, -11.3466701508, - 22.5429763794, -12.2961692810, 22.3815593719, -13.2254667282, - 22.2095966339, -14.1333789825, 22.0276317596, -15.0188398361, - 21.8362541199, -15.8809118271, 21.6360874176, -16.7187938690, - 21.4278011322, -17.5318374634, 21.2121028900, -18.3195438385, - 20.9897422791, -19.0815830231, 20.7615051270, -19.8178005219, - 20.5282230377, -20.5282230377, 20.2907657623, -21.2130737305, - 20.0500411987, -21.8727722168, 19.8070030212, -22.5079574585, - 19.5626392365, -23.1194839478, 19.3179836273, -23.7084350586, - 19.0741081238, -24.2761363983, 18.8321228027, -24.8241615295, - 18.5931816101, -25.3543395996, 19.2560462952, 25.1165828705, - 19.4964923859, 24.5825328827, 19.7404365540, 24.0318355560, - 19.9866752625, 23.4626197815, 20.2340469360, 22.8732719421, - 20.4814300537, 22.2624244690, 20.7277450562, 21.6289520264, - 20.9719581604, 20.9719581604, 21.2130737305, 20.2907657623, - 21.4501399994, 19.5849094391, 21.6822490692, 18.8541297913, - 21.9085330963, 18.0983524323, 22.1281681061, 17.3176956177, - 22.3403701782, 16.5124473572, 22.5444011688, 15.6830615997, - 22.7395629883, 14.8301496506, 22.9251995087, 13.9544687271, - 23.1006965637, 13.0569162369, 23.2654857635, 12.1385145187, - 23.4190368652, 11.2004089355, 23.5608654022, 10.2438545227, - 23.6905231476, 9.2702045441, 23.8076114655, 8.2809085846, - 23.9117717743, 7.2774958611, 24.0026855469, 6.2615699768, - 24.0800762177, 5.2347993851, 24.1437149048, 4.1989068985, - 24.1934070587, 3.1556618214, 24.2290077209, 2.1068701744, - 24.2504119873, 1.0543657541, 24.2575531006, -0.0000000000, - 24.2504119873, -1.0543657541, 24.2290077209, -2.1068701744, - 24.1934070587, -3.1556618214, 24.1437149048, -4.1989068985, - 24.0800762177, -5.2347993851, 24.0026855469, -6.2615699768, - 23.9117717743, -7.2774958611, 23.8076114655, -8.2809085846, - 23.6905231476, -9.2702045441, 23.5608654022, -10.2438545227, - 23.4190368652, -11.2004089355, 23.2654857635, -12.1385145187, - 23.1006965637, -13.0569162369, 22.9251995087, -13.9544687271, - 22.7395629883, -14.8301496506, 22.5444011688, -15.6830615997, - 22.3403701782, -16.5124473572, 22.1281681061, -17.3176956177, - 21.9085330963, -18.0983524323, 21.6822490692, -18.8541297913, - 21.4501399994, -19.5849094391, 21.2130737305, -20.2907657623, - 20.9719581604, -20.9719581604, 20.7277450562, -21.6289520264, - 20.4814300537, -22.2624244690, 20.2340469360, -22.8732719421, - 19.9866752625, -23.4626197815, 19.7404365540, -24.0318355560, - 19.4964923859, -24.5825328827, 19.2560462952, -25.1165828705, - 19.9025573730, 24.8781986237, 20.1432590485, 24.3397712708, - 20.3879585266, 23.7859516144, 20.6353988647, 23.2148227692, - 20.8843650818, 22.6247291565, 21.1336898804, 22.0142593384, - 21.3822460175, 21.3822460175, 21.6289520264, 20.7277450562, - 21.8727722168, 20.0500411987, 22.1127147675, 19.3486251831, - 22.3478298187, 18.6231899261, 22.5772113800, 17.8736248016, - 22.7999992371, 17.1000003815, 23.0153789520, 16.3025608063, - 23.2225780487, 15.4817190170, 23.4208679199, 14.6380424500, - 23.6095619202, 13.7722444534, 23.7880249023, 12.8851795197, - 23.9556579590, 11.9778289795, 24.1119098663, 11.0512914658, - 24.2562732697, 10.1067810059, 24.3882865906, 9.1456069946, - 24.5075283051, 8.1691761017, 24.6136226654, 7.1789731979, - 24.7062416077, 6.1765604019, 24.7850971222, 5.1635618210, - 24.8499469757, 4.1416578293, 24.9005908966, 3.1125738621, - 24.9368743896, 2.0780727863, 24.9586887360, 1.0399453640, - 24.9659690857, -0.0000000000, 24.9586887360, -1.0399453640, - 24.9368743896, -2.0780727863, 24.9005908966, -3.1125738621, - 24.8499469757, -4.1416578293, 24.7850971222, -5.1635618210, - 24.7062416077, -6.1765604019, 24.6136226654, -7.1789731979, - 24.5075283051, -8.1691761017, 24.3882865906, -9.1456069946, - 24.2562732697, -10.1067810059, 24.1119098663, -11.0512914658, - 23.9556579590, -11.9778289795, 23.7880249023, -12.8851795197, - 23.6095619202, -13.7722444534, 23.4208679199, -14.6380424500, - 23.2225780487, -15.4817190170, 23.0153789520, -16.3025608063, - 22.7999992371, -17.1000003815, 22.5772113800, -17.8736248016, - 22.3478298187, -18.6231899261, 22.1127147675, -19.3486251831, - 21.8727722168, -20.0500411987, 21.6289520264, -20.7277450562, - 21.3822460175, -21.3822460175, 21.1336898804, -22.0142593384, - 20.8843650818, -22.6247291565, 20.6353988647, -23.2148227692, - 20.3879585266, -23.7859516144, 20.1432590485, -24.3397712708, - 19.9025573730, -24.8781986237, 20.5337333679, 24.6404800415, - 20.7733898163, 24.0971317291, 21.0175857544, 23.5396957397, - 21.2650127411, 22.9662132263, 21.5144042969, 22.3749809265, - 21.7645435333, 21.7645435333, 22.0142593384, 21.1336898804, - 22.2624244690, 20.4814300537, 22.5079574585, 19.8070030212, - 22.7498264313, 19.1098537445, 22.9870414734, 18.3896331787, - 23.2186603546, 17.6461811066, 23.4437885284, 16.8795280457, - 23.6615715027, 16.0898685455, 23.8712100983, 15.2775745392, - 24.0719413757, 14.4431657791, 24.2630558014, 13.5873117447, - 24.4438858032, 12.7108211517, 24.6138114929, 11.8146295547, - 24.7722568512, 10.8997936249, 24.9186954498, 9.9674777985, - 25.0526409149, 9.0189504623, 25.1736602783, 8.0555715561, - 25.2813606262, 7.0787811279, 25.3753986359, 6.0900959969, - 25.4554748535, 5.0910949707, 25.5213375092, 4.0834140778, - 25.5727767944, 3.0687332153, 25.6096363068, 2.0487709045, - 25.6317958832, 1.0252718925, 25.6391906738, -0.0000000000, - 25.6317958832, -1.0252718925, 25.6096363068, -2.0487709045, - 25.5727767944, -3.0687332153, 25.5213375092, -4.0834140778, - 25.4554748535, -5.0910949707, 25.3753986359, -6.0900959969, - 25.2813606262, -7.0787811279, 25.1736602783, -8.0555715561, - 25.0526409149, -9.0189504623, 24.9186954498, -9.9674777985, - 24.7722568512, -10.8997936249, 24.6138114929, -11.8146295547, - 24.4438858032, -12.7108211517, 24.2630558014, -13.5873117447, - 24.0719413757, -14.4431657791, 23.8712100983, -15.2775745392, - 23.6615715027, -16.0898685455, 23.4437885284, -16.8795280457, - 23.2186603546, -17.6461811066, 22.9870414734, -18.3896331787, - 22.7498264313, -19.1098537445, 22.5079574585, -19.8070030212, - 22.2624244690, -20.4814300537, 22.0142593384, -21.1336898804, - 21.7645435333, -21.7645435333, 21.5144042969, -22.3749809265, - 21.2650127411, -22.9662132263, 21.0175857544, -23.5396957397, - 20.7733898163, -24.0971317291, 20.5337333679, -24.6404800415, - 21.1508102417, 24.4047813416, 21.3880653381, 23.8559188843, - 21.6304492950, 23.2943286896, 21.8765983582, 22.7180061340, - 22.1251964569, 22.1251964569, 22.3749809265, 21.5144042969, - 22.6247291565, 20.8843650818, 22.8732719421, 20.2340469360, - 23.1194839478, 19.5626392365, 23.3622894287, 18.8695411682, - 23.6006641388, 18.1543560028, 23.8336238861, 17.4168796539, - 24.0602378845, 16.6570873260, 24.2796230316, 15.8751382828, - 24.4909420013, 15.0713491440, 24.6934051514, 14.2461948395, - 24.8862724304, 13.4003000259, 25.0688495636, 12.5344247818, - 25.2404918671, 11.6494579315, 25.4006023407, 10.7464084625, - 25.5486316681, 9.8263969421, 25.6840744019, 8.8906412125, - 25.8064804077, 7.9404559135, 25.9154434204, 6.9772343636, - 26.0106010437, 6.0024461746, 26.0916442871, 5.0176239014, - 26.1583118439, 4.0243558884, 26.2103881836, 3.0242755413, - 26.2477054596, 2.0190541744, 26.2701416016, 1.0103900433, - 26.2776298523, -0.0000000000, 26.2701416016, -1.0103900433, - 26.2477054596, -2.0190541744, 26.2103881836, -3.0242755413, - 26.1583118439, -4.0243558884, 26.0916442871, -5.0176239014, - 26.0106010437, -6.0024461746, 25.9154434204, -6.9772343636, - 25.8064804077, -7.9404559135, 25.6840744019, -8.8906412125, - 25.5486316681, -9.8263969421, 25.4006023407, -10.7464084625, - 25.2404918671, -11.6494579315, 25.0688495636, -12.5344247818, - 24.8862724304, -13.4003000259, 24.6934051514, -14.2461948395, - 24.4909420013, -15.0713491440, 24.2796230316, -15.8751382828, - 24.0602378845, -16.6570873260, 23.8336238861, -17.4168796539, - 23.6006641388, -18.1543560028, 23.3622894287, -18.8695411682, - 23.1194839478, -19.5626392365, 22.8732719421, -20.2340469360, - 22.6247291565, -20.8843650818, 22.3749809265, -21.5144042969, - 22.1251964569, -22.1251964569, 21.8765983582, -22.7180061340, - 21.6304492950, -23.2943286896, 21.3880653381, -23.8559188843, - 21.1508102417, -24.4047813416, 21.7552566528, 24.1725063324, - 21.9887008667, 23.6174926758, 22.2279090881, 23.0511646271, - 22.4714660645, 22.4714660645, 22.7180061340, 21.8765983582, - 22.9662132263, 21.2650127411, 23.2148227692, 20.6353988647, - 23.4626197815, 19.9866752625, 23.7084350586, 19.3179836273, - 23.9511528015, 18.6286735535, 24.1897048950, 17.9182987213, - 24.4230728149, 17.1866073608, 24.6502914429, 16.4335269928, - 24.8704395294, 15.6591653824, 25.0826492310, 14.8637924194, - 25.2861042023, 14.0478353500, 25.4800300598, 13.2118673325, - 25.6637096405, 12.3566007614, 25.8364715576, 11.4828767776, - 25.9976978302, 10.5916547775, 26.1468143463, 9.6840057373, - 26.2833023071, 8.7611007690, 26.4066886902, 7.8242039680, - 26.5165519714, 6.8746614456, 26.6125202179, 5.9138932228, - 26.6942691803, 4.9433832169, 26.7615280151, 3.9646706581, - 26.8140716553, 2.9793412685, 26.8517265320, 1.9890167713, - 26.8743686676, 0.9953470230, 26.8819255829, -0.0000000000, - 26.8743686676, -0.9953470230, 26.8517265320, -1.9890167713, - 26.8140716553, -2.9793412685, 26.7615280151, -3.9646706581, - 26.6942691803, -4.9433832169, 26.6125202179, -5.9138932228, - 26.5165519714, -6.8746614456, 26.4066886902, -7.8242039680, - 26.2833023071, -8.7611007690, 26.1468143463, -9.6840057373, - 25.9976978302, -10.5916547775, 25.8364715576, -11.4828767776, - 25.6637096405, -12.3566007614, 25.4800300598, -13.2118673325, - 25.2861042023, -14.0478353500, 25.0826492310, -14.8637924194, - 24.8704395294, -15.6591653824, 24.6502914429, -16.4335269928, - 24.4230728149, -17.1866073608, 24.1897048950, -17.9182987213, - 23.9511528015, -18.6286735535, 23.7084350586, -19.3179836273, - 23.4626197815, -19.9866752625, 23.2148227692, -20.6353988647, - 22.9662132263, -21.2650127411, 22.7180061340, -21.8765983582, - 22.4714660645, -22.4714660645, 22.2279090881, -23.0511646271, - 21.9887008667, -23.6174926758, 21.7552566528, -24.1725063324, - 22.3487796783, 23.9451198578, 22.5769481659, 23.3832664490, - 22.8115653992, 22.8115653992, 23.0511646271, 22.2279090881, - 23.2943286896, 21.6304492950, 23.5396957397, 21.0175857544, - 23.7859516144, 20.3879585266, 24.0318355560, 19.7404365540, - 24.2761363983, 19.0741081238, 24.5176963806, 18.3882732391, - 24.7554092407, 17.6824359894, 24.9882202148, 16.9562911987, - 25.2151222229, 16.2097206116, 25.4351654053, 15.4427795410, - 25.6474494934, 14.6556854248, 25.8511238098, 13.8488159180, - 26.0453891754, 13.0226945877, 26.2294998169, 12.1779823303, - 26.4027633667, 11.3154697418, 26.5645332336, 10.4360666275, - 26.7142162323, 9.5407915115, 26.8512763977, 8.6307678223, - 26.9752216339, 7.7072062492, 27.0856151581, 6.7714037895, - 27.1820678711, 5.8247289658, 27.2642498016, 4.8686161041, - 27.3318767548, 3.9045536518, 27.3847141266, 2.9340765476, - 27.4225845337, 1.9587560892, 27.4453582764, 0.9801913500, - 27.4529571533, -0.0000000000, 27.4453582764, -0.9801913500, - 27.4225845337, -1.9587560892, 27.3847141266, -2.9340765476, - 27.3318767548, -3.9045536518, 27.2642498016, -4.8686161041, - 27.1820678711, -5.8247289658, 27.0856151581, -6.7714037895, - 26.9752216339, -7.7072062492, 26.8512763977, -8.6307678223, - 26.7142162323, -9.5407915115, 26.5645332336, -10.4360666275, - 26.4027633667, -11.3154697418, 26.2294998169, -12.1779823303, - 26.0453891754, -13.0226945877, 25.8511238098, -13.8488159180, - 25.6474494934, -14.6556854248, 25.4351654053, -15.4427795410, - 25.2151222229, -16.2097206116, 24.9882202148, -16.9562911987, - 24.7554092407, -17.6824359894, 24.5176963806, -18.3882732391, - 24.2761363983, -19.0741081238, 24.0318355560, -19.7404365540, - 23.7859516144, -20.3879585266, 23.5396957397, -21.0175857544, - 23.2943286896, -21.6304492950, 23.0511646271, -22.2279090881, - 22.8115653992, -22.8115653992, 22.5769481659, -23.3832664490, - 22.3487796783, -23.9451198578, 22.9333324432, 23.7241382599, - 23.1547069550, 23.1547069550, 23.3832664490, 22.5769481659, - 23.6174926758, 21.9887008667, 23.8559188843, 21.3880653381, - 24.0971317291, 20.7733898163, 24.3397712708, 20.1432590485, - 24.5825328827, 19.4964923859, 24.8241615295, 18.8321228027, - 25.0634574890, 18.1494007111, 25.2992763519, 17.4477767944, - 25.5305233002, 16.7268943787, 25.7561588287, 15.9865808487, - 25.9751949310, 15.2268390656, 26.1867027283, 14.4478359222, - 26.3897991180, 13.6498956680, 26.5836582184, 12.8334894180, - 26.7675056458, 11.9992265701, 26.9406242371, 11.1478443146, - 27.1023464203, 10.2802000046, 27.2520580292, 9.3972616196, - 27.3892002106, 8.5000963211, 27.5132656097, 7.5898661613, - 27.6238021851, 6.6678142548, 27.7204093933, 5.7352571487, - 27.8027400970, 4.7935757637, 27.8705005646, 3.8442070484, - 27.9234523773, 2.8886330128, 27.9614086151, 1.9283730984, - 27.9842357635, 0.9649736881, 27.9918537140, -0.0000000000, - 27.9842357635, -0.9649736881, 27.9614086151, -1.9283730984, - 27.9234523773, -2.8886330128, 27.8705005646, -3.8442070484, - 27.8027400970, -4.7935757637, 27.7204093933, -5.7352571487, - 27.6238021851, -6.6678142548, 27.5132656097, -7.5898661613, - 27.3892002106, -8.5000963211, 27.2520580292, -9.3972616196, - 27.1023464203, -10.2802000046, 26.9406242371, -11.1478443146, - 26.7675056458, -11.9992265701, 26.5836582184, -12.8334894180, - 26.3897991180, -13.6498956680, 26.1867027283, -14.4478359222, - 25.9751949310, -15.2268390656, 25.7561588287, -15.9865808487, - 25.5305233002, -16.7268943787, 25.2992763519, -17.4477767944, - 25.0634574890, -18.1494007111, 24.8241615295, -18.8321228027, - 24.5825328827, -19.4964923859, 24.3397712708, -20.1432590485, - 24.0971317291, -20.7733898163, 23.8559188843, -21.3880653381, - 23.6174926758, -21.9887008667, 23.3832664490, -22.5769481659, - 23.1547069550, -23.1547069550, 22.9333324432, -23.7241382599, - 23.5111312866, 23.5111312866, 23.7241382599, 22.9333324432, - 23.9451198578, 22.3487796783, 24.1725063324, 21.7552566528, - 24.4047813416, 21.1508102417, 24.6404800415, 20.5337333679, - 24.8781986237, 19.9025573730, 25.1165828705, 19.2560462952, - 25.3543395996, 18.5931816101, 25.5902252197, 17.9131584167, - 25.8230571747, 17.2153701782, 26.0517005920, 16.4994106293, - 26.2750816345, 15.7650489807, 26.4921798706, 15.0122346878, - 26.7020301819, 14.2410821915, 26.9037227631, 13.4518613815, - 27.0964012146, 12.6449871063, 27.2792663574, 11.8210153580, - 27.4515743256, 10.9806299210, 27.6126346588, 10.1246328354, - 27.7618141174, 9.2539377213, 27.8985328674, 8.3695592880, - 28.0222644806, 7.4726042747, 28.1325454712, 6.5642604828, - 28.2289581299, 5.6457915306, 28.3111438751, 4.7185239792, - 28.3788013458, 3.7838401794, 28.4316806793, 2.8431680202, - 28.4695892334, 1.8979727030, 28.4923896790, 0.9497463703, - 28.5000000000, -0.0000000000, 28.4923896790, -0.9497463703, - 28.4695892334, -1.8979727030, 28.4316806793, -2.8431680202, - 28.3788013458, -3.7838401794, 28.3111438751, -4.7185239792, - 28.2289581299, -5.6457915306, 28.1325454712, -6.5642604828, - 28.0222644806, -7.4726042747, 27.8985328674, -8.3695592880, - 27.7618141174, -9.2539377213, 27.6126346588, -10.1246328354, - 27.4515743256, -10.9806299210, 27.2792663574, -11.8210153580, - 27.0964012146, -12.6449871063, 26.9037227631, -13.4518613815, - 26.7020301819, -14.2410821915, 26.4921798706, -15.0122346878, - 26.2750816345, -15.7650489807, 26.0517005920, -16.4994106293, - 25.8230571747, -17.2153701782, 25.5902252197, -17.9131584167, - 25.3543395996, -18.5931816101, 25.1165828705, -19.2560462952, - 24.8781986237, -19.9025573730, 24.6404800415, -20.5337333679, - 24.4047813416, -21.1508102417, 24.1725063324, -21.7552566528, - 23.9451198578, -22.3487796783, 23.7241382599, -22.9333324432, - 23.5111312866, -23.5111312866, -}; - -} /* namespace Pdraw */ - -#endif /* USE_GLES2 */ +/** + * Parrot Drones Awesome Video Viewer Library + * OpenGL ES 2.0 HMD distortion correction + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Extracted from FPVToolbox by Frédéric Bertolus + * https://github.com/niavok/fpvtoolbox + * and translated from Java to C++ + */ + +#ifdef USE_GLES2 + +namespace Pdraw { + +extern const float pdraw_gles2HmdPositionsCockpitglasses[7442] = { + -23.5111312866, 23.5111312866, -23.7241382599, 22.9333324432, + -23.9451198578, 22.3487796783, -24.1725063324, 21.7552566528, + -24.4047813416, 21.1508102417, -24.6404800415, 20.5337333679, + -24.8781986237, 19.9025573730, -25.1165828705, 19.2560462952, + -25.3543395996, 18.5931816101, -25.5902252197, 17.9131584167, + -25.8230571747, 17.2153701782, -26.0517005920, 16.4994106293, + -26.2750816345, 15.7650489807, -26.4921798706, 15.0122346878, + -26.7020301819, 14.2410821915, -26.9037227631, 13.4518613815, + -27.0964012146, 12.6449871063, -27.2792663574, 11.8210153580, + -27.4515743256, 10.9806299210, -27.6126346588, 10.1246328354, + -27.7618141174, 9.2539377213, -27.8985328674, 8.3695592880, + -28.0222644806, 7.4726042747, -28.1325454712, 6.5642604828, + -28.2289581299, 5.6457915306, -28.3111438751, 4.7185239792, + -28.3788013458, 3.7838401794, -28.4316806793, 2.8431680202, + -28.4695892334, 1.8979727030, -28.4923896790, 0.9497463703, + -28.5000000000, -0.0000000000, -28.4923896790, -0.9497463703, + -28.4695892334, -1.8979727030, -28.4316806793, -2.8431680202, + -28.3788013458, -3.7838401794, -28.3111438751, -4.7185239792, + -28.2289581299, -5.6457915306, -28.1325454712, -6.5642604828, + -28.0222644806, -7.4726042747, -27.8985328674, -8.3695592880, + -27.7618141174, -9.2539377213, -27.6126346588, -10.1246328354, + -27.4515743256, -10.9806299210, -27.2792663574, -11.8210153580, + -27.0964012146, -12.6449871063, -26.9037227631, -13.4518613815, + -26.7020301819, -14.2410821915, -26.4921798706, -15.0122346878, + -26.2750816345, -15.7650489807, -26.0517005920, -16.4994106293, + -25.8230571747, -17.2153701782, -25.5902252197, -17.9131584167, + -25.3543395996, -18.5931816101, -25.1165828705, -19.2560462952, + -24.8781986237, -19.9025573730, -24.6404800415, -20.5337333679, + -24.4047813416, -21.1508102417, -24.1725063324, -21.7552566528, + -23.9451198578, -22.3487796783, -23.7241382599, -22.9333324432, + -23.5111312866, -23.5111312866, -22.9333324432, 23.7241382599, + -23.1547069550, 23.1547069550, -23.3832664490, 22.5769481659, + -23.6174926758, 21.9887008667, -23.8559188843, 21.3880653381, + -24.0971317291, 20.7733898163, -24.3397712708, 20.1432590485, + -24.5825328827, 19.4964923859, -24.8241615295, 18.8321228027, + -25.0634574890, 18.1494007111, -25.2992763519, 17.4477767944, + -25.5305233002, 16.7268943787, -25.7561588287, 15.9865808487, + -25.9751949310, 15.2268390656, -26.1867027283, 14.4478359222, + -26.3897991180, 13.6498956680, -26.5836582184, 12.8334894180, + -26.7675056458, 11.9992265701, -26.9406242371, 11.1478443146, + -27.1023464203, 10.2802000046, -27.2520580292, 9.3972616196, + -27.3892002106, 8.5000963211, -27.5132656097, 7.5898661613, + -27.6238021851, 6.6678142548, -27.7204093933, 5.7352571487, + -27.8027400970, 4.7935757637, -27.8705005646, 3.8442070484, + -27.9234523773, 2.8886330128, -27.9614086151, 1.9283730984, + -27.9842357635, 0.9649736881, -27.9918537140, -0.0000000000, + -27.9842357635, -0.9649736881, -27.9614086151, -1.9283730984, + -27.9234523773, -2.8886330128, -27.8705005646, -3.8442070484, + -27.8027400970, -4.7935757637, -27.7204093933, -5.7352571487, + -27.6238021851, -6.6678142548, -27.5132656097, -7.5898661613, + -27.3892002106, -8.5000963211, -27.2520580292, -9.3972616196, + -27.1023464203, -10.2802000046, -26.9406242371, -11.1478443146, + -26.7675056458, -11.9992265701, -26.5836582184, -12.8334894180, + -26.3897991180, -13.6498956680, -26.1867027283, -14.4478359222, + -25.9751949310, -15.2268390656, -25.7561588287, -15.9865808487, + -25.5305233002, -16.7268943787, -25.2992763519, -17.4477767944, + -25.0634574890, -18.1494007111, -24.8241615295, -18.8321228027, + -24.5825328827, -19.4964923859, -24.3397712708, -20.1432590485, + -24.0971317291, -20.7733898163, -23.8559188843, -21.3880653381, + -23.6174926758, -21.9887008667, -23.3832664490, -22.5769481659, + -23.1547069550, -23.1547069550, -22.9333324432, -23.7241382599, + -22.3487796783, 23.9451198578, -22.5769481659, 23.3832664490, + -22.8115653992, 22.8115653992, -23.0511646271, 22.2279090881, + -23.2943286896, 21.6304492950, -23.5396957397, 21.0175857544, + -23.7859516144, 20.3879585266, -24.0318355560, 19.7404365540, + -24.2761363983, 19.0741081238, -24.5176963806, 18.3882732391, + -24.7554092407, 17.6824359894, -24.9882202148, 16.9562911987, + -25.2151222229, 16.2097206116, -25.4351654053, 15.4427795410, + -25.6474494934, 14.6556854248, -25.8511238098, 13.8488159180, + -26.0453891754, 13.0226945877, -26.2294998169, 12.1779823303, + -26.4027633667, 11.3154697418, -26.5645332336, 10.4360666275, + -26.7142162323, 9.5407915115, -26.8512763977, 8.6307678223, + -26.9752216339, 7.7072062492, -27.0856151581, 6.7714037895, + -27.1820678711, 5.8247289658, -27.2642498016, 4.8686161041, + -27.3318767548, 3.9045536518, -27.3847141266, 2.9340765476, + -27.4225845337, 1.9587560892, -27.4453582764, 0.9801913500, + -27.4529571533, -0.0000000000, -27.4453582764, -0.9801913500, + -27.4225845337, -1.9587560892, -27.3847141266, -2.9340765476, + -27.3318767548, -3.9045536518, -27.2642498016, -4.8686161041, + -27.1820678711, -5.8247289658, -27.0856151581, -6.7714037895, + -26.9752216339, -7.7072062492, -26.8512763977, -8.6307678223, + -26.7142162323, -9.5407915115, -26.5645332336, -10.4360666275, + -26.4027633667, -11.3154697418, -26.2294998169, -12.1779823303, + -26.0453891754, -13.0226945877, -25.8511238098, -13.8488159180, + -25.6474494934, -14.6556854248, -25.4351654053, -15.4427795410, + -25.2151222229, -16.2097206116, -24.9882202148, -16.9562911987, + -24.7554092407, -17.6824359894, -24.5176963806, -18.3882732391, + -24.2761363983, -19.0741081238, -24.0318355560, -19.7404365540, + -23.7859516144, -20.3879585266, -23.5396957397, -21.0175857544, + -23.2943286896, -21.6304492950, -23.0511646271, -22.2279090881, + -22.8115653992, -22.8115653992, -22.5769481659, -23.3832664490, + -22.3487796783, -23.9451198578, -21.7552566528, 24.1725063324, + -21.9887008667, 23.6174926758, -22.2279090881, 23.0511646271, + -22.4714660645, 22.4714660645, -22.7180061340, 21.8765983582, + -22.9662132263, 21.2650127411, -23.2148227692, 20.6353988647, + -23.4626197815, 19.9866752625, -23.7084350586, 19.3179836273, + -23.9511528015, 18.6286735535, -24.1897048950, 17.9182987213, + -24.4230728149, 17.1866073608, -24.6502914429, 16.4335269928, + -24.8704395294, 15.6591653824, -25.0826492310, 14.8637924194, + -25.2861042023, 14.0478353500, -25.4800300598, 13.2118673325, + -25.6637096405, 12.3566007614, -25.8364715576, 11.4828767776, + -25.9976978302, 10.5916547775, -26.1468143463, 9.6840057373, + -26.2833023071, 8.7611007690, -26.4066886902, 7.8242039680, + -26.5165519714, 6.8746614456, -26.6125202179, 5.9138932228, + -26.6942691803, 4.9433832169, -26.7615280151, 3.9646706581, + -26.8140716553, 2.9793412685, -26.8517265320, 1.9890167713, + -26.8743686676, 0.9953470230, -26.8819255829, -0.0000000000, + -26.8743686676, -0.9953470230, -26.8517265320, -1.9890167713, + -26.8140716553, -2.9793412685, -26.7615280151, -3.9646706581, + -26.6942691803, -4.9433832169, -26.6125202179, -5.9138932228, + -26.5165519714, -6.8746614456, -26.4066886902, -7.8242039680, + -26.2833023071, -8.7611007690, -26.1468143463, -9.6840057373, + -25.9976978302, -10.5916547775, -25.8364715576, -11.4828767776, + -25.6637096405, -12.3566007614, -25.4800300598, -13.2118673325, + -25.2861042023, -14.0478353500, -25.0826492310, -14.8637924194, + -24.8704395294, -15.6591653824, -24.6502914429, -16.4335269928, + -24.4230728149, -17.1866073608, -24.1897048950, -17.9182987213, + -23.9511528015, -18.6286735535, -23.7084350586, -19.3179836273, + -23.4626197815, -19.9866752625, -23.2148227692, -20.6353988647, + -22.9662132263, -21.2650127411, -22.7180061340, -21.8765983582, + -22.4714660645, -22.4714660645, -22.2279090881, -23.0511646271, + -21.9887008667, -23.6174926758, -21.7552566528, -24.1725063324, + -21.1508102417, 24.4047813416, -21.3880653381, 23.8559188843, + -21.6304492950, 23.2943286896, -21.8765983582, 22.7180061340, + -22.1251964569, 22.1251964569, -22.3749809265, 21.5144042969, + -22.6247291565, 20.8843650818, -22.8732719421, 20.2340469360, + -23.1194839478, 19.5626392365, -23.3622894287, 18.8695411682, + -23.6006641388, 18.1543560028, -23.8336238861, 17.4168796539, + -24.0602378845, 16.6570873260, -24.2796230316, 15.8751382828, + -24.4909420013, 15.0713491440, -24.6934051514, 14.2461948395, + -24.8862724304, 13.4003000259, -25.0688495636, 12.5344247818, + -25.2404918671, 11.6494579315, -25.4006023407, 10.7464084625, + -25.5486316681, 9.8263969421, -25.6840744019, 8.8906412125, + -25.8064804077, 7.9404559135, -25.9154434204, 6.9772343636, + -26.0106010437, 6.0024461746, -26.0916442871, 5.0176239014, + -26.1583118439, 4.0243558884, -26.2103881836, 3.0242755413, + -26.2477054596, 2.0190541744, -26.2701416016, 1.0103900433, + -26.2776298523, -0.0000000000, -26.2701416016, -1.0103900433, + -26.2477054596, -2.0190541744, -26.2103881836, -3.0242755413, + -26.1583118439, -4.0243558884, -26.0916442871, -5.0176239014, + -26.0106010437, -6.0024461746, -25.9154434204, -6.9772343636, + -25.8064804077, -7.9404559135, -25.6840744019, -8.8906412125, + -25.5486316681, -9.8263969421, -25.4006023407, -10.7464084625, + -25.2404918671, -11.6494579315, -25.0688495636, -12.5344247818, + -24.8862724304, -13.4003000259, -24.6934051514, -14.2461948395, + -24.4909420013, -15.0713491440, -24.2796230316, -15.8751382828, + -24.0602378845, -16.6570873260, -23.8336238861, -17.4168796539, + -23.6006641388, -18.1543560028, -23.3622894287, -18.8695411682, + -23.1194839478, -19.5626392365, -22.8732719421, -20.2340469360, + -22.6247291565, -20.8843650818, -22.3749809265, -21.5144042969, + -22.1251964569, -22.1251964569, -21.8765983582, -22.7180061340, + -21.6304492950, -23.2943286896, -21.3880653381, -23.8559188843, + -21.1508102417, -24.4047813416, -20.5337333679, 24.6404800415, + -20.7733898163, 24.0971317291, -21.0175857544, 23.5396957397, + -21.2650127411, 22.9662132263, -21.5144042969, 22.3749809265, + -21.7645435333, 21.7645435333, -22.0142593384, 21.1336898804, + -22.2624244690, 20.4814300537, -22.5079574585, 19.8070030212, + -22.7498264313, 19.1098537445, -22.9870414734, 18.3896331787, + -23.2186603546, 17.6461811066, -23.4437885284, 16.8795280457, + -23.6615715027, 16.0898685455, -23.8712100983, 15.2775745392, + -24.0719413757, 14.4431657791, -24.2630558014, 13.5873117447, + -24.4438858032, 12.7108211517, -24.6138114929, 11.8146295547, + -24.7722568512, 10.8997936249, -24.9186954498, 9.9674777985, + -25.0526409149, 9.0189504623, -25.1736602783, 8.0555715561, + -25.2813606262, 7.0787811279, -25.3753986359, 6.0900959969, + -25.4554748535, 5.0910949707, -25.5213375092, 4.0834140778, + -25.5727767944, 3.0687332153, -25.6096363068, 2.0487709045, + -25.6317958832, 1.0252718925, -25.6391906738, -0.0000000000, + -25.6317958832, -1.0252718925, -25.6096363068, -2.0487709045, + -25.5727767944, -3.0687332153, -25.5213375092, -4.0834140778, + -25.4554748535, -5.0910949707, -25.3753986359, -6.0900959969, + -25.2813606262, -7.0787811279, -25.1736602783, -8.0555715561, + -25.0526409149, -9.0189504623, -24.9186954498, -9.9674777985, + -24.7722568512, -10.8997936249, -24.6138114929, -11.8146295547, + -24.4438858032, -12.7108211517, -24.2630558014, -13.5873117447, + -24.0719413757, -14.4431657791, -23.8712100983, -15.2775745392, + -23.6615715027, -16.0898685455, -23.4437885284, -16.8795280457, + -23.2186603546, -17.6461811066, -22.9870414734, -18.3896331787, + -22.7498264313, -19.1098537445, -22.5079574585, -19.8070030212, + -22.2624244690, -20.4814300537, -22.0142593384, -21.1336898804, + -21.7645435333, -21.7645435333, -21.5144042969, -22.3749809265, + -21.2650127411, -22.9662132263, -21.0175857544, -23.5396957397, + -20.7733898163, -24.0971317291, -20.5337333679, -24.6404800415, + -19.9025573730, 24.8781986237, -20.1432590485, 24.3397712708, + -20.3879585266, 23.7859516144, -20.6353988647, 23.2148227692, + -20.8843650818, 22.6247291565, -21.1336898804, 22.0142593384, + -21.3822460175, 21.3822460175, -21.6289520264, 20.7277450562, + -21.8727722168, 20.0500411987, -22.1127147675, 19.3486251831, + -22.3478298187, 18.6231899261, -22.5772113800, 17.8736248016, + -22.7999992371, 17.1000003815, -23.0153789520, 16.3025608063, + -23.2225780487, 15.4817190170, -23.4208679199, 14.6380424500, + -23.6095619202, 13.7722444534, -23.7880249023, 12.8851795197, + -23.9556579590, 11.9778289795, -24.1119098663, 11.0512914658, + -24.2562732697, 10.1067810059, -24.3882865906, 9.1456069946, + -24.5075283051, 8.1691761017, -24.6136226654, 7.1789731979, + -24.7062416077, 6.1765604019, -24.7850971222, 5.1635618210, + -24.8499469757, 4.1416578293, -24.9005908966, 3.1125738621, + -24.9368743896, 2.0780727863, -24.9586887360, 1.0399453640, + -24.9659690857, -0.0000000000, -24.9586887360, -1.0399453640, + -24.9368743896, -2.0780727863, -24.9005908966, -3.1125738621, + -24.8499469757, -4.1416578293, -24.7850971222, -5.1635618210, + -24.7062416077, -6.1765604019, -24.6136226654, -7.1789731979, + -24.5075283051, -8.1691761017, -24.3882865906, -9.1456069946, + -24.2562732697, -10.1067810059, -24.1119098663, -11.0512914658, + -23.9556579590, -11.9778289795, -23.7880249023, -12.8851795197, + -23.6095619202, -13.7722444534, -23.4208679199, -14.6380424500, + -23.2225780487, -15.4817190170, -23.0153789520, -16.3025608063, + -22.7999992371, -17.1000003815, -22.5772113800, -17.8736248016, + -22.3478298187, -18.6231899261, -22.1127147675, -19.3486251831, + -21.8727722168, -20.0500411987, -21.6289520264, -20.7277450562, + -21.3822460175, -21.3822460175, -21.1336898804, -22.0142593384, + -20.8843650818, -22.6247291565, -20.6353988647, -23.2148227692, + -20.3879585266, -23.7859516144, -20.1432590485, -24.3397712708, + -19.9025573730, -24.8781986237, -19.2560462952, 25.1165828705, + -19.4964923859, 24.5825328827, -19.7404365540, 24.0318355560, + -19.9866752625, 23.4626197815, -20.2340469360, 22.8732719421, + -20.4814300537, 22.2624244690, -20.7277450562, 21.6289520264, + -20.9719581604, 20.9719581604, -21.2130737305, 20.2907657623, + -21.4501399994, 19.5849094391, -21.6822490692, 18.8541297913, + -21.9085330963, 18.0983524323, -22.1281681061, 17.3176956177, + -22.3403701782, 16.5124473572, -22.5444011688, 15.6830615997, + -22.7395629883, 14.8301496506, -22.9251995087, 13.9544687271, + -23.1006965637, 13.0569162369, -23.2654857635, 12.1385145187, + -23.4190368652, 11.2004089355, -23.5608654022, 10.2438545227, + -23.6905231476, 9.2702045441, -23.8076114655, 8.2809085846, + -23.9117717743, 7.2774958611, -24.0026855469, 6.2615699768, + -24.0800762177, 5.2347993851, -24.1437149048, 4.1989068985, + -24.1934070587, 3.1556618214, -24.2290077209, 2.1068701744, + -24.2504119873, 1.0543657541, -24.2575531006, -0.0000000000, + -24.2504119873, -1.0543657541, -24.2290077209, -2.1068701744, + -24.1934070587, -3.1556618214, -24.1437149048, -4.1989068985, + -24.0800762177, -5.2347993851, -24.0026855469, -6.2615699768, + -23.9117717743, -7.2774958611, -23.8076114655, -8.2809085846, + -23.6905231476, -9.2702045441, -23.5608654022, -10.2438545227, + -23.4190368652, -11.2004089355, -23.2654857635, -12.1385145187, + -23.1006965637, -13.0569162369, -22.9251995087, -13.9544687271, + -22.7395629883, -14.8301496506, -22.5444011688, -15.6830615997, + -22.3403701782, -16.5124473572, -22.1281681061, -17.3176956177, + -21.9085330963, -18.0983524323, -21.6822490692, -18.8541297913, + -21.4501399994, -19.5849094391, -21.2130737305, -20.2907657623, + -20.9719581604, -20.9719581604, -20.7277450562, -21.6289520264, + -20.4814300537, -22.2624244690, -20.2340469360, -22.8732719421, + -19.9866752625, -23.4626197815, -19.7404365540, -24.0318355560, + -19.4964923859, -24.5825328827, -19.2560462952, -25.1165828705, + -18.5931816101, 25.3543395996, -18.8321228027, 24.8241615295, + -19.0741081238, 24.2761363983, -19.3179836273, 23.7084350586, + -19.5626392365, 23.1194839478, -19.8070030212, 22.5079574585, + -20.0500411987, 21.8727722168, -20.2907657623, 21.2130737305, + -20.5282230377, 20.5282230377, -20.7615051270, 19.8178005219, + -20.9897422791, 19.0815830231, -21.2121028900, 18.3195438385, + -21.4278011322, 17.5318374634, -21.6360874176, 16.7187938690, + -21.8362541199, 15.8809118271, -22.0276317596, 15.0188398361, + -22.2095966339, 14.1333789825, -22.3815593719, 13.2254667282, + -22.5429763794, 12.2961692810, -22.6933403015, 11.3466701508, + -22.8321857452, 10.3782663345, -22.9590892792, 9.3923549652, + -23.0736675262, 8.3904247284, -23.1755733490, 7.3740458488, + -23.2645053864, 6.3448653221, -23.3402004242, 5.3045911789, + -23.4024372101, 4.2549886703, -23.4510307312, 3.1978678703, + -23.4858417511, 2.1350765228, -23.5067691803, 1.0684895515, + -23.5137519836, -0.0000000000, -23.5067691803, -1.0684895515, + -23.4858417511, -2.1350765228, -23.4510307312, -3.1978678703, + -23.4024372101, -4.2549886703, -23.3402004242, -5.3045911789, + -23.2645053864, -6.3448653221, -23.1755733490, -7.3740458488, + -23.0736675262, -8.3904247284, -22.9590892792, -9.3923549652, + -22.8321857452, -10.3782663345, -22.6933403015, -11.3466701508, + -22.5429763794, -12.2961692810, -22.3815593719, -13.2254667282, + -22.2095966339, -14.1333789825, -22.0276317596, -15.0188398361, + -21.8362541199, -15.8809118271, -21.6360874176, -16.7187938690, + -21.4278011322, -17.5318374634, -21.2121028900, -18.3195438385, + -20.9897422791, -19.0815830231, -20.7615051270, -19.8178005219, + -20.5282230377, -20.5282230377, -20.2907657623, -21.2130737305, + -20.0500411987, -21.8727722168, -19.8070030212, -22.5079574585, + -19.5626392365, -23.1194839478, -19.3179836273, -23.7084350586, + -19.0741081238, -24.2761363983, -18.8321228027, -24.8241615295, + -18.5931816101, -25.3543395996, -17.9131584167, 25.5902252197, + -18.1494007111, 25.0634574890, -18.3882732391, 24.5176963806, + -18.6286735535, 23.9511528015, -18.8695411682, 23.3622894287, + -19.1098537445, 22.7498264313, -19.3486251831, 22.1127147675, + -19.5849094391, 21.4501399994, -19.8178005219, 20.7615051270, + -20.0464286804, 20.0464286804, -20.2699642181, 19.3047275543, + -20.4876136780, 18.5364131927, -20.6986255646, 17.7416801453, + -20.9022865295, 16.9208984375, -21.0979213715, 16.0746059418, + -21.2848892212, 15.2034921646, -21.4625949860, 14.3083972931, + -21.6304779053, 13.3902959824, -21.7880172729, 12.4502954483, + -21.9347286224, 11.4896192551, -22.0701675415, 10.5096044540, + -22.1939334869, 9.5116853714, -22.3056526184, 8.4973917007, + -22.4050025940, 7.4683341980, -22.4916915894, 6.4261975288, + -22.5654678345, 5.3727307320, -22.6261196136, 4.3097372055, + -22.6734752655, 3.2390677929, -22.7073955536, 2.1626091003, + -22.7277870178, 1.0822755098, -22.7345905304, -0.0000000000, + -22.7277870178, -1.0822755098, -22.7073955536, -2.1626091003, + -22.6734752655, -3.2390677929, -22.6261196136, -4.3097372055, + -22.5654678345, -5.3727307320, -22.4916915894, -6.4261975288, + -22.4050025940, -7.4683341980, -22.3056526184, -8.4973917007, + -22.1939334869, -9.5116853714, -22.0701675415, -10.5096044540, + -21.9347286224, -11.4896192551, -21.7880172729, -12.4502954483, + -21.6304779053, -13.3902959824, -21.4625949860, -14.3083972931, + -21.2848892212, -15.2034921646, -21.0979213715, -16.0746059418, + -20.9022865295, -16.9208984375, -20.6986255646, -17.7416801453, + -20.4876136780, -18.5364131927, -20.2699642181, -19.3047275543, + -20.0464286804, -20.0464286804, -19.8178005219, -20.7615051270, + -19.5849094391, -21.4501399994, -19.3486251831, -22.1127147675, + -19.1098537445, -22.7498264313, -18.8695411682, -23.3622894287, + -18.6286735535, -23.9511528015, -18.3882732391, -24.5176963806, + -18.1494007111, -25.0634574890, -17.9131584167, -25.5902252197, + -17.2153701782, 25.8230571747, -17.4477767944, 25.2992763519, + -17.6824359894, 24.7554092407, -17.9182987213, 24.1897048950, + -18.1543560028, 23.6006641388, -18.3896331787, 22.9870414734, + -18.6231899261, 22.3478298187, -18.8541297913, 21.6822490692, + -19.0815830231, 20.9897422791, -19.3047275543, 20.2699642181, + -19.5227680206, 19.5227680206, -19.7349548340, 18.7482070923, + -19.9405689240, 17.9465122223, -20.1389274597, 17.1180896759, + -20.3293914795, 16.2635135651, -20.5113525391, 15.3835144043, + -20.6842403412, 14.4789676666, -20.8475208282, 13.5508880615, + -21.0006980896, 12.6004190445, -21.1433124542, 11.6288223267, + -21.2749423981, 10.6374711990, -21.3952007294, 9.6278400421, + -21.5037364960, 8.6014947891, -21.6002407074, 7.5600843430, + -21.6844348907, 6.5053300858, -21.7560806274, 5.4390201569, + -21.8149738312, 4.3629951477, -21.8609542847, 3.2791430950, + -21.8938865662, 2.1893887520, -21.9136848450, 1.0956841707, + -21.9202899933, -0.0000000000, -21.9136848450, -1.0956841707, + -21.8938865662, -2.1893887520, -21.8609542847, -3.2791430950, + -21.8149738312, -4.3629951477, -21.7560806274, -5.4390201569, + -21.6844348907, -6.5053300858, -21.6002407074, -7.5600843430, + -21.5037364960, -8.6014947891, -21.3952007294, -9.6278400421, + -21.2749423981, -10.6374711990, -21.1433124542, -11.6288223267, + -21.0006980896, -12.6004190445, -20.8475208282, -13.5508880615, + -20.6842403412, -14.4789676666, -20.5113525391, -15.3835144043, + -20.3293914795, -16.2635135651, -20.1389274597, -17.1180896759, + -19.9405689240, -17.9465122223, -19.7349548340, -18.7482070923, + -19.5227680206, -19.5227680206, -19.3047275543, -20.2699642181, + -19.0815830231, -20.9897422791, -18.8541297913, -21.6822490692, + -18.6231899261, -22.3478298187, -18.3896331787, -22.9870414734, + -18.1543560028, -23.6006641388, -17.9182987213, -24.1897048950, + -17.6824359894, -24.7554092407, -17.4477767944, -25.2992763519, + -17.2153701782, -25.8230571747, -16.4994106293, 26.0517005920, + -16.7268943787, 25.5305233002, -16.9562911987, 24.9882202148, + -17.1866073608, 24.4230728149, -17.4168796539, 23.8336238861, + -17.6461811066, 23.2186603546, -17.8736248016, 22.5772113800, + -18.0983524323, 21.9085330963, -18.3195438385, 21.2121028900, + -18.5364131927, 20.4876136780, -18.7482070923, 19.7349548340, + -18.9542121887, 18.9542121887, -19.1537456512, 18.1456527710, + -19.3461608887, 17.3097229004, -19.5308494568, 16.4470310211, + -19.7072315216, 15.5583400726, -19.8747653961, 14.6445646286, + -20.0329475403, 13.7067537308, -20.1813049316, 12.7460880280, + -20.3194007874, 11.7638635635, -20.4468326569, 10.7614917755, + -20.5632362366, 9.7404804230, -20.6682758331, 8.7024316788, + -20.7616577148, 7.6490316391, -20.8431167603, 6.5820369720, + -20.9124298096, 5.5032711029, -20.9694004059, 4.4146108627, + -21.0138759613, 3.3179802895, -21.0457305908, 2.2153401375, + -21.0648784637, 1.1086778641, -21.0712661743, -0.0000000000, + -21.0648784637, -1.1086778641, -21.0457305908, -2.2153401375, + -21.0138759613, -3.3179802895, -20.9694004059, -4.4146108627, + -20.9124298096, -5.5032711029, -20.8431167603, -6.5820369720, + -20.7616577148, -7.6490316391, -20.6682758331, -8.7024316788, + -20.5632362366, -9.7404804230, -20.4468326569, -10.7614917755, + -20.3194007874, -11.7638635635, -20.1813049316, -12.7460880280, + -20.0329475403, -13.7067537308, -19.8747653961, -14.6445646286, + -19.7072315216, -15.5583400726, -19.5308494568, -16.4470310211, + -19.3461608887, -17.3097229004, -19.1537456512, -18.1456527710, + -18.9542121887, -18.9542121887, -18.7482070923, -19.7349548340, + -18.5364131927, -20.4876136780, -18.3195438385, -21.2121028900, + -18.0983524323, -21.9085330963, -17.8736248016, -22.5772113800, + -17.6461811066, -23.2186603546, -17.4168796539, -23.8336238861, + -17.1866073608, -24.4230728149, -16.9562911987, -24.9882202148, + -16.7268943787, -25.5305233002, -16.4994106293, -26.0517005920, + -15.7650489807, 26.2750816345, -15.9865808487, 25.7561588287, + -16.2097206116, 25.2151222229, -16.4335269928, 24.6502914429, + -16.6570873260, 24.0602378845, -16.8795280457, 23.4437885284, + -17.1000003815, 22.7999992371, -17.3176956177, 22.1281681061, + -17.5318374634, 21.4278011322, -17.7416801453, 20.6986255646, + -17.9465122223, 19.9405689240, -18.1456527710, 19.1537456512, + -18.3384609222, 18.3384609222, -18.5243206024, 17.4951934814, + -18.7026557922, 16.6245822906, -18.8729190826, 15.7274322510, + -19.0345954895, 14.8046855927, -19.1872081757, 13.8574275970, + -19.3303070068, 12.8868713379, -19.4634819031, 11.8943500519, + -19.5863494873, 10.8813056946, -19.6985645294, 9.8492822647, + -19.7998123169, 8.7999162674, -19.8898086548, 7.7349257469, + -19.9683074951, 6.6561026573, -20.0350952148, 5.5653042793, + -20.0899868011, 4.4644412994, -20.1328353882, 3.3554723263, + -20.1635227203, 2.2403914928, -20.1819686890, 1.1212204695, + -20.1881237030, -0.0000000000, -20.1819686890, -1.1212204695, + -20.1635227203, -2.2403914928, -20.1328353882, -3.3554723263, + -20.0899868011, -4.4644412994, -20.0350952148, -5.5653042793, + -19.9683074951, -6.6561026573, -19.8898086548, -7.7349257469, + -19.7998123169, -8.7999162674, -19.6985645294, -9.8492822647, + -19.5863494873, -10.8813056946, -19.4634819031, -11.8943500519, + -19.3303070068, -12.8868713379, -19.1872081757, -13.8574275970, + -19.0345954895, -14.8046855927, -18.8729190826, -15.7274322510, + -18.7026557922, -16.6245822906, -18.5243206024, -17.4951934814, + -18.3384609222, -18.3384609222, -18.1456527710, -19.1537456512, + -17.9465122223, -19.9405689240, -17.7416801453, -20.6986255646, + -17.5318374634, -21.4278011322, -17.3176956177, -22.1281681061, + -17.1000003815, -22.7999992371, -16.8795280457, -23.4437885284, + -16.6570873260, -24.0602378845, -16.4335269928, -24.6502914429, + -16.2097206116, -25.2151222229, -15.9865808487, -25.7561588287, + -15.7650489807, -26.2750816345, -15.0122346878, 26.4921798706, + -15.2268390656, 25.9751949310, -15.4427795410, 25.4351654053, + -15.6591653824, 24.8704395294, -15.8751382828, 24.2796230316, + -16.0898685455, 23.6615715027, -16.3025608063, 23.0153789520, + -16.5124473572, 22.3403701782, -16.7187938690, 21.6360874176, + -16.9208984375, 20.9022865295, -17.1180896759, 20.1389274597, + -17.3097229004, 19.3461608887, -17.4951934814, 18.5243206024, + -17.6739177704, 17.6739177704, -17.8453540802, 16.7956275940, + -18.0089836121, 15.8902797699, -18.1643218994, 14.9588537216, + -18.3109169006, 14.0024662018, -18.4483470917, 13.0223627090, + -18.5762195587, 12.0199069977, -18.6941757202, 10.9965744019, + -18.8018894196, 9.9539413452, -18.8990612030, 8.8936758041, + -18.9854259491, 7.8175282478, -19.0607490540, 6.7273230553, + -19.1248283386, 5.6249494553, -19.1774902344, 4.5123505592, + -19.2185974121, 3.3915169239, -19.2480354309, 2.2644748688, + -19.2657318115, 1.1332782507, -19.2716350555, -0.0000000000, + -19.2657318115, -1.1332782507, -19.2480354309, -2.2644748688, + -19.2185974121, -3.3915169239, -19.1774902344, -4.5123505592, + -19.1248283386, -5.6249494553, -19.0607490540, -6.7273230553, + -18.9854259491, -7.8175282478, -18.8990612030, -8.8936758041, + -18.8018894196, -9.9539413452, -18.6941757202, -10.9965744019, + -18.5762195587, -12.0199069977, -18.4483470917, -13.0223627090, + -18.3109169006, -14.0024662018, -18.1643218994, -14.9588537216, + -18.0089836121, -15.8902797699, -17.8453540802, -16.7956275940, + -17.6739177704, -17.6739177704, -17.4951934814, -18.5243206024, + -17.3097229004, -19.3461608887, -17.1180896759, -20.1389274597, + -16.9208984375, -20.9022865295, -16.7187938690, -21.6360874176, + -16.5124473572, -22.3403701782, -16.3025608063, -23.0153789520, + -16.0898685455, -23.6615715027, -15.8751382828, -24.2796230316, + -15.6591653824, -24.8704395294, -15.4427795410, -25.4351654053, + -15.2268390656, -25.9751949310, -15.0122346878, -26.4921798706, + -14.2410821915, 26.7020301819, -14.4478359222, 26.1867027283, + -14.6556854248, 25.6474494934, -14.8637924194, 25.0826492310, + -15.0713491440, 24.4909420013, -15.2775745392, 23.8712100983, + -15.4817190170, 23.2225780487, -15.6830615997, 22.5444011688, + -15.8809118271, 21.8362541199, -16.0746059418, 21.0979213715, + -16.2635135651, 20.3293914795, -16.4470310211, 19.5308494568, + -16.6245822906, 18.7026557922, -16.7956275940, 17.8453540802, + -16.9596481323, 16.9596481323, -17.1161594391, 16.0464000702, + -17.2647075653, 15.1066188812, -17.4048633575, 14.1414518356, + -17.5362319946, 13.1521739960, -17.6584434509, 12.1401796341, + -17.7711601257, 11.1069755554, -17.8740749359, 10.0541667938, + -17.9669055939, 8.9834527969, -18.0494022369, 7.8966135979, + -18.1213474274, 6.7955055237, -18.1825466156, 5.6820459366, + -18.2328395844, 4.5582098961, -18.2720947266, 3.4260177612, + -18.3002071381, 2.2875258923, -18.3171043396, 1.1448190212, + -18.3227405548, -0.0000000000, -18.3171043396, -1.1448190212, + -18.3002071381, -2.2875258923, -18.2720947266, -3.4260177612, + -18.2328395844, -4.5582098961, -18.1825466156, -5.6820459366, + -18.1213474274, -6.7955055237, -18.0494022369, -7.8966135979, + -17.9669055939, -8.9834527969, -17.8740749359, -10.0541667938, + -17.7711601257, -11.1069755554, -17.6584434509, -12.1401796341, + -17.5362319946, -13.1521739960, -17.4048633575, -14.1414518356, + -17.2647075653, -15.1066188812, -17.1161594391, -16.0464000702, + -16.9596481323, -16.9596481323, -16.7956275940, -17.8453540802, + -16.6245822906, -18.7026557922, -16.4470310211, -19.5308494568, + -16.2635135651, -20.3293914795, -16.0746059418, -21.0979213715, + -15.8809118271, -21.8362541199, -15.6830615997, -22.5444011688, + -15.4817190170, -23.2225780487, -15.2775745392, -23.8712100983, + -15.0713491440, -24.4909420013, -14.8637924194, -25.0826492310, + -14.6556854248, -25.6474494934, -14.4478359222, -26.1867027283, + -14.2410821915, -26.7020301819, -13.4518613815, 26.9037227631, + -13.6498956680, 26.3897991180, -13.8488159180, 25.8511238098, + -14.0478353500, 25.2861042023, -14.2461948395, 24.6934051514, + -14.4431657791, 24.0719413757, -14.6380424500, 23.4208679199, + -14.8301496506, 22.7395629883, -15.0188398361, 22.0276317596, + -15.2034921646, 21.2848892212, -15.3835144043, 20.5113525391, + -15.5583400726, 19.7072315216, -15.7274322510, 18.8729190826, + -15.8902797699, 18.0089836121, -16.0464000702, 17.1161594391, + -16.1953392029, 16.1953392029, -16.3366680145, 15.2475576401, + -16.4699878693, 14.2739896774, -16.5949268341, 13.2759418488, + -16.7111396790, 12.2548351288, -16.8183078766, 11.2122049332, + -16.9161434174, 10.1496858597, -17.0043830872, 9.0690040588, + -17.0827941895, 7.9719705582, -17.1511688232, 6.8604674339, + -17.2093257904, 5.7364420891, -17.2571182251, 4.6018981934, + -17.2944164276, 3.4588835239, -17.3211288452, 2.3094837666, + -17.3371829987, 1.1558121443, -17.3425388336, -0.0000000000, + -17.3371829987, -1.1558121443, -17.3211288452, -2.3094837666, + -17.2944164276, -3.4588835239, -17.2571182251, -4.6018981934, + -17.2093257904, -5.7364420891, -17.1511688232, -6.8604674339, + -17.0827941895, -7.9719705582, -17.0043830872, -9.0690040588, + -16.9161434174, -10.1496858597, -16.8183078766, -11.2122049332, + -16.7111396790, -12.2548351288, -16.5949268341, -13.2759418488, + -16.4699878693, -14.2739896774, -16.3366680145, -15.2475576401, + -16.1953392029, -16.1953392029, -16.0464000702, -17.1161594391, + -15.8902797699, -18.0089836121, -15.7274322510, -18.8729190826, + -15.5583400726, -19.7072315216, -15.3835144043, -20.5113525391, + -15.2034921646, -21.2848892212, -15.0188398361, -22.0276317596, + -14.8301496506, -22.7395629883, -14.6380424500, -23.4208679199, + -14.4431657791, -24.0719413757, -14.2461948395, -24.6934051514, + -14.0478353500, -25.2861042023, -13.8488159180, -25.8511238098, + -13.6498956680, -26.3897991180, -13.4518613815, -26.9037227631, + -12.6449871063, 27.0964012146, -12.8334894180, 26.5836582184, + -13.0226945877, 26.0453891754, -13.2118673325, 25.4800300598, + -13.4003000259, 24.8862724304, -13.5873117447, 24.2630558014, + -13.7722444534, 23.6095619202, -13.9544687271, 22.9251995087, + -14.1333789825, 22.2095966339, -14.3083972931, 21.4625949860, + -14.4789676666, 20.6842403412, -14.6445646286, 19.8747653961, + -14.8046855927, 19.0345954895, -14.9588537216, 18.1643218994, + -15.1066188812, 17.2647075653, -15.2475576401, 16.3366680145, + -15.3812685013, 15.3812685013, -15.5073804855, 14.3997106552, + -15.6255445480, 13.3933238983, -15.7354402542, 12.3635606766, + -15.8367710114, 11.3119792938, -15.9292659760, 10.2402420044, + -16.0126800537, 9.1501035690, -16.0867977142, 8.0433988571, + -16.1514225006, 6.9220380783, -16.2063865662, 5.7879953384, + -16.2515525818, 4.6433005333, -16.2868003845, 3.4900286198, + -16.3120422363, 2.3302917480, -16.3272132874, 1.1662294865, + -16.3322734833, -0.0000000000, -16.3272132874, -1.1662294865, + -16.3120422363, -2.3302917480, -16.2868003845, -3.4900286198, + -16.2515525818, -4.6433005333, -16.2063865662, -5.7879953384, + -16.1514225006, -6.9220380783, -16.0867977142, -8.0433988571, + -16.0126800537, -9.1501035690, -15.9292659760, -10.2402420044, + -15.8367710114, -11.3119792938, -15.7354402542, -12.3635606766, + -15.6255445480, -13.3933238983, -15.5073804855, -14.3997106552, + -15.3812685013, -15.3812685013, -15.2475576401, -16.3366680145, + -15.1066188812, -17.2647075653, -14.9588537216, -18.1643218994, + -14.8046855927, -19.0345954895, -14.6445646286, -19.8747653961, + -14.4789676666, -20.6842403412, -14.3083972931, -21.4625949860, + -14.1333789825, -22.2095966339, -13.9544687271, -22.9251995087, + -13.7722444534, -23.6095619202, -13.5873117447, -24.2630558014, + -13.4003000259, -24.8862724304, -13.2118673325, -25.4800300598, + -13.0226945877, -26.0453891754, -12.8334894180, -26.5836582184, + -12.6449871063, -27.0964012146, -11.8210153580, 27.2792663574, + -11.9992265701, 26.7675056458, -12.1779823303, 26.2294998169, + -12.3566007614, 25.6637096405, -12.5344247818, 25.0688495636, + -12.7108211517, 24.4438858032, -12.8851795197, 23.7880249023, + -13.0569162369, 23.1006965637, -13.2254667282, 22.3815593719, + -13.3902959824, 21.6304779053, -13.5508880615, 20.8475208282, + -13.7067537308, 20.0329475403, -13.8574275970, 19.1872081757, + -14.0024662018, 18.3109169006, -14.1414518356, 17.4048633575, + -14.2739896774, 16.4699878693, -14.3997106552, 15.5073804855, + -14.5182666779, 14.5182666779, -14.6293354034, 13.5040016174, + -14.7326173782, 12.4660615921, -14.8278398514, 11.4060306549, + -14.9147500992, 10.3255958557, -14.9931211472, 9.2265357971, + -15.0627498627, 8.1107110977, -15.1234579086, 6.9800572395, + -15.1750888824, 5.8365726471, -15.2175111771, 4.6823110580, + -15.2506179810, 3.5193734169, -15.2743263245, 2.3498964310, + -15.2885742188, 1.1760442257, -15.2933282852, -0.0000000000, + -15.2885742188, -1.1760442257, -15.2743263245, -2.3498964310, + -15.2506179810, -3.5193734169, -15.2175111771, -4.6823110580, + -15.1750888824, -5.8365726471, -15.1234579086, -6.9800572395, + -15.0627498627, -8.1107110977, -14.9931211472, -9.2265357971, + -14.9147500992, -10.3255958557, -14.8278398514, -11.4060306549, + -14.7326173782, -12.4660615921, -14.6293354034, -13.5040016174, + -14.5182666779, -14.5182666779, -14.3997106552, -15.5073804855, + -14.2739896774, -16.4699878693, -14.1414518356, -17.4048633575, + -14.0024662018, -18.3109169006, -13.8574275970, -19.1872081757, + -13.7067537308, -20.0329475403, -13.5508880615, -20.8475208282, + -13.3902959824, -21.6304779053, -13.2254667282, -22.3815593719, + -13.0569162369, -23.1006965637, -12.8851795197, -23.7880249023, + -12.7108211517, -24.4438858032, -12.5344247818, -25.0688495636, + -12.3566007614, -25.6637096405, -12.1779823303, -26.2294998169, + -11.9992265701, -26.7675056458, -11.8210153580, -27.2792663574, + -10.9806299210, 27.4515743256, -11.1478443146, 26.9406242371, + -11.3154697418, 26.4027633667, -11.4828767776, 25.8364715576, + -11.6494579315, 25.2404918671, -11.8146295547, 24.6138114929, + -11.9778289795, 23.9556579590, -12.1385145187, 23.2654857635, + -12.2961692810, 22.5429763794, -12.4502954483, 21.7880172729, + -12.6004190445, 21.0006980896, -12.7460880280, 20.1813049316, + -12.8868713379, 19.3303070068, -13.0223627090, 18.4483470917, + -13.1521739960, 17.5362319946, -13.2759418488, 16.5949268341, + -13.3933238983, 15.6255445480, -13.5040016174, 14.6293354034, + -13.6076755524, 13.6076755524, -13.7040710449, 12.5620651245, + -13.7929334641, 11.4941110611, -13.8740310669, 10.4055233002, + -13.9471549988, 9.2981033325, -14.0121173859, 8.1737356186, + -14.0687532425, 7.0343766212, -14.1169185638, 5.8820490837, + -14.1564912796, 4.7188305855, -14.1873731613, 3.5468432903, + -14.2094869614, 2.3682479858, -14.2227773666, 1.1852314472, + -14.2272119522, -0.0000000000, -14.2227773666, -1.1852314472, + -14.2094869614, -2.3682479858, -14.1873731613, -3.5468432903, + -14.1564912796, -4.7188305855, -14.1169185638, -5.8820490837, + -14.0687532425, -7.0343766212, -14.0121173859, -8.1737356186, + -13.9471549988, -9.2981033325, -13.8740310669, -10.4055233002, + -13.7929334641, -11.4941110611, -13.7040710449, -12.5620651245, + -13.6076755524, -13.6076755524, -13.5040016174, -14.6293354034, + -13.3933238983, -15.6255445480, -13.2759418488, -16.5949268341, + -13.1521739960, -17.5362319946, -13.0223627090, -18.4483470917, + -12.8868713379, -19.3303070068, -12.7460880280, -20.1813049316, + -12.6004190445, -21.0006980896, -12.4502954483, -21.7880172729, + -12.2961692810, -22.5429763794, -12.1385145187, -23.2654857635, + -11.9778289795, -23.9556579590, -11.8146295547, -24.6138114929, + -11.6494579315, -25.2404918671, -11.4828767776, -25.8364715576, + -11.3154697418, -26.4027633667, -11.1478443146, -26.9406242371, + -10.9806299210, -27.4515743256, -10.1246328354, 27.6126346588, + -10.2802000046, 27.1023464203, -10.4360666275, 26.5645332336, + -10.5916547775, 25.9976978302, -10.7464084625, 25.4006023407, + -10.8997936249, 24.7722568512, -11.0512914658, 24.1119098663, + -11.2004089355, 23.4190368652, -11.3466701508, 22.6933403015, + -11.4896192551, 21.9347286224, -11.6288223267, 21.1433124542, + -11.7638635635, 20.3194007874, -11.8943500519, 19.4634819031, + -12.0199069977, 18.5762195587, -12.1401796341, 17.6584434509, + -12.2548351288, 16.7111396790, -12.3635606766, 15.7354402542, + -12.4660615921, 14.7326173782, -12.5620651245, 13.7040710449, + -12.6513185501, 12.6513185501, -12.7335901260, 11.5759906769, + -12.8086662292, 10.4798183441, -12.8763561249, 9.3646221161, + -12.9364862442, 8.2323093414, -12.9889059067, 7.0848579407, + -13.0334835052, 5.9243106842, -13.0701084137, 4.7527666092, + -13.0986881256, 3.5723693371, -13.1191530228, 2.3853003979, + -13.1314516068, 1.1937683821, -13.1355552673, -0.0000000000, + -13.1314516068, -1.1937683821, -13.1191530228, -2.3853003979, + -13.0986881256, -3.5723693371, -13.0701084137, -4.7527666092, + -13.0334835052, -5.9243106842, -12.9889059067, -7.0848579407, + -12.9364862442, -8.2323093414, -12.8763561249, -9.3646221161, + -12.8086662292, -10.4798183441, -12.7335901260, -11.5759906769, + -12.6513185501, -12.6513185501, -12.5620651245, -13.7040710449, + -12.4660615921, -14.7326173782, -12.3635606766, -15.7354402542, + -12.2548351288, -16.7111396790, -12.1401796341, -17.6584434509, + -12.0199069977, -18.5762195587, -11.8943500519, -19.4634819031, + -11.7638635635, -20.3194007874, -11.6288223267, -21.1433124542, + -11.4896192551, -21.9347286224, -11.3466701508, -22.6933403015, + -11.2004089355, -23.4190368652, -11.0512914658, -24.1119098663, + -10.8997936249, -24.7722568512, -10.7464084625, -25.4006023407, + -10.5916547775, -25.9976978302, -10.4360666275, -26.5645332336, + -10.2802000046, -27.1023464203, -10.1246328354, -27.6126346588, + -9.2539377213, 27.7618141174, -9.3972616196, 27.2520580292, + -9.5407915115, 26.7142162323, -9.6840057373, 26.1468143463, + -9.8263969421, 25.5486316681, -9.9674777985, 24.9186954498, + -10.1067810059, 24.2562732697, -10.2438545227, 23.5608654022, + -10.3782663345, 22.8321857452, -10.5096044540, 22.0701675415, + -10.6374711990, 21.2749423981, -10.7614917755, 20.4468326569, + -10.8813056946, 19.5863494873, -10.9965744019, 18.6941757202, + -11.1069755554, 17.7711601257, -11.2122049332, 16.8183078766, + -11.3119792938, 15.8367710114, -11.4060306549, 14.8278398514, + -11.4941110611, 13.7929334641, -11.5759906769, 12.7335901260, + -11.6514587402, 11.6514587402, -11.7203216553, 10.5482892990, + -11.7824039459, 9.4259233475, -11.8375511169, 8.2862854004, + -11.8856229782, 7.1313738823, -11.9265022278, 5.9632511139, + -11.9600868225, 4.7840347290, -11.9862937927, 3.5958881378, + -12.0050592422, 2.4010119438, -12.0163364410, 1.2016336918, + -12.0200986862, -0.0000000000, -12.0163364410, -1.2016336918, + -12.0050592422, -2.4010119438, -11.9862937927, -3.5958881378, + -11.9600868225, -4.7840347290, -11.9265022278, -5.9632511139, + -11.8856229782, -7.1313738823, -11.8375511169, -8.2862854004, + -11.7824039459, -9.4259233475, -11.7203216553, -10.5482892990, + -11.6514587402, -11.6514587402, -11.5759906769, -12.7335901260, + -11.4941110611, -13.7929334641, -11.4060306549, -14.8278398514, + -11.3119792938, -15.8367710114, -11.2122049332, -16.8183078766, + -11.1069755554, -17.7711601257, -10.9965744019, -18.6941757202, + -10.8813056946, -19.5863494873, -10.7614917755, -20.4468326569, + -10.6374711990, -21.2749423981, -10.5096044540, -22.0701675415, + -10.3782663345, -22.8321857452, -10.2438545227, -23.5608654022, + -10.1067810059, -24.2562732697, -9.9674777985, -24.9186954498, + -9.8263969421, -25.5486316681, -9.6840057373, -26.1468143463, + -9.5407915115, -26.7142162323, -9.3972616196, -27.2520580292, + -9.2539377213, -27.7618141174, -8.3695592880, 27.8985328674, + -8.5000963211, 27.3892002106, -8.6307678223, 26.8512763977, + -8.7611007690, 26.2833023071, -8.8906412125, 25.6840744019, + -9.0189504623, 25.0526409149, -9.1456069946, 24.3882865906, + -9.2702045441, 23.6905231476, -9.3923549652, 22.9590892792, + -9.5116853714, 22.1939334869, -9.6278400421, 21.3952007294, + -9.7404804230, 20.5632362366, -9.8492822647, 19.6985645294, + -9.9539413452, 18.8018894196, -10.0541667938, 17.8740749359, + -10.1496858597, 16.9161434174, -10.2402420044, 15.9292659760, + -10.3255958557, 14.9147500992, -10.4055233002, 13.8740310669, + -10.4798183441, 12.8086662292, -10.5482892990, 11.7203216553, + -10.6107635498, 10.6107635498, -10.6670837402, 9.4818515778, + -10.7171087265, 8.3355283737, -10.7607145309, 7.1738095284, + -10.7977933884, 5.9987745285, -10.8282556534, 4.8125581741, + -10.8520250320, 3.6173417568, -10.8690452576, 2.4153432846, + -10.8792734146, 1.2088081837, -10.8826856613, -0.0000000000, + -10.8792734146, -1.2088081837, -10.8690452576, -2.4153432846, + -10.8520250320, -3.6173417568, -10.8282556534, -4.8125581741, + -10.7977933884, -5.9987745285, -10.7607145309, -7.1738095284, + -10.7171087265, -8.3355283737, -10.6670837402, -9.4818515778, + -10.6107635498, -10.6107635498, -10.5482892990, -11.7203216553, + -10.4798183441, -12.8086662292, -10.4055233002, -13.8740310669, + -10.3255958557, -14.9147500992, -10.2402420044, -15.9292659760, + -10.1496858597, -16.9161434174, -10.0541667938, -17.8740749359, + -9.9539413452, -18.8018894196, -9.8492822647, -19.6985645294, + -9.7404804230, -20.5632362366, -9.6278400421, -21.3952007294, + -9.5116853714, -22.1939334869, -9.3923549652, -22.9590892792, + -9.2702045441, -23.6905231476, -9.1456069946, -24.3882865906, + -9.0189504623, -25.0526409149, -8.8906412125, -25.6840744019, + -8.7611007690, -26.2833023071, -8.6307678223, -26.8512763977, + -8.5000963211, -27.3892002106, -8.3695592880, -27.8985328674, + -7.4726042747, 28.0222644806, -7.5898661613, 27.5132656097, + -7.7072062492, 26.9752216339, -7.8242039680, 26.4066886902, + -7.9404559135, 25.8064804077, -8.0555715561, 25.1736602783, + -8.1691761017, 24.5075283051, -8.2809085846, 23.8076114655, + -8.3904247284, 23.0736675262, -8.4973917007, 22.3056526184, + -8.6014947891, 21.5037364960, -8.7024316788, 20.6682758331, + -8.7999162674, 19.7998123169, -8.8936758041, 18.8990612030, + -8.9834527969, 17.9669055939, -9.0690040588, 17.0043830872, + -9.1501035690, 16.0126800537, -9.2265357971, 14.9931211472, + -9.2981033325, 13.9471549988, -9.3646221161, 12.8763561249, + -9.4259233475, 11.7824039459, -9.4818515778, 10.6670837402, + -9.5322685242, 9.5322685242, -9.5770473480, 8.3799161911, + -9.6160793304, 7.2120594978, -9.6492681503, 6.0307927132, + -9.6765327454, 4.8382663727, -9.6978073120, 3.6366777420, + -9.7130403519, 2.4282600880, -9.7221946716, 1.2152743340, + -9.7252483368, -0.0000000000, -9.7221946716, -1.2152743340, + -9.7130403519, -2.4282600880, -9.6978073120, -3.6366777420, + -9.6765327454, -4.8382663727, -9.6492681503, -6.0307927132, + -9.6160793304, -7.2120594978, -9.5770473480, -8.3799161911, + -9.5322685242, -9.5322685242, -9.4818515778, -10.6670837402, + -9.4259233475, -11.7824039459, -9.3646221161, -12.8763561249, + -9.2981033325, -13.9471549988, -9.2265357971, -14.9931211472, + -9.1501035690, -16.0126800537, -9.0690040588, -17.0043830872, + -8.9834527969, -17.9669055939, -8.8936758041, -18.8990612030, + -8.7999162674, -19.7998123169, -8.7024316788, -20.6682758331, + -8.6014947891, -21.5037364960, -8.4973917007, -22.3056526184, + -8.3904247284, -23.0736675262, -8.2809085846, -23.8076114655, + -8.1691761017, -24.5075283051, -8.0555715561, -25.1736602783, + -7.9404559135, -25.8064804077, -7.8242039680, -26.4066886902, + -7.7072062492, -26.9752216339, -7.5898661613, -27.5132656097, + -7.4726042747, -28.0222644806, -6.5642604828, 28.1325454712, + -6.6678142548, 27.6238021851, -6.7714037895, 27.0856151581, + -6.8746614456, 26.5165519714, -6.9772343636, 25.9154434204, + -7.0787811279, 25.2813606262, -7.1789731979, 24.6136226654, + -7.2774958611, 23.9117717743, -7.3740458488, 23.1755733490, + -7.4683341980, 22.4050025940, -7.5600843430, 21.6002407074, + -7.6490316391, 20.7616577148, -7.7349257469, 19.8898086548, + -7.8175282478, 18.9854259491, -7.8966135979, 18.0494022369, + -7.9719705582, 17.0827941895, -8.0433988571, 16.0867977142, + -8.1107110977, 15.0627498627, -8.1737356186, 14.0121173859, + -8.2323093414, 12.9364862442, -8.2862854004, 11.8375511169, + -8.3355283737, 10.7171087265, -8.3799161911, 9.5770473480, + -8.4193401337, 8.4193401337, -8.4537019730, 7.2460298538, + -8.4829187393, 6.0592274666, -8.5069198608, 4.8610973358, + -8.5256481171, 3.6538491249, -8.5390567780, 2.4397306442, + -8.5471153259, 1.2210165262, -8.5498037338, -0.0000000000, + -8.5471153259, -1.2210165262, -8.5390567780, -2.4397306442, + -8.5256481171, -3.6538491249, -8.5069198608, -4.8610973358, + -8.4829187393, -6.0592274666, -8.4537019730, -7.2460298538, + -8.4193401337, -8.4193401337, -8.3799161911, -9.5770473480, + -8.3355283737, -10.7171087265, -8.2862854004, -11.8375511169, + -8.2323093414, -12.9364862442, -8.1737356186, -14.0121173859, + -8.1107110977, -15.0627498627, -8.0433988571, -16.0867977142, + -7.9719705582, -17.0827941895, -7.8966135979, -18.0494022369, + -7.8175282478, -18.9854259491, -7.7349257469, -19.8898086548, + -7.6490316391, -20.7616577148, -7.5600843430, -21.6002407074, + -7.4683341980, -22.4050025940, -7.3740458488, -23.1755733490, + -7.2774958611, -23.9117717743, -7.1789731979, -24.6136226654, + -7.0787811279, -25.2813606262, -6.9772343636, -25.9154434204, + -6.8746614456, -26.5165519714, -6.7714037895, -27.0856151581, + -6.6678142548, -27.6238021851, -6.5642604828, -28.1325454712, + -5.6457915306, 28.2289581299, -5.7352571487, 27.7204093933, + -5.8247289658, 27.1820678711, -5.9138932228, 26.6125202179, + -6.0024461746, 26.0106010437, -6.0900959969, 25.3753986359, + -6.1765604019, 24.7062416077, -6.2615699768, 24.0026855469, + -6.3448653221, 23.2645053864, -6.4261975288, 22.4916915894, + -6.5053300858, 21.6844348907, -6.5820369720, 20.8431167603, + -6.6561026573, 19.9683074951, -6.7273230553, 19.0607490540, + -6.7955055237, 18.1213474274, -6.8604674339, 17.1511688232, + -6.9220380783, 16.1514225006, -6.9800572395, 15.1234579086, + -7.0343766212, 14.0687532425, -7.0848579407, 12.9889059067, + -7.1313738823, 11.8856229782, -7.1738095284, 10.7607145309, + -7.2120594978, 9.6160793304, -7.2460298538, 8.4537019730, + -7.2756385803, 7.2756385803, -7.3008131981, 6.0840110779, + -7.3214931488, 4.8809957504, -7.3376293182, 3.6688146591, + -7.3491826057, 2.4497275352, -7.3561258316, 1.2260209322, + -7.3584418297, -0.0000000000, -7.3561258316, -1.2260209322, + -7.3491826057, -2.4497275352, -7.3376293182, -3.6688146591, + -7.3214931488, -4.8809957504, -7.3008131981, -6.0840110779, + -7.2756385803, -7.2756385803, -7.2460298538, -8.4537019730, + -7.2120594978, -9.6160793304, -7.1738095284, -10.7607145309, + -7.1313738823, -11.8856229782, -7.0848579407, -12.9889059067, + -7.0343766212, -14.0687532425, -6.9800572395, -15.1234579086, + -6.9220380783, -16.1514225006, -6.8604674339, -17.1511688232, + -6.7955055237, -18.1213474274, -6.7273230553, -19.0607490540, + -6.6561026573, -19.9683074951, -6.5820369720, -20.8431167603, + -6.5053300858, -21.6844348907, -6.4261975288, -22.4916915894, + -6.3448653221, -23.2645053864, -6.2615699768, -24.0026855469, + -6.1765604019, -24.7062416077, -6.0900959969, -25.3753986359, + -6.0024461746, -26.0106010437, -5.9138932228, -26.6125202179, + -5.8247289658, -27.1820678711, -5.7352571487, -27.7204093933, + -5.6457915306, -28.2289581299, -4.7185239792, 28.3111438751, + -4.7935757637, 27.8027400970, -4.8686161041, 27.2642498016, + -4.9433832169, 26.6942691803, -5.0176239014, 26.0916442871, + -5.0910949707, 25.4554748535, -5.1635618210, 24.7850971222, + -5.2347993851, 24.0800762177, -5.3045911789, 23.3402004242, + -5.3727307320, 22.5654678345, -5.4390201569, 21.7560806274, + -5.5032711029, 20.9124298096, -5.5653042793, 20.0350952148, + -5.6249494553, 19.1248283386, -5.6820459366, 18.1825466156, + -5.7364420891, 17.2093257904, -5.7879953384, 16.2063865662, + -5.8365726471, 15.1750888824, -5.8820490837, 14.1169185638, + -5.9243106842, 13.0334835052, -5.9632511139, 11.9265022278, + -5.9987745285, 10.7977933884, -6.0307927132, 9.6492681503, + -6.0592274666, 8.4829187393, -6.0840110779, 7.3008131981, + -6.1050825119, 6.1050825119, -6.1223917007, 4.8979134560, + -6.1358976364, 3.6815385818, -6.1455674171, 2.4582269192, + -6.1513786316, 1.2302757502, -6.1533174515, -0.0000000000, + -6.1513786316, -1.2302757502, -6.1455674171, -2.4582269192, + -6.1358976364, -3.6815385818, -6.1223917007, -4.8979134560, + -6.1050825119, -6.1050825119, -6.0840110779, -7.3008131981, + -6.0592274666, -8.4829187393, -6.0307927132, -9.6492681503, + -5.9987745285, -10.7977933884, -5.9632511139, -11.9265022278, + -5.9243106842, -13.0334835052, -5.8820490837, -14.1169185638, + -5.8365726471, -15.1750888824, -5.7879953384, -16.2063865662, + -5.7364420891, -17.2093257904, -5.6820459366, -18.1825466156, + -5.6249494553, -19.1248283386, -5.5653042793, -20.0350952148, + -5.5032711029, -20.9124298096, -5.4390201569, -21.7560806274, + -5.3727307320, -22.5654678345, -5.3045911789, -23.3402004242, + -5.2347993851, -24.0800762177, -5.1635618210, -24.7850971222, + -5.0910949707, -25.4554748535, -5.0176239014, -26.0916442871, + -4.9433832169, -26.6942691803, -4.8686161041, -27.2642498016, + -4.7935757637, -27.8027400970, -4.7185239792, -28.3111438751, + -3.7838401794, 28.3788013458, -3.8442070484, 27.8705005646, + -3.9045536518, 27.3318767548, -3.9646706581, 26.7615280151, + -4.0243558884, 26.1583118439, -4.0834140778, 25.5213375092, + -4.1416578293, 24.8499469757, -4.1989068985, 24.1437149048, + -4.2549886703, 23.4024372101, -4.3097372055, 22.6261196136, + -4.3629951477, 21.8149738312, -4.4146108627, 20.9694004059, + -4.4644412994, 20.0899868011, -4.5123505592, 19.1774902344, + -4.5582098961, 18.2328395844, -4.6018981934, 17.2571182251, + -4.6433005333, 16.2515525818, -4.6823110580, 15.2175111771, + -4.7188305855, 14.1564912796, -4.7527666092, 13.0701084137, + -4.7840347290, 11.9600868225, -4.8125581741, 10.8282556534, + -4.8382663727, 9.6765327454, -4.8610973358, 8.5069198608, + -4.8809957504, 7.3214931488, -4.8979134560, 6.1223917007, + -4.9118108749, 4.9118108749, -4.9226536751, 3.6919903755, + -4.9304175377, 2.4652087688, -4.9350829124, 1.2337707281, + -4.9366393089, -0.0000000000, -4.9350829124, -1.2337707281, + -4.9304175377, -2.4652087688, -4.9226536751, -3.6919903755, + -4.9118108749, -4.9118108749, -4.8979134560, -6.1223917007, + -4.8809957504, -7.3214931488, -4.8610973358, -8.5069198608, + -4.8382663727, -9.6765327454, -4.8125581741, -10.8282556534, + -4.7840347290, -11.9600868225, -4.7527666092, -13.0701084137, + -4.7188305855, -14.1564912796, -4.6823110580, -15.2175111771, + -4.6433005333, -16.2515525818, -4.6018981934, -17.2571182251, + -4.5582098961, -18.2328395844, -4.5123505592, -19.1774902344, + -4.4644412994, -20.0899868011, -4.4146108627, -20.9694004059, + -4.3629951477, -21.8149738312, -4.3097372055, -22.6261196136, + -4.2549886703, -23.4024372101, -4.1989068985, -24.1437149048, + -4.1416578293, -24.8499469757, -4.0834140778, -25.5213375092, + -4.0243558884, -26.1583118439, -3.9646706581, -26.7615280151, + -3.9045536518, -27.3318767548, -3.8442070484, -27.8705005646, + -3.7838401794, -28.3788013458, -2.8431680202, 28.4316806793, + -2.8886330128, 27.9234523773, -2.9340765476, 27.3847141266, + -2.9793412685, 26.8140716553, -3.0242755413, 26.2103881836, + -3.0687332153, 25.5727767944, -3.1125738621, 24.9005908966, + -3.1556618214, 24.1934070587, -3.1978678703, 23.4510307312, + -3.2390677929, 22.6734752655, -3.2791430950, 21.8609542847, + -3.3179802895, 21.0138759613, -3.3554723263, 20.1328353882, + -3.3915169239, 19.2185974121, -3.4260177612, 18.2720947266, + -3.4588835239, 17.2944164276, -3.4900286198, 16.2868003845, + -3.5193734169, 15.2506179810, -3.5468432903, 14.1873731613, + -3.5723693371, 13.0986881256, -3.5958881378, 11.9862937927, + -3.6173417568, 10.8520250320, -3.6366777420, 9.6978073120, + -3.6538491249, 8.5256481171, -3.6688146591, 7.3376293182, + -3.6815385818, 6.1358976364, -3.6919903755, 4.9226536751, + -3.7001452446, 3.7001452446, -3.7059841156, 2.4706559181, + -3.7094929218, 1.2364976406, -3.7106633186, -0.0000000000, + -3.7094929218, -1.2364976406, -3.7059841156, -2.4706559181, + -3.7001452446, -3.7001452446, -3.6919903755, -4.9226536751, + -3.6815385818, -6.1358976364, -3.6688146591, -7.3376293182, + -3.6538491249, -8.5256481171, -3.6366777420, -9.6978073120, + -3.6173417568, -10.8520250320, -3.5958881378, -11.9862937927, + -3.5723693371, -13.0986881256, -3.5468432903, -14.1873731613, + -3.5193734169, -15.2506179810, -3.4900286198, -16.2868003845, + -3.4588835239, -17.2944164276, -3.4260177612, -18.2720947266, + -3.3915169239, -19.2185974121, -3.3554723263, -20.1328353882, + -3.3179802895, -21.0138759613, -3.2791430950, -21.8609542847, + -3.2390677929, -22.6734752655, -3.1978678703, -23.4510307312, + -3.1556618214, -24.1934070587, -3.1125738621, -24.9005908966, + -3.0687332153, -25.5727767944, -3.0242755413, -26.2103881836, + -2.9793412685, -26.8140716553, -2.9340765476, -27.3847141266, + -2.8886330128, -27.9234523773, -2.8431680202, -28.4316806793, + -1.8979727030, 28.4695892334, -1.9283730984, 27.9614086151, + -1.9587560892, 27.4225845337, -1.9890167713, 26.8517265320, + -2.0190541744, 26.2477054596, -2.0487709045, 25.6096363068, + -2.0780727863, 24.9368743896, -2.1068701744, 24.2290077209, + -2.1350765228, 23.4858417511, -2.1626091003, 22.7073955536, + -2.1893887520, 21.8938865662, -2.2153401375, 21.0457305908, + -2.2403914928, 20.1635227203, -2.2644748688, 19.2480354309, + -2.2875258923, 18.3002071381, -2.3094837666, 17.3211288452, + -2.3302917480, 16.3120422363, -2.3498964310, 15.2743263245, + -2.3682479858, 14.2094869614, -2.3853003979, 13.1191530228, + -2.4010119438, 12.0050592422, -2.4153432846, 10.8690452576, + -2.4282600880, 9.7130403519, -2.4397306442, 8.5390567780, + -2.4497275352, 7.3491826057, -2.4582269192, 6.1455674171, + -2.4652087688, 4.9304175377, -2.4706559181, 3.7059841156, + -2.4745562077, 2.4745562077, -2.4768998623, 1.2384499311, + -2.4776818752, -0.0000000000, -2.4768998623, -1.2384499311, + -2.4745562077, -2.4745562077, -2.4706559181, -3.7059841156, + -2.4652087688, -4.9304175377, -2.4582269192, -6.1455674171, + -2.4497275352, -7.3491826057, -2.4397306442, -8.5390567780, + -2.4282600880, -9.7130403519, -2.4153432846, -10.8690452576, + -2.4010119438, -12.0050592422, -2.3853003979, -13.1191530228, + -2.3682479858, -14.2094869614, -2.3498964310, -15.2743263245, + -2.3302917480, -16.3120422363, -2.3094837666, -17.3211288452, + -2.2875258923, -18.3002071381, -2.2644748688, -19.2480354309, + -2.2403914928, -20.1635227203, -2.2153401375, -21.0457305908, + -2.1893887520, -21.8938865662, -2.1626091003, -22.7073955536, + -2.1350765228, -23.4858417511, -2.1068701744, -24.2290077209, + -2.0780727863, -24.9368743896, -2.0487709045, -25.6096363068, + -2.0190541744, -26.2477054596, -1.9890167713, -26.8517265320, + -1.9587560892, -27.4225845337, -1.9283730984, -27.9614086151, + -1.8979727030, -28.4695892334, -0.9497463703, 28.4923896790, + -0.9649736881, 27.9842357635, -0.9801913500, 27.4453582764, + -0.9953470230, 26.8743686676, -1.0103900433, 26.2701416016, + -1.0252718925, 25.6317958832, -1.0399453640, 24.9586887360, + -1.0543657541, 24.2504119873, -1.0684895515, 23.5067691803, + -1.0822755098, 22.7277870178, -1.0956841707, 21.9136848450, + -1.1086778641, 21.0648784637, -1.1212204695, 20.1819686890, + -1.1332782507, 19.2657318115, -1.1448190212, 18.3171043396, + -1.1558121443, 17.3371829987, -1.1662294865, 16.3272132874, + -1.1760442257, 15.2885742188, -1.1852314472, 14.2227773666, + -1.1937683821, 13.1314516068, -1.2016336918, 12.0163364410, + -1.2088081837, 10.8792734146, -1.2152743340, 9.7221946716, + -1.2210165262, 8.5471153259, -1.2260209322, 7.3561258316, + -1.2302757502, 6.1513786316, -1.2337707281, 4.9350829124, + -1.2364976406, 3.7094929218, -1.2384499311, 2.4768998623, + -1.2396231890, 1.2396231890, -1.2400146723, -0.0000000000, + -1.2396231890, -1.2396231890, -1.2384499311, -2.4768998623, + -1.2364976406, -3.7094929218, -1.2337707281, -4.9350829124, + -1.2302757502, -6.1513786316, -1.2260209322, -7.3561258316, + -1.2210165262, -8.5471153259, -1.2152743340, -9.7221946716, + -1.2088081837, -10.8792734146, -1.2016336918, -12.0163364410, + -1.1937683821, -13.1314516068, -1.1852314472, -14.2227773666, + -1.1760442257, -15.2885742188, -1.1662294865, -16.3272132874, + -1.1558121443, -17.3371829987, -1.1448190212, -18.3171043396, + -1.1332782507, -19.2657318115, -1.1212204695, -20.1819686890, + -1.1086778641, -21.0648784637, -1.0956841707, -21.9136848450, + -1.0822755098, -22.7277870178, -1.0684895515, -23.5067691803, + -1.0543657541, -24.2504119873, -1.0399453640, -24.9586887360, + -1.0252718925, -25.6317958832, -1.0103900433, -26.2701416016, + -0.9953470230, -26.8743686676, -0.9801913500, -27.4453582764, + -0.9649736881, -27.9842357635, -0.9497463703, -28.4923896790, + 0.0000000000, 28.5000000000, 0.0000000000, 27.9918537140, + 0.0000000000, 27.4529571533, 0.0000000000, 26.8819255829, + 0.0000000000, 26.2776298523, 0.0000000000, 25.6391906738, + 0.0000000000, 24.9659690857, 0.0000000000, 24.2575531006, + 0.0000000000, 23.5137519836, 0.0000000000, 22.7345905304, + 0.0000000000, 21.9202899933, 0.0000000000, 21.0712661743, + 0.0000000000, 20.1881237030, 0.0000000000, 19.2716350555, + 0.0000000000, 18.3227405548, 0.0000000000, 17.3425388336, + 0.0000000000, 16.3322734833, 0.0000000000, 15.2933282852, + 0.0000000000, 14.2272119522, 0.0000000000, 13.1355552673, + 0.0000000000, 12.0200986862, 0.0000000000, 10.8826856613, + 0.0000000000, 9.7252483368, 0.0000000000, 8.5498037338, + 0.0000000000, 7.3584418297, 0.0000000000, 6.1533174515, + 0.0000000000, 4.9366393089, 0.0000000000, 3.7106633186, + 0.0000000000, 2.4776818752, 0.0000000000, 1.2400146723, + 0.0000000000, -0.0000000000, 0.0000000000, -1.2400146723, + 0.0000000000, -2.4776818752, 0.0000000000, -3.7106633186, + 0.0000000000, -4.9366393089, 0.0000000000, -6.1533174515, + 0.0000000000, -7.3584418297, 0.0000000000, -8.5498037338, + 0.0000000000, -9.7252483368, 0.0000000000, -10.8826856613, + 0.0000000000, -12.0200986862, 0.0000000000, -13.1355552673, + 0.0000000000, -14.2272119522, 0.0000000000, -15.2933282852, + 0.0000000000, -16.3322734833, 0.0000000000, -17.3425388336, + 0.0000000000, -18.3227405548, 0.0000000000, -19.2716350555, + 0.0000000000, -20.1881237030, 0.0000000000, -21.0712661743, + 0.0000000000, -21.9202899933, 0.0000000000, -22.7345905304, + 0.0000000000, -23.5137519836, 0.0000000000, -24.2575531006, + 0.0000000000, -24.9659690857, 0.0000000000, -25.6391906738, + 0.0000000000, -26.2776298523, 0.0000000000, -26.8819255829, + 0.0000000000, -27.4529571533, 0.0000000000, -27.9918537140, + 0.0000000000, -28.5000000000, 0.9497463703, 28.4923896790, + 0.9649736881, 27.9842357635, 0.9801913500, 27.4453582764, + 0.9953470230, 26.8743686676, 1.0103900433, 26.2701416016, + 1.0252718925, 25.6317958832, 1.0399453640, 24.9586887360, + 1.0543657541, 24.2504119873, 1.0684895515, 23.5067691803, + 1.0822755098, 22.7277870178, 1.0956841707, 21.9136848450, + 1.1086778641, 21.0648784637, 1.1212204695, 20.1819686890, + 1.1332782507, 19.2657318115, 1.1448190212, 18.3171043396, + 1.1558121443, 17.3371829987, 1.1662294865, 16.3272132874, + 1.1760442257, 15.2885742188, 1.1852314472, 14.2227773666, + 1.1937683821, 13.1314516068, 1.2016336918, 12.0163364410, + 1.2088081837, 10.8792734146, 1.2152743340, 9.7221946716, + 1.2210165262, 8.5471153259, 1.2260209322, 7.3561258316, + 1.2302757502, 6.1513786316, 1.2337707281, 4.9350829124, + 1.2364976406, 3.7094929218, 1.2384499311, 2.4768998623, + 1.2396231890, 1.2396231890, 1.2400146723, -0.0000000000, + 1.2396231890, -1.2396231890, 1.2384499311, -2.4768998623, + 1.2364976406, -3.7094929218, 1.2337707281, -4.9350829124, + 1.2302757502, -6.1513786316, 1.2260209322, -7.3561258316, + 1.2210165262, -8.5471153259, 1.2152743340, -9.7221946716, + 1.2088081837, -10.8792734146, 1.2016336918, -12.0163364410, + 1.1937683821, -13.1314516068, 1.1852314472, -14.2227773666, + 1.1760442257, -15.2885742188, 1.1662294865, -16.3272132874, + 1.1558121443, -17.3371829987, 1.1448190212, -18.3171043396, + 1.1332782507, -19.2657318115, 1.1212204695, -20.1819686890, + 1.1086778641, -21.0648784637, 1.0956841707, -21.9136848450, + 1.0822755098, -22.7277870178, 1.0684895515, -23.5067691803, + 1.0543657541, -24.2504119873, 1.0399453640, -24.9586887360, + 1.0252718925, -25.6317958832, 1.0103900433, -26.2701416016, + 0.9953470230, -26.8743686676, 0.9801913500, -27.4453582764, + 0.9649736881, -27.9842357635, 0.9497463703, -28.4923896790, + 1.8979727030, 28.4695892334, 1.9283730984, 27.9614086151, + 1.9587560892, 27.4225845337, 1.9890167713, 26.8517265320, + 2.0190541744, 26.2477054596, 2.0487709045, 25.6096363068, + 2.0780727863, 24.9368743896, 2.1068701744, 24.2290077209, + 2.1350765228, 23.4858417511, 2.1626091003, 22.7073955536, + 2.1893887520, 21.8938865662, 2.2153401375, 21.0457305908, + 2.2403914928, 20.1635227203, 2.2644748688, 19.2480354309, + 2.2875258923, 18.3002071381, 2.3094837666, 17.3211288452, + 2.3302917480, 16.3120422363, 2.3498964310, 15.2743263245, + 2.3682479858, 14.2094869614, 2.3853003979, 13.1191530228, + 2.4010119438, 12.0050592422, 2.4153432846, 10.8690452576, + 2.4282600880, 9.7130403519, 2.4397306442, 8.5390567780, + 2.4497275352, 7.3491826057, 2.4582269192, 6.1455674171, + 2.4652087688, 4.9304175377, 2.4706559181, 3.7059841156, + 2.4745562077, 2.4745562077, 2.4768998623, 1.2384499311, + 2.4776818752, -0.0000000000, 2.4768998623, -1.2384499311, + 2.4745562077, -2.4745562077, 2.4706559181, -3.7059841156, + 2.4652087688, -4.9304175377, 2.4582269192, -6.1455674171, + 2.4497275352, -7.3491826057, 2.4397306442, -8.5390567780, + 2.4282600880, -9.7130403519, 2.4153432846, -10.8690452576, + 2.4010119438, -12.0050592422, 2.3853003979, -13.1191530228, + 2.3682479858, -14.2094869614, 2.3498964310, -15.2743263245, + 2.3302917480, -16.3120422363, 2.3094837666, -17.3211288452, + 2.2875258923, -18.3002071381, 2.2644748688, -19.2480354309, + 2.2403914928, -20.1635227203, 2.2153401375, -21.0457305908, + 2.1893887520, -21.8938865662, 2.1626091003, -22.7073955536, + 2.1350765228, -23.4858417511, 2.1068701744, -24.2290077209, + 2.0780727863, -24.9368743896, 2.0487709045, -25.6096363068, + 2.0190541744, -26.2477054596, 1.9890167713, -26.8517265320, + 1.9587560892, -27.4225845337, 1.9283730984, -27.9614086151, + 1.8979727030, -28.4695892334, 2.8431680202, 28.4316806793, + 2.8886330128, 27.9234523773, 2.9340765476, 27.3847141266, + 2.9793412685, 26.8140716553, 3.0242755413, 26.2103881836, + 3.0687332153, 25.5727767944, 3.1125738621, 24.9005908966, + 3.1556618214, 24.1934070587, 3.1978678703, 23.4510307312, + 3.2390677929, 22.6734752655, 3.2791430950, 21.8609542847, + 3.3179802895, 21.0138759613, 3.3554723263, 20.1328353882, + 3.3915169239, 19.2185974121, 3.4260177612, 18.2720947266, + 3.4588835239, 17.2944164276, 3.4900286198, 16.2868003845, + 3.5193734169, 15.2506179810, 3.5468432903, 14.1873731613, + 3.5723693371, 13.0986881256, 3.5958881378, 11.9862937927, + 3.6173417568, 10.8520250320, 3.6366777420, 9.6978073120, + 3.6538491249, 8.5256481171, 3.6688146591, 7.3376293182, + 3.6815385818, 6.1358976364, 3.6919903755, 4.9226536751, + 3.7001452446, 3.7001452446, 3.7059841156, 2.4706559181, + 3.7094929218, 1.2364976406, 3.7106633186, -0.0000000000, + 3.7094929218, -1.2364976406, 3.7059841156, -2.4706559181, + 3.7001452446, -3.7001452446, 3.6919903755, -4.9226536751, + 3.6815385818, -6.1358976364, 3.6688146591, -7.3376293182, + 3.6538491249, -8.5256481171, 3.6366777420, -9.6978073120, + 3.6173417568, -10.8520250320, 3.5958881378, -11.9862937927, + 3.5723693371, -13.0986881256, 3.5468432903, -14.1873731613, + 3.5193734169, -15.2506179810, 3.4900286198, -16.2868003845, + 3.4588835239, -17.2944164276, 3.4260177612, -18.2720947266, + 3.3915169239, -19.2185974121, 3.3554723263, -20.1328353882, + 3.3179802895, -21.0138759613, 3.2791430950, -21.8609542847, + 3.2390677929, -22.6734752655, 3.1978678703, -23.4510307312, + 3.1556618214, -24.1934070587, 3.1125738621, -24.9005908966, + 3.0687332153, -25.5727767944, 3.0242755413, -26.2103881836, + 2.9793412685, -26.8140716553, 2.9340765476, -27.3847141266, + 2.8886330128, -27.9234523773, 2.8431680202, -28.4316806793, + 3.7838401794, 28.3788013458, 3.8442070484, 27.8705005646, + 3.9045536518, 27.3318767548, 3.9646706581, 26.7615280151, + 4.0243558884, 26.1583118439, 4.0834140778, 25.5213375092, + 4.1416578293, 24.8499469757, 4.1989068985, 24.1437149048, + 4.2549886703, 23.4024372101, 4.3097372055, 22.6261196136, + 4.3629951477, 21.8149738312, 4.4146108627, 20.9694004059, + 4.4644412994, 20.0899868011, 4.5123505592, 19.1774902344, + 4.5582098961, 18.2328395844, 4.6018981934, 17.2571182251, + 4.6433005333, 16.2515525818, 4.6823110580, 15.2175111771, + 4.7188305855, 14.1564912796, 4.7527666092, 13.0701084137, + 4.7840347290, 11.9600868225, 4.8125581741, 10.8282556534, + 4.8382663727, 9.6765327454, 4.8610973358, 8.5069198608, + 4.8809957504, 7.3214931488, 4.8979134560, 6.1223917007, + 4.9118108749, 4.9118108749, 4.9226536751, 3.6919903755, + 4.9304175377, 2.4652087688, 4.9350829124, 1.2337707281, + 4.9366393089, -0.0000000000, 4.9350829124, -1.2337707281, + 4.9304175377, -2.4652087688, 4.9226536751, -3.6919903755, + 4.9118108749, -4.9118108749, 4.8979134560, -6.1223917007, + 4.8809957504, -7.3214931488, 4.8610973358, -8.5069198608, + 4.8382663727, -9.6765327454, 4.8125581741, -10.8282556534, + 4.7840347290, -11.9600868225, 4.7527666092, -13.0701084137, + 4.7188305855, -14.1564912796, 4.6823110580, -15.2175111771, + 4.6433005333, -16.2515525818, 4.6018981934, -17.2571182251, + 4.5582098961, -18.2328395844, 4.5123505592, -19.1774902344, + 4.4644412994, -20.0899868011, 4.4146108627, -20.9694004059, + 4.3629951477, -21.8149738312, 4.3097372055, -22.6261196136, + 4.2549886703, -23.4024372101, 4.1989068985, -24.1437149048, + 4.1416578293, -24.8499469757, 4.0834140778, -25.5213375092, + 4.0243558884, -26.1583118439, 3.9646706581, -26.7615280151, + 3.9045536518, -27.3318767548, 3.8442070484, -27.8705005646, + 3.7838401794, -28.3788013458, 4.7185239792, 28.3111438751, + 4.7935757637, 27.8027400970, 4.8686161041, 27.2642498016, + 4.9433832169, 26.6942691803, 5.0176239014, 26.0916442871, + 5.0910949707, 25.4554748535, 5.1635618210, 24.7850971222, + 5.2347993851, 24.0800762177, 5.3045911789, 23.3402004242, + 5.3727307320, 22.5654678345, 5.4390201569, 21.7560806274, + 5.5032711029, 20.9124298096, 5.5653042793, 20.0350952148, + 5.6249494553, 19.1248283386, 5.6820459366, 18.1825466156, + 5.7364420891, 17.2093257904, 5.7879953384, 16.2063865662, + 5.8365726471, 15.1750888824, 5.8820490837, 14.1169185638, + 5.9243106842, 13.0334835052, 5.9632511139, 11.9265022278, + 5.9987745285, 10.7977933884, 6.0307927132, 9.6492681503, + 6.0592274666, 8.4829187393, 6.0840110779, 7.3008131981, + 6.1050825119, 6.1050825119, 6.1223917007, 4.8979134560, + 6.1358976364, 3.6815385818, 6.1455674171, 2.4582269192, + 6.1513786316, 1.2302757502, 6.1533174515, -0.0000000000, + 6.1513786316, -1.2302757502, 6.1455674171, -2.4582269192, + 6.1358976364, -3.6815385818, 6.1223917007, -4.8979134560, + 6.1050825119, -6.1050825119, 6.0840110779, -7.3008131981, + 6.0592274666, -8.4829187393, 6.0307927132, -9.6492681503, + 5.9987745285, -10.7977933884, 5.9632511139, -11.9265022278, + 5.9243106842, -13.0334835052, 5.8820490837, -14.1169185638, + 5.8365726471, -15.1750888824, 5.7879953384, -16.2063865662, + 5.7364420891, -17.2093257904, 5.6820459366, -18.1825466156, + 5.6249494553, -19.1248283386, 5.5653042793, -20.0350952148, + 5.5032711029, -20.9124298096, 5.4390201569, -21.7560806274, + 5.3727307320, -22.5654678345, 5.3045911789, -23.3402004242, + 5.2347993851, -24.0800762177, 5.1635618210, -24.7850971222, + 5.0910949707, -25.4554748535, 5.0176239014, -26.0916442871, + 4.9433832169, -26.6942691803, 4.8686161041, -27.2642498016, + 4.7935757637, -27.8027400970, 4.7185239792, -28.3111438751, + 5.6457915306, 28.2289581299, 5.7352571487, 27.7204093933, + 5.8247289658, 27.1820678711, 5.9138932228, 26.6125202179, + 6.0024461746, 26.0106010437, 6.0900959969, 25.3753986359, + 6.1765604019, 24.7062416077, 6.2615699768, 24.0026855469, + 6.3448653221, 23.2645053864, 6.4261975288, 22.4916915894, + 6.5053300858, 21.6844348907, 6.5820369720, 20.8431167603, + 6.6561026573, 19.9683074951, 6.7273230553, 19.0607490540, + 6.7955055237, 18.1213474274, 6.8604674339, 17.1511688232, + 6.9220380783, 16.1514225006, 6.9800572395, 15.1234579086, + 7.0343766212, 14.0687532425, 7.0848579407, 12.9889059067, + 7.1313738823, 11.8856229782, 7.1738095284, 10.7607145309, + 7.2120594978, 9.6160793304, 7.2460298538, 8.4537019730, + 7.2756385803, 7.2756385803, 7.3008131981, 6.0840110779, + 7.3214931488, 4.8809957504, 7.3376293182, 3.6688146591, + 7.3491826057, 2.4497275352, 7.3561258316, 1.2260209322, + 7.3584418297, -0.0000000000, 7.3561258316, -1.2260209322, + 7.3491826057, -2.4497275352, 7.3376293182, -3.6688146591, + 7.3214931488, -4.8809957504, 7.3008131981, -6.0840110779, + 7.2756385803, -7.2756385803, 7.2460298538, -8.4537019730, + 7.2120594978, -9.6160793304, 7.1738095284, -10.7607145309, + 7.1313738823, -11.8856229782, 7.0848579407, -12.9889059067, + 7.0343766212, -14.0687532425, 6.9800572395, -15.1234579086, + 6.9220380783, -16.1514225006, 6.8604674339, -17.1511688232, + 6.7955055237, -18.1213474274, 6.7273230553, -19.0607490540, + 6.6561026573, -19.9683074951, 6.5820369720, -20.8431167603, + 6.5053300858, -21.6844348907, 6.4261975288, -22.4916915894, + 6.3448653221, -23.2645053864, 6.2615699768, -24.0026855469, + 6.1765604019, -24.7062416077, 6.0900959969, -25.3753986359, + 6.0024461746, -26.0106010437, 5.9138932228, -26.6125202179, + 5.8247289658, -27.1820678711, 5.7352571487, -27.7204093933, + 5.6457915306, -28.2289581299, 6.5642604828, 28.1325454712, + 6.6678142548, 27.6238021851, 6.7714037895, 27.0856151581, + 6.8746614456, 26.5165519714, 6.9772343636, 25.9154434204, + 7.0787811279, 25.2813606262, 7.1789731979, 24.6136226654, + 7.2774958611, 23.9117717743, 7.3740458488, 23.1755733490, + 7.4683341980, 22.4050025940, 7.5600843430, 21.6002407074, + 7.6490316391, 20.7616577148, 7.7349257469, 19.8898086548, + 7.8175282478, 18.9854259491, 7.8966135979, 18.0494022369, + 7.9719705582, 17.0827941895, 8.0433988571, 16.0867977142, + 8.1107110977, 15.0627498627, 8.1737356186, 14.0121173859, + 8.2323093414, 12.9364862442, 8.2862854004, 11.8375511169, + 8.3355283737, 10.7171087265, 8.3799161911, 9.5770473480, + 8.4193401337, 8.4193401337, 8.4537019730, 7.2460298538, + 8.4829187393, 6.0592274666, 8.5069198608, 4.8610973358, + 8.5256481171, 3.6538491249, 8.5390567780, 2.4397306442, + 8.5471153259, 1.2210165262, 8.5498037338, -0.0000000000, + 8.5471153259, -1.2210165262, 8.5390567780, -2.4397306442, + 8.5256481171, -3.6538491249, 8.5069198608, -4.8610973358, + 8.4829187393, -6.0592274666, 8.4537019730, -7.2460298538, + 8.4193401337, -8.4193401337, 8.3799161911, -9.5770473480, + 8.3355283737, -10.7171087265, 8.2862854004, -11.8375511169, + 8.2323093414, -12.9364862442, 8.1737356186, -14.0121173859, + 8.1107110977, -15.0627498627, 8.0433988571, -16.0867977142, + 7.9719705582, -17.0827941895, 7.8966135979, -18.0494022369, + 7.8175282478, -18.9854259491, 7.7349257469, -19.8898086548, + 7.6490316391, -20.7616577148, 7.5600843430, -21.6002407074, + 7.4683341980, -22.4050025940, 7.3740458488, -23.1755733490, + 7.2774958611, -23.9117717743, 7.1789731979, -24.6136226654, + 7.0787811279, -25.2813606262, 6.9772343636, -25.9154434204, + 6.8746614456, -26.5165519714, 6.7714037895, -27.0856151581, + 6.6678142548, -27.6238021851, 6.5642604828, -28.1325454712, + 7.4726042747, 28.0222644806, 7.5898661613, 27.5132656097, + 7.7072062492, 26.9752216339, 7.8242039680, 26.4066886902, + 7.9404559135, 25.8064804077, 8.0555715561, 25.1736602783, + 8.1691761017, 24.5075283051, 8.2809085846, 23.8076114655, + 8.3904247284, 23.0736675262, 8.4973917007, 22.3056526184, + 8.6014947891, 21.5037364960, 8.7024316788, 20.6682758331, + 8.7999162674, 19.7998123169, 8.8936758041, 18.8990612030, + 8.9834527969, 17.9669055939, 9.0690040588, 17.0043830872, + 9.1501035690, 16.0126800537, 9.2265357971, 14.9931211472, + 9.2981033325, 13.9471549988, 9.3646221161, 12.8763561249, + 9.4259233475, 11.7824039459, 9.4818515778, 10.6670837402, + 9.5322685242, 9.5322685242, 9.5770473480, 8.3799161911, + 9.6160793304, 7.2120594978, 9.6492681503, 6.0307927132, + 9.6765327454, 4.8382663727, 9.6978073120, 3.6366777420, + 9.7130403519, 2.4282600880, 9.7221946716, 1.2152743340, + 9.7252483368, -0.0000000000, 9.7221946716, -1.2152743340, + 9.7130403519, -2.4282600880, 9.6978073120, -3.6366777420, + 9.6765327454, -4.8382663727, 9.6492681503, -6.0307927132, + 9.6160793304, -7.2120594978, 9.5770473480, -8.3799161911, + 9.5322685242, -9.5322685242, 9.4818515778, -10.6670837402, + 9.4259233475, -11.7824039459, 9.3646221161, -12.8763561249, + 9.2981033325, -13.9471549988, 9.2265357971, -14.9931211472, + 9.1501035690, -16.0126800537, 9.0690040588, -17.0043830872, + 8.9834527969, -17.9669055939, 8.8936758041, -18.8990612030, + 8.7999162674, -19.7998123169, 8.7024316788, -20.6682758331, + 8.6014947891, -21.5037364960, 8.4973917007, -22.3056526184, + 8.3904247284, -23.0736675262, 8.2809085846, -23.8076114655, + 8.1691761017, -24.5075283051, 8.0555715561, -25.1736602783, + 7.9404559135, -25.8064804077, 7.8242039680, -26.4066886902, + 7.7072062492, -26.9752216339, 7.5898661613, -27.5132656097, + 7.4726042747, -28.0222644806, 8.3695592880, 27.8985328674, + 8.5000963211, 27.3892002106, 8.6307678223, 26.8512763977, + 8.7611007690, 26.2833023071, 8.8906412125, 25.6840744019, + 9.0189504623, 25.0526409149, 9.1456069946, 24.3882865906, + 9.2702045441, 23.6905231476, 9.3923549652, 22.9590892792, + 9.5116853714, 22.1939334869, 9.6278400421, 21.3952007294, + 9.7404804230, 20.5632362366, 9.8492822647, 19.6985645294, + 9.9539413452, 18.8018894196, 10.0541667938, 17.8740749359, + 10.1496858597, 16.9161434174, 10.2402420044, 15.9292659760, + 10.3255958557, 14.9147500992, 10.4055233002, 13.8740310669, + 10.4798183441, 12.8086662292, 10.5482892990, 11.7203216553, + 10.6107635498, 10.6107635498, 10.6670837402, 9.4818515778, + 10.7171087265, 8.3355283737, 10.7607145309, 7.1738095284, + 10.7977933884, 5.9987745285, 10.8282556534, 4.8125581741, + 10.8520250320, 3.6173417568, 10.8690452576, 2.4153432846, + 10.8792734146, 1.2088081837, 10.8826856613, -0.0000000000, + 10.8792734146, -1.2088081837, 10.8690452576, -2.4153432846, + 10.8520250320, -3.6173417568, 10.8282556534, -4.8125581741, + 10.7977933884, -5.9987745285, 10.7607145309, -7.1738095284, + 10.7171087265, -8.3355283737, 10.6670837402, -9.4818515778, + 10.6107635498, -10.6107635498, 10.5482892990, -11.7203216553, + 10.4798183441, -12.8086662292, 10.4055233002, -13.8740310669, + 10.3255958557, -14.9147500992, 10.2402420044, -15.9292659760, + 10.1496858597, -16.9161434174, 10.0541667938, -17.8740749359, + 9.9539413452, -18.8018894196, 9.8492822647, -19.6985645294, + 9.7404804230, -20.5632362366, 9.6278400421, -21.3952007294, + 9.5116853714, -22.1939334869, 9.3923549652, -22.9590892792, + 9.2702045441, -23.6905231476, 9.1456069946, -24.3882865906, + 9.0189504623, -25.0526409149, 8.8906412125, -25.6840744019, + 8.7611007690, -26.2833023071, 8.6307678223, -26.8512763977, + 8.5000963211, -27.3892002106, 8.3695592880, -27.8985328674, + 9.2539377213, 27.7618141174, 9.3972616196, 27.2520580292, + 9.5407915115, 26.7142162323, 9.6840057373, 26.1468143463, + 9.8263969421, 25.5486316681, 9.9674777985, 24.9186954498, + 10.1067810059, 24.2562732697, 10.2438545227, 23.5608654022, + 10.3782663345, 22.8321857452, 10.5096044540, 22.0701675415, + 10.6374711990, 21.2749423981, 10.7614917755, 20.4468326569, + 10.8813056946, 19.5863494873, 10.9965744019, 18.6941757202, + 11.1069755554, 17.7711601257, 11.2122049332, 16.8183078766, + 11.3119792938, 15.8367710114, 11.4060306549, 14.8278398514, + 11.4941110611, 13.7929334641, 11.5759906769, 12.7335901260, + 11.6514587402, 11.6514587402, 11.7203216553, 10.5482892990, + 11.7824039459, 9.4259233475, 11.8375511169, 8.2862854004, + 11.8856229782, 7.1313738823, 11.9265022278, 5.9632511139, + 11.9600868225, 4.7840347290, 11.9862937927, 3.5958881378, + 12.0050592422, 2.4010119438, 12.0163364410, 1.2016336918, + 12.0200986862, -0.0000000000, 12.0163364410, -1.2016336918, + 12.0050592422, -2.4010119438, 11.9862937927, -3.5958881378, + 11.9600868225, -4.7840347290, 11.9265022278, -5.9632511139, + 11.8856229782, -7.1313738823, 11.8375511169, -8.2862854004, + 11.7824039459, -9.4259233475, 11.7203216553, -10.5482892990, + 11.6514587402, -11.6514587402, 11.5759906769, -12.7335901260, + 11.4941110611, -13.7929334641, 11.4060306549, -14.8278398514, + 11.3119792938, -15.8367710114, 11.2122049332, -16.8183078766, + 11.1069755554, -17.7711601257, 10.9965744019, -18.6941757202, + 10.8813056946, -19.5863494873, 10.7614917755, -20.4468326569, + 10.6374711990, -21.2749423981, 10.5096044540, -22.0701675415, + 10.3782663345, -22.8321857452, 10.2438545227, -23.5608654022, + 10.1067810059, -24.2562732697, 9.9674777985, -24.9186954498, + 9.8263969421, -25.5486316681, 9.6840057373, -26.1468143463, + 9.5407915115, -26.7142162323, 9.3972616196, -27.2520580292, + 9.2539377213, -27.7618141174, 10.1246328354, 27.6126346588, + 10.2802000046, 27.1023464203, 10.4360666275, 26.5645332336, + 10.5916547775, 25.9976978302, 10.7464084625, 25.4006023407, + 10.8997936249, 24.7722568512, 11.0512914658, 24.1119098663, + 11.2004089355, 23.4190368652, 11.3466701508, 22.6933403015, + 11.4896192551, 21.9347286224, 11.6288223267, 21.1433124542, + 11.7638635635, 20.3194007874, 11.8943500519, 19.4634819031, + 12.0199069977, 18.5762195587, 12.1401796341, 17.6584434509, + 12.2548351288, 16.7111396790, 12.3635606766, 15.7354402542, + 12.4660615921, 14.7326173782, 12.5620651245, 13.7040710449, + 12.6513185501, 12.6513185501, 12.7335901260, 11.5759906769, + 12.8086662292, 10.4798183441, 12.8763561249, 9.3646221161, + 12.9364862442, 8.2323093414, 12.9889059067, 7.0848579407, + 13.0334835052, 5.9243106842, 13.0701084137, 4.7527666092, + 13.0986881256, 3.5723693371, 13.1191530228, 2.3853003979, + 13.1314516068, 1.1937683821, 13.1355552673, -0.0000000000, + 13.1314516068, -1.1937683821, 13.1191530228, -2.3853003979, + 13.0986881256, -3.5723693371, 13.0701084137, -4.7527666092, + 13.0334835052, -5.9243106842, 12.9889059067, -7.0848579407, + 12.9364862442, -8.2323093414, 12.8763561249, -9.3646221161, + 12.8086662292, -10.4798183441, 12.7335901260, -11.5759906769, + 12.6513185501, -12.6513185501, 12.5620651245, -13.7040710449, + 12.4660615921, -14.7326173782, 12.3635606766, -15.7354402542, + 12.2548351288, -16.7111396790, 12.1401796341, -17.6584434509, + 12.0199069977, -18.5762195587, 11.8943500519, -19.4634819031, + 11.7638635635, -20.3194007874, 11.6288223267, -21.1433124542, + 11.4896192551, -21.9347286224, 11.3466701508, -22.6933403015, + 11.2004089355, -23.4190368652, 11.0512914658, -24.1119098663, + 10.8997936249, -24.7722568512, 10.7464084625, -25.4006023407, + 10.5916547775, -25.9976978302, 10.4360666275, -26.5645332336, + 10.2802000046, -27.1023464203, 10.1246328354, -27.6126346588, + 10.9806299210, 27.4515743256, 11.1478443146, 26.9406242371, + 11.3154697418, 26.4027633667, 11.4828767776, 25.8364715576, + 11.6494579315, 25.2404918671, 11.8146295547, 24.6138114929, + 11.9778289795, 23.9556579590, 12.1385145187, 23.2654857635, + 12.2961692810, 22.5429763794, 12.4502954483, 21.7880172729, + 12.6004190445, 21.0006980896, 12.7460880280, 20.1813049316, + 12.8868713379, 19.3303070068, 13.0223627090, 18.4483470917, + 13.1521739960, 17.5362319946, 13.2759418488, 16.5949268341, + 13.3933238983, 15.6255445480, 13.5040016174, 14.6293354034, + 13.6076755524, 13.6076755524, 13.7040710449, 12.5620651245, + 13.7929334641, 11.4941110611, 13.8740310669, 10.4055233002, + 13.9471549988, 9.2981033325, 14.0121173859, 8.1737356186, + 14.0687532425, 7.0343766212, 14.1169185638, 5.8820490837, + 14.1564912796, 4.7188305855, 14.1873731613, 3.5468432903, + 14.2094869614, 2.3682479858, 14.2227773666, 1.1852314472, + 14.2272119522, -0.0000000000, 14.2227773666, -1.1852314472, + 14.2094869614, -2.3682479858, 14.1873731613, -3.5468432903, + 14.1564912796, -4.7188305855, 14.1169185638, -5.8820490837, + 14.0687532425, -7.0343766212, 14.0121173859, -8.1737356186, + 13.9471549988, -9.2981033325, 13.8740310669, -10.4055233002, + 13.7929334641, -11.4941110611, 13.7040710449, -12.5620651245, + 13.6076755524, -13.6076755524, 13.5040016174, -14.6293354034, + 13.3933238983, -15.6255445480, 13.2759418488, -16.5949268341, + 13.1521739960, -17.5362319946, 13.0223627090, -18.4483470917, + 12.8868713379, -19.3303070068, 12.7460880280, -20.1813049316, + 12.6004190445, -21.0006980896, 12.4502954483, -21.7880172729, + 12.2961692810, -22.5429763794, 12.1385145187, -23.2654857635, + 11.9778289795, -23.9556579590, 11.8146295547, -24.6138114929, + 11.6494579315, -25.2404918671, 11.4828767776, -25.8364715576, + 11.3154697418, -26.4027633667, 11.1478443146, -26.9406242371, + 10.9806299210, -27.4515743256, 11.8210153580, 27.2792663574, + 11.9992265701, 26.7675056458, 12.1779823303, 26.2294998169, + 12.3566007614, 25.6637096405, 12.5344247818, 25.0688495636, + 12.7108211517, 24.4438858032, 12.8851795197, 23.7880249023, + 13.0569162369, 23.1006965637, 13.2254667282, 22.3815593719, + 13.3902959824, 21.6304779053, 13.5508880615, 20.8475208282, + 13.7067537308, 20.0329475403, 13.8574275970, 19.1872081757, + 14.0024662018, 18.3109169006, 14.1414518356, 17.4048633575, + 14.2739896774, 16.4699878693, 14.3997106552, 15.5073804855, + 14.5182666779, 14.5182666779, 14.6293354034, 13.5040016174, + 14.7326173782, 12.4660615921, 14.8278398514, 11.4060306549, + 14.9147500992, 10.3255958557, 14.9931211472, 9.2265357971, + 15.0627498627, 8.1107110977, 15.1234579086, 6.9800572395, + 15.1750888824, 5.8365726471, 15.2175111771, 4.6823110580, + 15.2506179810, 3.5193734169, 15.2743263245, 2.3498964310, + 15.2885742188, 1.1760442257, 15.2933282852, -0.0000000000, + 15.2885742188, -1.1760442257, 15.2743263245, -2.3498964310, + 15.2506179810, -3.5193734169, 15.2175111771, -4.6823110580, + 15.1750888824, -5.8365726471, 15.1234579086, -6.9800572395, + 15.0627498627, -8.1107110977, 14.9931211472, -9.2265357971, + 14.9147500992, -10.3255958557, 14.8278398514, -11.4060306549, + 14.7326173782, -12.4660615921, 14.6293354034, -13.5040016174, + 14.5182666779, -14.5182666779, 14.3997106552, -15.5073804855, + 14.2739896774, -16.4699878693, 14.1414518356, -17.4048633575, + 14.0024662018, -18.3109169006, 13.8574275970, -19.1872081757, + 13.7067537308, -20.0329475403, 13.5508880615, -20.8475208282, + 13.3902959824, -21.6304779053, 13.2254667282, -22.3815593719, + 13.0569162369, -23.1006965637, 12.8851795197, -23.7880249023, + 12.7108211517, -24.4438858032, 12.5344247818, -25.0688495636, + 12.3566007614, -25.6637096405, 12.1779823303, -26.2294998169, + 11.9992265701, -26.7675056458, 11.8210153580, -27.2792663574, + 12.6449871063, 27.0964012146, 12.8334894180, 26.5836582184, + 13.0226945877, 26.0453891754, 13.2118673325, 25.4800300598, + 13.4003000259, 24.8862724304, 13.5873117447, 24.2630558014, + 13.7722444534, 23.6095619202, 13.9544687271, 22.9251995087, + 14.1333789825, 22.2095966339, 14.3083972931, 21.4625949860, + 14.4789676666, 20.6842403412, 14.6445646286, 19.8747653961, + 14.8046855927, 19.0345954895, 14.9588537216, 18.1643218994, + 15.1066188812, 17.2647075653, 15.2475576401, 16.3366680145, + 15.3812685013, 15.3812685013, 15.5073804855, 14.3997106552, + 15.6255445480, 13.3933238983, 15.7354402542, 12.3635606766, + 15.8367710114, 11.3119792938, 15.9292659760, 10.2402420044, + 16.0126800537, 9.1501035690, 16.0867977142, 8.0433988571, + 16.1514225006, 6.9220380783, 16.2063865662, 5.7879953384, + 16.2515525818, 4.6433005333, 16.2868003845, 3.4900286198, + 16.3120422363, 2.3302917480, 16.3272132874, 1.1662294865, + 16.3322734833, -0.0000000000, 16.3272132874, -1.1662294865, + 16.3120422363, -2.3302917480, 16.2868003845, -3.4900286198, + 16.2515525818, -4.6433005333, 16.2063865662, -5.7879953384, + 16.1514225006, -6.9220380783, 16.0867977142, -8.0433988571, + 16.0126800537, -9.1501035690, 15.9292659760, -10.2402420044, + 15.8367710114, -11.3119792938, 15.7354402542, -12.3635606766, + 15.6255445480, -13.3933238983, 15.5073804855, -14.3997106552, + 15.3812685013, -15.3812685013, 15.2475576401, -16.3366680145, + 15.1066188812, -17.2647075653, 14.9588537216, -18.1643218994, + 14.8046855927, -19.0345954895, 14.6445646286, -19.8747653961, + 14.4789676666, -20.6842403412, 14.3083972931, -21.4625949860, + 14.1333789825, -22.2095966339, 13.9544687271, -22.9251995087, + 13.7722444534, -23.6095619202, 13.5873117447, -24.2630558014, + 13.4003000259, -24.8862724304, 13.2118673325, -25.4800300598, + 13.0226945877, -26.0453891754, 12.8334894180, -26.5836582184, + 12.6449871063, -27.0964012146, 13.4518613815, 26.9037227631, + 13.6498956680, 26.3897991180, 13.8488159180, 25.8511238098, + 14.0478353500, 25.2861042023, 14.2461948395, 24.6934051514, + 14.4431657791, 24.0719413757, 14.6380424500, 23.4208679199, + 14.8301496506, 22.7395629883, 15.0188398361, 22.0276317596, + 15.2034921646, 21.2848892212, 15.3835144043, 20.5113525391, + 15.5583400726, 19.7072315216, 15.7274322510, 18.8729190826, + 15.8902797699, 18.0089836121, 16.0464000702, 17.1161594391, + 16.1953392029, 16.1953392029, 16.3366680145, 15.2475576401, + 16.4699878693, 14.2739896774, 16.5949268341, 13.2759418488, + 16.7111396790, 12.2548351288, 16.8183078766, 11.2122049332, + 16.9161434174, 10.1496858597, 17.0043830872, 9.0690040588, + 17.0827941895, 7.9719705582, 17.1511688232, 6.8604674339, + 17.2093257904, 5.7364420891, 17.2571182251, 4.6018981934, + 17.2944164276, 3.4588835239, 17.3211288452, 2.3094837666, + 17.3371829987, 1.1558121443, 17.3425388336, -0.0000000000, + 17.3371829987, -1.1558121443, 17.3211288452, -2.3094837666, + 17.2944164276, -3.4588835239, 17.2571182251, -4.6018981934, + 17.2093257904, -5.7364420891, 17.1511688232, -6.8604674339, + 17.0827941895, -7.9719705582, 17.0043830872, -9.0690040588, + 16.9161434174, -10.1496858597, 16.8183078766, -11.2122049332, + 16.7111396790, -12.2548351288, 16.5949268341, -13.2759418488, + 16.4699878693, -14.2739896774, 16.3366680145, -15.2475576401, + 16.1953392029, -16.1953392029, 16.0464000702, -17.1161594391, + 15.8902797699, -18.0089836121, 15.7274322510, -18.8729190826, + 15.5583400726, -19.7072315216, 15.3835144043, -20.5113525391, + 15.2034921646, -21.2848892212, 15.0188398361, -22.0276317596, + 14.8301496506, -22.7395629883, 14.6380424500, -23.4208679199, + 14.4431657791, -24.0719413757, 14.2461948395, -24.6934051514, + 14.0478353500, -25.2861042023, 13.8488159180, -25.8511238098, + 13.6498956680, -26.3897991180, 13.4518613815, -26.9037227631, + 14.2410821915, 26.7020301819, 14.4478359222, 26.1867027283, + 14.6556854248, 25.6474494934, 14.8637924194, 25.0826492310, + 15.0713491440, 24.4909420013, 15.2775745392, 23.8712100983, + 15.4817190170, 23.2225780487, 15.6830615997, 22.5444011688, + 15.8809118271, 21.8362541199, 16.0746059418, 21.0979213715, + 16.2635135651, 20.3293914795, 16.4470310211, 19.5308494568, + 16.6245822906, 18.7026557922, 16.7956275940, 17.8453540802, + 16.9596481323, 16.9596481323, 17.1161594391, 16.0464000702, + 17.2647075653, 15.1066188812, 17.4048633575, 14.1414518356, + 17.5362319946, 13.1521739960, 17.6584434509, 12.1401796341, + 17.7711601257, 11.1069755554, 17.8740749359, 10.0541667938, + 17.9669055939, 8.9834527969, 18.0494022369, 7.8966135979, + 18.1213474274, 6.7955055237, 18.1825466156, 5.6820459366, + 18.2328395844, 4.5582098961, 18.2720947266, 3.4260177612, + 18.3002071381, 2.2875258923, 18.3171043396, 1.1448190212, + 18.3227405548, -0.0000000000, 18.3171043396, -1.1448190212, + 18.3002071381, -2.2875258923, 18.2720947266, -3.4260177612, + 18.2328395844, -4.5582098961, 18.1825466156, -5.6820459366, + 18.1213474274, -6.7955055237, 18.0494022369, -7.8966135979, + 17.9669055939, -8.9834527969, 17.8740749359, -10.0541667938, + 17.7711601257, -11.1069755554, 17.6584434509, -12.1401796341, + 17.5362319946, -13.1521739960, 17.4048633575, -14.1414518356, + 17.2647075653, -15.1066188812, 17.1161594391, -16.0464000702, + 16.9596481323, -16.9596481323, 16.7956275940, -17.8453540802, + 16.6245822906, -18.7026557922, 16.4470310211, -19.5308494568, + 16.2635135651, -20.3293914795, 16.0746059418, -21.0979213715, + 15.8809118271, -21.8362541199, 15.6830615997, -22.5444011688, + 15.4817190170, -23.2225780487, 15.2775745392, -23.8712100983, + 15.0713491440, -24.4909420013, 14.8637924194, -25.0826492310, + 14.6556854248, -25.6474494934, 14.4478359222, -26.1867027283, + 14.2410821915, -26.7020301819, 15.0122346878, 26.4921798706, + 15.2268390656, 25.9751949310, 15.4427795410, 25.4351654053, + 15.6591653824, 24.8704395294, 15.8751382828, 24.2796230316, + 16.0898685455, 23.6615715027, 16.3025608063, 23.0153789520, + 16.5124473572, 22.3403701782, 16.7187938690, 21.6360874176, + 16.9208984375, 20.9022865295, 17.1180896759, 20.1389274597, + 17.3097229004, 19.3461608887, 17.4951934814, 18.5243206024, + 17.6739177704, 17.6739177704, 17.8453540802, 16.7956275940, + 18.0089836121, 15.8902797699, 18.1643218994, 14.9588537216, + 18.3109169006, 14.0024662018, 18.4483470917, 13.0223627090, + 18.5762195587, 12.0199069977, 18.6941757202, 10.9965744019, + 18.8018894196, 9.9539413452, 18.8990612030, 8.8936758041, + 18.9854259491, 7.8175282478, 19.0607490540, 6.7273230553, + 19.1248283386, 5.6249494553, 19.1774902344, 4.5123505592, + 19.2185974121, 3.3915169239, 19.2480354309, 2.2644748688, + 19.2657318115, 1.1332782507, 19.2716350555, -0.0000000000, + 19.2657318115, -1.1332782507, 19.2480354309, -2.2644748688, + 19.2185974121, -3.3915169239, 19.1774902344, -4.5123505592, + 19.1248283386, -5.6249494553, 19.0607490540, -6.7273230553, + 18.9854259491, -7.8175282478, 18.8990612030, -8.8936758041, + 18.8018894196, -9.9539413452, 18.6941757202, -10.9965744019, + 18.5762195587, -12.0199069977, 18.4483470917, -13.0223627090, + 18.3109169006, -14.0024662018, 18.1643218994, -14.9588537216, + 18.0089836121, -15.8902797699, 17.8453540802, -16.7956275940, + 17.6739177704, -17.6739177704, 17.4951934814, -18.5243206024, + 17.3097229004, -19.3461608887, 17.1180896759, -20.1389274597, + 16.9208984375, -20.9022865295, 16.7187938690, -21.6360874176, + 16.5124473572, -22.3403701782, 16.3025608063, -23.0153789520, + 16.0898685455, -23.6615715027, 15.8751382828, -24.2796230316, + 15.6591653824, -24.8704395294, 15.4427795410, -25.4351654053, + 15.2268390656, -25.9751949310, 15.0122346878, -26.4921798706, + 15.7650489807, 26.2750816345, 15.9865808487, 25.7561588287, + 16.2097206116, 25.2151222229, 16.4335269928, 24.6502914429, + 16.6570873260, 24.0602378845, 16.8795280457, 23.4437885284, + 17.1000003815, 22.7999992371, 17.3176956177, 22.1281681061, + 17.5318374634, 21.4278011322, 17.7416801453, 20.6986255646, + 17.9465122223, 19.9405689240, 18.1456527710, 19.1537456512, + 18.3384609222, 18.3384609222, 18.5243206024, 17.4951934814, + 18.7026557922, 16.6245822906, 18.8729190826, 15.7274322510, + 19.0345954895, 14.8046855927, 19.1872081757, 13.8574275970, + 19.3303070068, 12.8868713379, 19.4634819031, 11.8943500519, + 19.5863494873, 10.8813056946, 19.6985645294, 9.8492822647, + 19.7998123169, 8.7999162674, 19.8898086548, 7.7349257469, + 19.9683074951, 6.6561026573, 20.0350952148, 5.5653042793, + 20.0899868011, 4.4644412994, 20.1328353882, 3.3554723263, + 20.1635227203, 2.2403914928, 20.1819686890, 1.1212204695, + 20.1881237030, -0.0000000000, 20.1819686890, -1.1212204695, + 20.1635227203, -2.2403914928, 20.1328353882, -3.3554723263, + 20.0899868011, -4.4644412994, 20.0350952148, -5.5653042793, + 19.9683074951, -6.6561026573, 19.8898086548, -7.7349257469, + 19.7998123169, -8.7999162674, 19.6985645294, -9.8492822647, + 19.5863494873, -10.8813056946, 19.4634819031, -11.8943500519, + 19.3303070068, -12.8868713379, 19.1872081757, -13.8574275970, + 19.0345954895, -14.8046855927, 18.8729190826, -15.7274322510, + 18.7026557922, -16.6245822906, 18.5243206024, -17.4951934814, + 18.3384609222, -18.3384609222, 18.1456527710, -19.1537456512, + 17.9465122223, -19.9405689240, 17.7416801453, -20.6986255646, + 17.5318374634, -21.4278011322, 17.3176956177, -22.1281681061, + 17.1000003815, -22.7999992371, 16.8795280457, -23.4437885284, + 16.6570873260, -24.0602378845, 16.4335269928, -24.6502914429, + 16.2097206116, -25.2151222229, 15.9865808487, -25.7561588287, + 15.7650489807, -26.2750816345, 16.4994106293, 26.0517005920, + 16.7268943787, 25.5305233002, 16.9562911987, 24.9882202148, + 17.1866073608, 24.4230728149, 17.4168796539, 23.8336238861, + 17.6461811066, 23.2186603546, 17.8736248016, 22.5772113800, + 18.0983524323, 21.9085330963, 18.3195438385, 21.2121028900, + 18.5364131927, 20.4876136780, 18.7482070923, 19.7349548340, + 18.9542121887, 18.9542121887, 19.1537456512, 18.1456527710, + 19.3461608887, 17.3097229004, 19.5308494568, 16.4470310211, + 19.7072315216, 15.5583400726, 19.8747653961, 14.6445646286, + 20.0329475403, 13.7067537308, 20.1813049316, 12.7460880280, + 20.3194007874, 11.7638635635, 20.4468326569, 10.7614917755, + 20.5632362366, 9.7404804230, 20.6682758331, 8.7024316788, + 20.7616577148, 7.6490316391, 20.8431167603, 6.5820369720, + 20.9124298096, 5.5032711029, 20.9694004059, 4.4146108627, + 21.0138759613, 3.3179802895, 21.0457305908, 2.2153401375, + 21.0648784637, 1.1086778641, 21.0712661743, -0.0000000000, + 21.0648784637, -1.1086778641, 21.0457305908, -2.2153401375, + 21.0138759613, -3.3179802895, 20.9694004059, -4.4146108627, + 20.9124298096, -5.5032711029, 20.8431167603, -6.5820369720, + 20.7616577148, -7.6490316391, 20.6682758331, -8.7024316788, + 20.5632362366, -9.7404804230, 20.4468326569, -10.7614917755, + 20.3194007874, -11.7638635635, 20.1813049316, -12.7460880280, + 20.0329475403, -13.7067537308, 19.8747653961, -14.6445646286, + 19.7072315216, -15.5583400726, 19.5308494568, -16.4470310211, + 19.3461608887, -17.3097229004, 19.1537456512, -18.1456527710, + 18.9542121887, -18.9542121887, 18.7482070923, -19.7349548340, + 18.5364131927, -20.4876136780, 18.3195438385, -21.2121028900, + 18.0983524323, -21.9085330963, 17.8736248016, -22.5772113800, + 17.6461811066, -23.2186603546, 17.4168796539, -23.8336238861, + 17.1866073608, -24.4230728149, 16.9562911987, -24.9882202148, + 16.7268943787, -25.5305233002, 16.4994106293, -26.0517005920, + 17.2153701782, 25.8230571747, 17.4477767944, 25.2992763519, + 17.6824359894, 24.7554092407, 17.9182987213, 24.1897048950, + 18.1543560028, 23.6006641388, 18.3896331787, 22.9870414734, + 18.6231899261, 22.3478298187, 18.8541297913, 21.6822490692, + 19.0815830231, 20.9897422791, 19.3047275543, 20.2699642181, + 19.5227680206, 19.5227680206, 19.7349548340, 18.7482070923, + 19.9405689240, 17.9465122223, 20.1389274597, 17.1180896759, + 20.3293914795, 16.2635135651, 20.5113525391, 15.3835144043, + 20.6842403412, 14.4789676666, 20.8475208282, 13.5508880615, + 21.0006980896, 12.6004190445, 21.1433124542, 11.6288223267, + 21.2749423981, 10.6374711990, 21.3952007294, 9.6278400421, + 21.5037364960, 8.6014947891, 21.6002407074, 7.5600843430, + 21.6844348907, 6.5053300858, 21.7560806274, 5.4390201569, + 21.8149738312, 4.3629951477, 21.8609542847, 3.2791430950, + 21.8938865662, 2.1893887520, 21.9136848450, 1.0956841707, + 21.9202899933, -0.0000000000, 21.9136848450, -1.0956841707, + 21.8938865662, -2.1893887520, 21.8609542847, -3.2791430950, + 21.8149738312, -4.3629951477, 21.7560806274, -5.4390201569, + 21.6844348907, -6.5053300858, 21.6002407074, -7.5600843430, + 21.5037364960, -8.6014947891, 21.3952007294, -9.6278400421, + 21.2749423981, -10.6374711990, 21.1433124542, -11.6288223267, + 21.0006980896, -12.6004190445, 20.8475208282, -13.5508880615, + 20.6842403412, -14.4789676666, 20.5113525391, -15.3835144043, + 20.3293914795, -16.2635135651, 20.1389274597, -17.1180896759, + 19.9405689240, -17.9465122223, 19.7349548340, -18.7482070923, + 19.5227680206, -19.5227680206, 19.3047275543, -20.2699642181, + 19.0815830231, -20.9897422791, 18.8541297913, -21.6822490692, + 18.6231899261, -22.3478298187, 18.3896331787, -22.9870414734, + 18.1543560028, -23.6006641388, 17.9182987213, -24.1897048950, + 17.6824359894, -24.7554092407, 17.4477767944, -25.2992763519, + 17.2153701782, -25.8230571747, 17.9131584167, 25.5902252197, + 18.1494007111, 25.0634574890, 18.3882732391, 24.5176963806, + 18.6286735535, 23.9511528015, 18.8695411682, 23.3622894287, + 19.1098537445, 22.7498264313, 19.3486251831, 22.1127147675, + 19.5849094391, 21.4501399994, 19.8178005219, 20.7615051270, + 20.0464286804, 20.0464286804, 20.2699642181, 19.3047275543, + 20.4876136780, 18.5364131927, 20.6986255646, 17.7416801453, + 20.9022865295, 16.9208984375, 21.0979213715, 16.0746059418, + 21.2848892212, 15.2034921646, 21.4625949860, 14.3083972931, + 21.6304779053, 13.3902959824, 21.7880172729, 12.4502954483, + 21.9347286224, 11.4896192551, 22.0701675415, 10.5096044540, + 22.1939334869, 9.5116853714, 22.3056526184, 8.4973917007, + 22.4050025940, 7.4683341980, 22.4916915894, 6.4261975288, + 22.5654678345, 5.3727307320, 22.6261196136, 4.3097372055, + 22.6734752655, 3.2390677929, 22.7073955536, 2.1626091003, + 22.7277870178, 1.0822755098, 22.7345905304, -0.0000000000, + 22.7277870178, -1.0822755098, 22.7073955536, -2.1626091003, + 22.6734752655, -3.2390677929, 22.6261196136, -4.3097372055, + 22.5654678345, -5.3727307320, 22.4916915894, -6.4261975288, + 22.4050025940, -7.4683341980, 22.3056526184, -8.4973917007, + 22.1939334869, -9.5116853714, 22.0701675415, -10.5096044540, + 21.9347286224, -11.4896192551, 21.7880172729, -12.4502954483, + 21.6304779053, -13.3902959824, 21.4625949860, -14.3083972931, + 21.2848892212, -15.2034921646, 21.0979213715, -16.0746059418, + 20.9022865295, -16.9208984375, 20.6986255646, -17.7416801453, + 20.4876136780, -18.5364131927, 20.2699642181, -19.3047275543, + 20.0464286804, -20.0464286804, 19.8178005219, -20.7615051270, + 19.5849094391, -21.4501399994, 19.3486251831, -22.1127147675, + 19.1098537445, -22.7498264313, 18.8695411682, -23.3622894287, + 18.6286735535, -23.9511528015, 18.3882732391, -24.5176963806, + 18.1494007111, -25.0634574890, 17.9131584167, -25.5902252197, + 18.5931816101, 25.3543395996, 18.8321228027, 24.8241615295, + 19.0741081238, 24.2761363983, 19.3179836273, 23.7084350586, + 19.5626392365, 23.1194839478, 19.8070030212, 22.5079574585, + 20.0500411987, 21.8727722168, 20.2907657623, 21.2130737305, + 20.5282230377, 20.5282230377, 20.7615051270, 19.8178005219, + 20.9897422791, 19.0815830231, 21.2121028900, 18.3195438385, + 21.4278011322, 17.5318374634, 21.6360874176, 16.7187938690, + 21.8362541199, 15.8809118271, 22.0276317596, 15.0188398361, + 22.2095966339, 14.1333789825, 22.3815593719, 13.2254667282, + 22.5429763794, 12.2961692810, 22.6933403015, 11.3466701508, + 22.8321857452, 10.3782663345, 22.9590892792, 9.3923549652, + 23.0736675262, 8.3904247284, 23.1755733490, 7.3740458488, + 23.2645053864, 6.3448653221, 23.3402004242, 5.3045911789, + 23.4024372101, 4.2549886703, 23.4510307312, 3.1978678703, + 23.4858417511, 2.1350765228, 23.5067691803, 1.0684895515, + 23.5137519836, -0.0000000000, 23.5067691803, -1.0684895515, + 23.4858417511, -2.1350765228, 23.4510307312, -3.1978678703, + 23.4024372101, -4.2549886703, 23.3402004242, -5.3045911789, + 23.2645053864, -6.3448653221, 23.1755733490, -7.3740458488, + 23.0736675262, -8.3904247284, 22.9590892792, -9.3923549652, + 22.8321857452, -10.3782663345, 22.6933403015, -11.3466701508, + 22.5429763794, -12.2961692810, 22.3815593719, -13.2254667282, + 22.2095966339, -14.1333789825, 22.0276317596, -15.0188398361, + 21.8362541199, -15.8809118271, 21.6360874176, -16.7187938690, + 21.4278011322, -17.5318374634, 21.2121028900, -18.3195438385, + 20.9897422791, -19.0815830231, 20.7615051270, -19.8178005219, + 20.5282230377, -20.5282230377, 20.2907657623, -21.2130737305, + 20.0500411987, -21.8727722168, 19.8070030212, -22.5079574585, + 19.5626392365, -23.1194839478, 19.3179836273, -23.7084350586, + 19.0741081238, -24.2761363983, 18.8321228027, -24.8241615295, + 18.5931816101, -25.3543395996, 19.2560462952, 25.1165828705, + 19.4964923859, 24.5825328827, 19.7404365540, 24.0318355560, + 19.9866752625, 23.4626197815, 20.2340469360, 22.8732719421, + 20.4814300537, 22.2624244690, 20.7277450562, 21.6289520264, + 20.9719581604, 20.9719581604, 21.2130737305, 20.2907657623, + 21.4501399994, 19.5849094391, 21.6822490692, 18.8541297913, + 21.9085330963, 18.0983524323, 22.1281681061, 17.3176956177, + 22.3403701782, 16.5124473572, 22.5444011688, 15.6830615997, + 22.7395629883, 14.8301496506, 22.9251995087, 13.9544687271, + 23.1006965637, 13.0569162369, 23.2654857635, 12.1385145187, + 23.4190368652, 11.2004089355, 23.5608654022, 10.2438545227, + 23.6905231476, 9.2702045441, 23.8076114655, 8.2809085846, + 23.9117717743, 7.2774958611, 24.0026855469, 6.2615699768, + 24.0800762177, 5.2347993851, 24.1437149048, 4.1989068985, + 24.1934070587, 3.1556618214, 24.2290077209, 2.1068701744, + 24.2504119873, 1.0543657541, 24.2575531006, -0.0000000000, + 24.2504119873, -1.0543657541, 24.2290077209, -2.1068701744, + 24.1934070587, -3.1556618214, 24.1437149048, -4.1989068985, + 24.0800762177, -5.2347993851, 24.0026855469, -6.2615699768, + 23.9117717743, -7.2774958611, 23.8076114655, -8.2809085846, + 23.6905231476, -9.2702045441, 23.5608654022, -10.2438545227, + 23.4190368652, -11.2004089355, 23.2654857635, -12.1385145187, + 23.1006965637, -13.0569162369, 22.9251995087, -13.9544687271, + 22.7395629883, -14.8301496506, 22.5444011688, -15.6830615997, + 22.3403701782, -16.5124473572, 22.1281681061, -17.3176956177, + 21.9085330963, -18.0983524323, 21.6822490692, -18.8541297913, + 21.4501399994, -19.5849094391, 21.2130737305, -20.2907657623, + 20.9719581604, -20.9719581604, 20.7277450562, -21.6289520264, + 20.4814300537, -22.2624244690, 20.2340469360, -22.8732719421, + 19.9866752625, -23.4626197815, 19.7404365540, -24.0318355560, + 19.4964923859, -24.5825328827, 19.2560462952, -25.1165828705, + 19.9025573730, 24.8781986237, 20.1432590485, 24.3397712708, + 20.3879585266, 23.7859516144, 20.6353988647, 23.2148227692, + 20.8843650818, 22.6247291565, 21.1336898804, 22.0142593384, + 21.3822460175, 21.3822460175, 21.6289520264, 20.7277450562, + 21.8727722168, 20.0500411987, 22.1127147675, 19.3486251831, + 22.3478298187, 18.6231899261, 22.5772113800, 17.8736248016, + 22.7999992371, 17.1000003815, 23.0153789520, 16.3025608063, + 23.2225780487, 15.4817190170, 23.4208679199, 14.6380424500, + 23.6095619202, 13.7722444534, 23.7880249023, 12.8851795197, + 23.9556579590, 11.9778289795, 24.1119098663, 11.0512914658, + 24.2562732697, 10.1067810059, 24.3882865906, 9.1456069946, + 24.5075283051, 8.1691761017, 24.6136226654, 7.1789731979, + 24.7062416077, 6.1765604019, 24.7850971222, 5.1635618210, + 24.8499469757, 4.1416578293, 24.9005908966, 3.1125738621, + 24.9368743896, 2.0780727863, 24.9586887360, 1.0399453640, + 24.9659690857, -0.0000000000, 24.9586887360, -1.0399453640, + 24.9368743896, -2.0780727863, 24.9005908966, -3.1125738621, + 24.8499469757, -4.1416578293, 24.7850971222, -5.1635618210, + 24.7062416077, -6.1765604019, 24.6136226654, -7.1789731979, + 24.5075283051, -8.1691761017, 24.3882865906, -9.1456069946, + 24.2562732697, -10.1067810059, 24.1119098663, -11.0512914658, + 23.9556579590, -11.9778289795, 23.7880249023, -12.8851795197, + 23.6095619202, -13.7722444534, 23.4208679199, -14.6380424500, + 23.2225780487, -15.4817190170, 23.0153789520, -16.3025608063, + 22.7999992371, -17.1000003815, 22.5772113800, -17.8736248016, + 22.3478298187, -18.6231899261, 22.1127147675, -19.3486251831, + 21.8727722168, -20.0500411987, 21.6289520264, -20.7277450562, + 21.3822460175, -21.3822460175, 21.1336898804, -22.0142593384, + 20.8843650818, -22.6247291565, 20.6353988647, -23.2148227692, + 20.3879585266, -23.7859516144, 20.1432590485, -24.3397712708, + 19.9025573730, -24.8781986237, 20.5337333679, 24.6404800415, + 20.7733898163, 24.0971317291, 21.0175857544, 23.5396957397, + 21.2650127411, 22.9662132263, 21.5144042969, 22.3749809265, + 21.7645435333, 21.7645435333, 22.0142593384, 21.1336898804, + 22.2624244690, 20.4814300537, 22.5079574585, 19.8070030212, + 22.7498264313, 19.1098537445, 22.9870414734, 18.3896331787, + 23.2186603546, 17.6461811066, 23.4437885284, 16.8795280457, + 23.6615715027, 16.0898685455, 23.8712100983, 15.2775745392, + 24.0719413757, 14.4431657791, 24.2630558014, 13.5873117447, + 24.4438858032, 12.7108211517, 24.6138114929, 11.8146295547, + 24.7722568512, 10.8997936249, 24.9186954498, 9.9674777985, + 25.0526409149, 9.0189504623, 25.1736602783, 8.0555715561, + 25.2813606262, 7.0787811279, 25.3753986359, 6.0900959969, + 25.4554748535, 5.0910949707, 25.5213375092, 4.0834140778, + 25.5727767944, 3.0687332153, 25.6096363068, 2.0487709045, + 25.6317958832, 1.0252718925, 25.6391906738, -0.0000000000, + 25.6317958832, -1.0252718925, 25.6096363068, -2.0487709045, + 25.5727767944, -3.0687332153, 25.5213375092, -4.0834140778, + 25.4554748535, -5.0910949707, 25.3753986359, -6.0900959969, + 25.2813606262, -7.0787811279, 25.1736602783, -8.0555715561, + 25.0526409149, -9.0189504623, 24.9186954498, -9.9674777985, + 24.7722568512, -10.8997936249, 24.6138114929, -11.8146295547, + 24.4438858032, -12.7108211517, 24.2630558014, -13.5873117447, + 24.0719413757, -14.4431657791, 23.8712100983, -15.2775745392, + 23.6615715027, -16.0898685455, 23.4437885284, -16.8795280457, + 23.2186603546, -17.6461811066, 22.9870414734, -18.3896331787, + 22.7498264313, -19.1098537445, 22.5079574585, -19.8070030212, + 22.2624244690, -20.4814300537, 22.0142593384, -21.1336898804, + 21.7645435333, -21.7645435333, 21.5144042969, -22.3749809265, + 21.2650127411, -22.9662132263, 21.0175857544, -23.5396957397, + 20.7733898163, -24.0971317291, 20.5337333679, -24.6404800415, + 21.1508102417, 24.4047813416, 21.3880653381, 23.8559188843, + 21.6304492950, 23.2943286896, 21.8765983582, 22.7180061340, + 22.1251964569, 22.1251964569, 22.3749809265, 21.5144042969, + 22.6247291565, 20.8843650818, 22.8732719421, 20.2340469360, + 23.1194839478, 19.5626392365, 23.3622894287, 18.8695411682, + 23.6006641388, 18.1543560028, 23.8336238861, 17.4168796539, + 24.0602378845, 16.6570873260, 24.2796230316, 15.8751382828, + 24.4909420013, 15.0713491440, 24.6934051514, 14.2461948395, + 24.8862724304, 13.4003000259, 25.0688495636, 12.5344247818, + 25.2404918671, 11.6494579315, 25.4006023407, 10.7464084625, + 25.5486316681, 9.8263969421, 25.6840744019, 8.8906412125, + 25.8064804077, 7.9404559135, 25.9154434204, 6.9772343636, + 26.0106010437, 6.0024461746, 26.0916442871, 5.0176239014, + 26.1583118439, 4.0243558884, 26.2103881836, 3.0242755413, + 26.2477054596, 2.0190541744, 26.2701416016, 1.0103900433, + 26.2776298523, -0.0000000000, 26.2701416016, -1.0103900433, + 26.2477054596, -2.0190541744, 26.2103881836, -3.0242755413, + 26.1583118439, -4.0243558884, 26.0916442871, -5.0176239014, + 26.0106010437, -6.0024461746, 25.9154434204, -6.9772343636, + 25.8064804077, -7.9404559135, 25.6840744019, -8.8906412125, + 25.5486316681, -9.8263969421, 25.4006023407, -10.7464084625, + 25.2404918671, -11.6494579315, 25.0688495636, -12.5344247818, + 24.8862724304, -13.4003000259, 24.6934051514, -14.2461948395, + 24.4909420013, -15.0713491440, 24.2796230316, -15.8751382828, + 24.0602378845, -16.6570873260, 23.8336238861, -17.4168796539, + 23.6006641388, -18.1543560028, 23.3622894287, -18.8695411682, + 23.1194839478, -19.5626392365, 22.8732719421, -20.2340469360, + 22.6247291565, -20.8843650818, 22.3749809265, -21.5144042969, + 22.1251964569, -22.1251964569, 21.8765983582, -22.7180061340, + 21.6304492950, -23.2943286896, 21.3880653381, -23.8559188843, + 21.1508102417, -24.4047813416, 21.7552566528, 24.1725063324, + 21.9887008667, 23.6174926758, 22.2279090881, 23.0511646271, + 22.4714660645, 22.4714660645, 22.7180061340, 21.8765983582, + 22.9662132263, 21.2650127411, 23.2148227692, 20.6353988647, + 23.4626197815, 19.9866752625, 23.7084350586, 19.3179836273, + 23.9511528015, 18.6286735535, 24.1897048950, 17.9182987213, + 24.4230728149, 17.1866073608, 24.6502914429, 16.4335269928, + 24.8704395294, 15.6591653824, 25.0826492310, 14.8637924194, + 25.2861042023, 14.0478353500, 25.4800300598, 13.2118673325, + 25.6637096405, 12.3566007614, 25.8364715576, 11.4828767776, + 25.9976978302, 10.5916547775, 26.1468143463, 9.6840057373, + 26.2833023071, 8.7611007690, 26.4066886902, 7.8242039680, + 26.5165519714, 6.8746614456, 26.6125202179, 5.9138932228, + 26.6942691803, 4.9433832169, 26.7615280151, 3.9646706581, + 26.8140716553, 2.9793412685, 26.8517265320, 1.9890167713, + 26.8743686676, 0.9953470230, 26.8819255829, -0.0000000000, + 26.8743686676, -0.9953470230, 26.8517265320, -1.9890167713, + 26.8140716553, -2.9793412685, 26.7615280151, -3.9646706581, + 26.6942691803, -4.9433832169, 26.6125202179, -5.9138932228, + 26.5165519714, -6.8746614456, 26.4066886902, -7.8242039680, + 26.2833023071, -8.7611007690, 26.1468143463, -9.6840057373, + 25.9976978302, -10.5916547775, 25.8364715576, -11.4828767776, + 25.6637096405, -12.3566007614, 25.4800300598, -13.2118673325, + 25.2861042023, -14.0478353500, 25.0826492310, -14.8637924194, + 24.8704395294, -15.6591653824, 24.6502914429, -16.4335269928, + 24.4230728149, -17.1866073608, 24.1897048950, -17.9182987213, + 23.9511528015, -18.6286735535, 23.7084350586, -19.3179836273, + 23.4626197815, -19.9866752625, 23.2148227692, -20.6353988647, + 22.9662132263, -21.2650127411, 22.7180061340, -21.8765983582, + 22.4714660645, -22.4714660645, 22.2279090881, -23.0511646271, + 21.9887008667, -23.6174926758, 21.7552566528, -24.1725063324, + 22.3487796783, 23.9451198578, 22.5769481659, 23.3832664490, + 22.8115653992, 22.8115653992, 23.0511646271, 22.2279090881, + 23.2943286896, 21.6304492950, 23.5396957397, 21.0175857544, + 23.7859516144, 20.3879585266, 24.0318355560, 19.7404365540, + 24.2761363983, 19.0741081238, 24.5176963806, 18.3882732391, + 24.7554092407, 17.6824359894, 24.9882202148, 16.9562911987, + 25.2151222229, 16.2097206116, 25.4351654053, 15.4427795410, + 25.6474494934, 14.6556854248, 25.8511238098, 13.8488159180, + 26.0453891754, 13.0226945877, 26.2294998169, 12.1779823303, + 26.4027633667, 11.3154697418, 26.5645332336, 10.4360666275, + 26.7142162323, 9.5407915115, 26.8512763977, 8.6307678223, + 26.9752216339, 7.7072062492, 27.0856151581, 6.7714037895, + 27.1820678711, 5.8247289658, 27.2642498016, 4.8686161041, + 27.3318767548, 3.9045536518, 27.3847141266, 2.9340765476, + 27.4225845337, 1.9587560892, 27.4453582764, 0.9801913500, + 27.4529571533, -0.0000000000, 27.4453582764, -0.9801913500, + 27.4225845337, -1.9587560892, 27.3847141266, -2.9340765476, + 27.3318767548, -3.9045536518, 27.2642498016, -4.8686161041, + 27.1820678711, -5.8247289658, 27.0856151581, -6.7714037895, + 26.9752216339, -7.7072062492, 26.8512763977, -8.6307678223, + 26.7142162323, -9.5407915115, 26.5645332336, -10.4360666275, + 26.4027633667, -11.3154697418, 26.2294998169, -12.1779823303, + 26.0453891754, -13.0226945877, 25.8511238098, -13.8488159180, + 25.6474494934, -14.6556854248, 25.4351654053, -15.4427795410, + 25.2151222229, -16.2097206116, 24.9882202148, -16.9562911987, + 24.7554092407, -17.6824359894, 24.5176963806, -18.3882732391, + 24.2761363983, -19.0741081238, 24.0318355560, -19.7404365540, + 23.7859516144, -20.3879585266, 23.5396957397, -21.0175857544, + 23.2943286896, -21.6304492950, 23.0511646271, -22.2279090881, + 22.8115653992, -22.8115653992, 22.5769481659, -23.3832664490, + 22.3487796783, -23.9451198578, 22.9333324432, 23.7241382599, + 23.1547069550, 23.1547069550, 23.3832664490, 22.5769481659, + 23.6174926758, 21.9887008667, 23.8559188843, 21.3880653381, + 24.0971317291, 20.7733898163, 24.3397712708, 20.1432590485, + 24.5825328827, 19.4964923859, 24.8241615295, 18.8321228027, + 25.0634574890, 18.1494007111, 25.2992763519, 17.4477767944, + 25.5305233002, 16.7268943787, 25.7561588287, 15.9865808487, + 25.9751949310, 15.2268390656, 26.1867027283, 14.4478359222, + 26.3897991180, 13.6498956680, 26.5836582184, 12.8334894180, + 26.7675056458, 11.9992265701, 26.9406242371, 11.1478443146, + 27.1023464203, 10.2802000046, 27.2520580292, 9.3972616196, + 27.3892002106, 8.5000963211, 27.5132656097, 7.5898661613, + 27.6238021851, 6.6678142548, 27.7204093933, 5.7352571487, + 27.8027400970, 4.7935757637, 27.8705005646, 3.8442070484, + 27.9234523773, 2.8886330128, 27.9614086151, 1.9283730984, + 27.9842357635, 0.9649736881, 27.9918537140, -0.0000000000, + 27.9842357635, -0.9649736881, 27.9614086151, -1.9283730984, + 27.9234523773, -2.8886330128, 27.8705005646, -3.8442070484, + 27.8027400970, -4.7935757637, 27.7204093933, -5.7352571487, + 27.6238021851, -6.6678142548, 27.5132656097, -7.5898661613, + 27.3892002106, -8.5000963211, 27.2520580292, -9.3972616196, + 27.1023464203, -10.2802000046, 26.9406242371, -11.1478443146, + 26.7675056458, -11.9992265701, 26.5836582184, -12.8334894180, + 26.3897991180, -13.6498956680, 26.1867027283, -14.4478359222, + 25.9751949310, -15.2268390656, 25.7561588287, -15.9865808487, + 25.5305233002, -16.7268943787, 25.2992763519, -17.4477767944, + 25.0634574890, -18.1494007111, 24.8241615295, -18.8321228027, + 24.5825328827, -19.4964923859, 24.3397712708, -20.1432590485, + 24.0971317291, -20.7733898163, 23.8559188843, -21.3880653381, + 23.6174926758, -21.9887008667, 23.3832664490, -22.5769481659, + 23.1547069550, -23.1547069550, 22.9333324432, -23.7241382599, + 23.5111312866, 23.5111312866, 23.7241382599, 22.9333324432, + 23.9451198578, 22.3487796783, 24.1725063324, 21.7552566528, + 24.4047813416, 21.1508102417, 24.6404800415, 20.5337333679, + 24.8781986237, 19.9025573730, 25.1165828705, 19.2560462952, + 25.3543395996, 18.5931816101, 25.5902252197, 17.9131584167, + 25.8230571747, 17.2153701782, 26.0517005920, 16.4994106293, + 26.2750816345, 15.7650489807, 26.4921798706, 15.0122346878, + 26.7020301819, 14.2410821915, 26.9037227631, 13.4518613815, + 27.0964012146, 12.6449871063, 27.2792663574, 11.8210153580, + 27.4515743256, 10.9806299210, 27.6126346588, 10.1246328354, + 27.7618141174, 9.2539377213, 27.8985328674, 8.3695592880, + 28.0222644806, 7.4726042747, 28.1325454712, 6.5642604828, + 28.2289581299, 5.6457915306, 28.3111438751, 4.7185239792, + 28.3788013458, 3.7838401794, 28.4316806793, 2.8431680202, + 28.4695892334, 1.8979727030, 28.4923896790, 0.9497463703, + 28.5000000000, -0.0000000000, 28.4923896790, -0.9497463703, + 28.4695892334, -1.8979727030, 28.4316806793, -2.8431680202, + 28.3788013458, -3.7838401794, 28.3111438751, -4.7185239792, + 28.2289581299, -5.6457915306, 28.1325454712, -6.5642604828, + 28.0222644806, -7.4726042747, 27.8985328674, -8.3695592880, + 27.7618141174, -9.2539377213, 27.6126346588, -10.1246328354, + 27.4515743256, -10.9806299210, 27.2792663574, -11.8210153580, + 27.0964012146, -12.6449871063, 26.9037227631, -13.4518613815, + 26.7020301819, -14.2410821915, 26.4921798706, -15.0122346878, + 26.2750816345, -15.7650489807, 26.0517005920, -16.4994106293, + 25.8230571747, -17.2153701782, 25.5902252197, -17.9131584167, + 25.3543395996, -18.5931816101, 25.1165828705, -19.2560462952, + 24.8781986237, -19.9025573730, 24.6404800415, -20.5337333679, + 24.4047813416, -21.1508102417, 24.1725063324, -21.7552566528, + 23.9451198578, -22.3487796783, 23.7241382599, -22.9333324432, + 23.5111312866, -23.5111312866, +}; + +} /* namespace Pdraw */ + +#endif /* USE_GLES2 */ diff --git a/libpdraw/src/pdraw_gles2_hmd_positions_cockpitglasses2.cpp b/libpdraw/src/pdraw_gles2_hmd_positions_cockpitglasses2.cpp index b37ee61..c8cf47f 100644 --- a/libpdraw/src/pdraw_gles2_hmd_positions_cockpitglasses2.cpp +++ b/libpdraw/src/pdraw_gles2_hmd_positions_cockpitglasses2.cpp @@ -1,1901 +1,1901 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * OpenGL ES 2.0 HMD distortion correction - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifdef USE_GLES2 - -namespace Pdraw { - -extern const float pdraw_gles2HmdPositionsCockpitglasses2[7442] = { - -28.1967468262, 28.1967468262, -28.3750057220, 27.4291725159, - -28.5509490967, 26.6475524902, -28.7242774963, 25.8518505096, - -28.8947029114, 25.0420761108, -29.0619239807, 24.2182693481, - -29.2256336212, 23.3805084229, -29.3855266571, 22.5289039612, - -29.5412883759, 21.6636123657, -29.6926136017, 20.7848300934, - -29.8391952515, 19.8927974701, -29.9807300568, 18.9877948761, - -30.1169204712, 18.0701522827, -30.2474803925, 17.1402397156, - -30.3721294403, 16.1984691620, -30.4906005859, 15.2453002930, - -30.6026382446, 14.2812309265, -30.7080020905, 13.3068008423, - -30.8064727783, 12.3225889206, -30.8978214264, 11.3292007446, - -30.9818668365, 10.3272886276, -31.0584316254, 9.3175296783, - -31.1273555756, 8.3006286621, -31.1884994507, 7.2773165703, - -31.2417373657, 6.2483472824, -31.2869625092, 5.2144937515, - -31.3240852356, 4.1765446663, -31.3530349731, 3.1353034973, - -31.3737506866, 2.0915834904, -31.3861980438, 1.0462065935, - -31.3903484344, -0.0000000000, -31.3861980438, -1.0462065935, - -31.3737506866, -2.0915834904, -31.3530349731, -3.1353034973, - -31.3240852356, -4.1765446663, -31.2869625092, -5.2144937515, - -31.2417373657, -6.2483472824, -31.1884994507, -7.2773165703, - -31.1273555756, -8.3006286621, -31.0584316254, -9.3175296783, - -30.9818668365, -10.3272886276, -30.8978214264, -11.3292007446, - -30.8064727783, -12.3225889206, -30.7080020905, -13.3068008423, - -30.6026382446, -14.2812309265, -30.4906005859, -15.2453002930, - -30.3721294403, -16.1984691620, -30.2474803925, -17.1402397156, - -30.1169204712, -18.0701522827, -29.9807300568, -18.9877948761, - -29.8391952515, -19.8927974701, -29.6926136017, -20.7848300934, - -29.5412883759, -21.6636123657, -29.3855266571, -22.5289039612, - -29.2256336212, -23.3805084229, -29.0619239807, -24.2182693481, - -28.8947029114, -25.0420761108, -28.7242774963, -25.8518505096, - -28.5509490967, -26.6475524902, -28.3750057220, -27.4291725159, - -28.1967468262, -28.1967468262, -27.4291725159, 28.3750057220, - -27.6052837372, 27.6052837372, -27.7791213989, 26.8212203979, - -27.9504032135, 26.0227890015, -28.1188316345, 25.2099876404, - -28.2841033936, 24.3828468323, -28.4459114075, 23.5414447784, - -28.6039466858, 22.6858882904, -28.7578964233, 21.8163356781, - -28.9074535370, 20.9329833984, -29.0523090363, 20.0360755920, - -29.1921634674, 19.1259002686, -29.3267192841, 18.2027912140, - -29.4556884766, 17.2671279907, -29.5787963867, 16.3193359375, - -29.6957893372, 15.3598909378, -29.8063888550, 14.3892917633, - -29.9103755951, 13.4081001282, -30.0075283051, 12.4169082642, - -30.0976409912, 11.4163465500, -30.1805267334, 10.4070777893, - -30.2560138702, 9.3897972107, -30.3239517212, 8.3652276993, - -30.3842048645, 7.3341183662, -30.4366550446, 6.2972393036, - -30.4812030792, 5.2553801537, -30.5177650452, 4.2093467712, - -30.5462703705, 3.1599588394, -30.5666675568, 2.1080460548, - -30.5789222717, 1.0544456244, -30.5830078125, -0.0000000000, - -30.5789222717, -1.0544456244, -30.5666675568, -2.1080460548, - -30.5462703705, -3.1599588394, -30.5177650452, -4.2093467712, - -30.4812030792, -5.2553801537, -30.4366550446, -6.2972393036, - -30.3842048645, -7.3341183662, -30.3239517212, -8.3652276993, - -30.2560138702, -9.3897972107, -30.1805267334, -10.4070777893, - -30.0976409912, -11.4163465500, -30.0075283051, -12.4169082642, - -29.9103755951, -13.4081001282, -29.8063888550, -14.3892917633, - -29.6957893372, -15.3598909378, -29.5787963867, -16.3193359375, - -29.4556884766, -17.2671279907, -29.3267192841, -18.2027912140, - -29.1921634674, -19.1259002686, -29.0523090363, -20.0360755920, - -28.9074535370, -20.9329833984, -28.7578964233, -21.8163356781, - -28.6039466858, -22.6858882904, -28.4459114075, -23.5414447784, - -28.2841033936, -24.3828468323, -28.1188316345, -25.2099876404, - -27.9504032135, -26.0227890015, -27.7791213989, -26.8212203979, - -27.6052837372, -27.6052837372, -27.4291725159, -28.3750057220, - -26.6475524902, 28.5509490967, -26.8212203979, 27.7791213989, - -26.9926738739, 26.9926738739, -27.1616210938, 26.1915645599, - -27.3277645111, 25.3757820129, -27.4907989502, 24.5453567505, - -27.6504173279, 23.7003574371, -27.8063087463, 22.8408966064, - -27.9581623077, 21.9671268463, -28.1056671143, 21.0792503357, - -28.2485179901, 20.1775131226, -28.3864135742, 19.2622089386, - -28.5190601349, 18.3336811066, -28.6461734772, 17.3923187256, - -28.7674865723, 16.4385623932, -28.8827209473, 15.4728860855, - -28.9916419983, 14.4958209991, -29.0940208435, 13.5079383850, - -29.1896381378, 12.5098447800, -29.2782974243, 11.5021886826, - -29.3598213196, 10.4856500626, -29.4340457916, 9.4609432220, - -29.5008239746, 8.4288072586, -29.5600337982, 7.3900084496, - -29.6115608215, 6.3453345299, -29.6553134918, 5.2955918312, - -29.6912136078, 4.2416019440, -29.7192001343, 3.1842000484, - -29.7392234802, 2.1242303848, -29.7512512207, 1.0625447035, - -29.7552623749, -0.0000000000, -29.7512512207, -1.0625447035, - -29.7392234802, -2.1242303848, -29.7192001343, -3.1842000484, - -29.6912136078, -4.2416019440, -29.6553134918, -5.2955918312, - -29.6115608215, -6.3453345299, -29.5600337982, -7.3900084496, - -29.5008239746, -8.4288072586, -29.4340457916, -9.4609432220, - -29.3598213196, -10.4856500626, -29.2782974243, -11.5021886826, - -29.1896381378, -12.5098447800, -29.0940208435, -13.5079383850, - -28.9916419983, -14.4958209991, -28.8827209473, -15.4728860855, - -28.7674865723, -16.4385623932, -28.6461734772, -17.3923187256, - -28.5190601349, -18.3336811066, -28.3864135742, -19.2622089386, - -28.2485179901, -20.1775131226, -28.1056671143, -21.0792503357, - -27.9581623077, -21.9671268463, -27.8063087463, -22.8408966064, - -27.6504173279, -23.7003574371, -27.4907989502, -24.5453567505, - -27.3277645111, -25.3757820129, -27.1616210938, -26.1915645599, - -26.9926738739, -26.9926738739, -26.8212203979, -27.7791213989, - -26.6475524902, -28.5509490967, -25.8518505096, 28.7242774963, - -26.0227890015, 27.9504032135, -26.1915645599, 27.1616210938, - -26.3578815460, 26.3578815460, -26.5214443207, 25.5391674042, - -26.6819458008, 24.7055053711, -26.8390789032, 23.8569583893, - -26.9925327301, 22.9936389923, -27.1419944763, 22.1156997681, - -27.2871570587, 21.2233448029, -27.4277153015, 20.3168258667, - -27.5633678436, 19.3964443207, -27.6938343048, 18.4625568390, - -27.8188114166, 17.5155487061, -27.9380435944, 16.5558776855, - -28.0512752533, 15.5840415955, -28.1582660675, 14.6005821228, - -28.2587909698, 13.6060838699, -28.3526420593, 12.6011743546, - -28.4396305084, 11.5865163803, -28.5195865631, 10.5628099442, - -28.5923538208, 9.5307846069, -28.6578006744, 8.4912004471, - -28.7158069611, 7.4448385239, -28.7662715912, 6.3925046921, - -28.8091106415, 5.3350205421, -28.8442516327, 4.2732224464, - -28.8716411591, 3.2079601288, -28.8912334442, 2.1400914192, - -28.9030017853, 1.0704815388, -28.9069271088, -0.0000000000, - -28.9030017853, -1.0704815388, -28.8912334442, -2.1400914192, - -28.8716411591, -3.2079601288, -28.8442516327, -4.2732224464, - -28.8091106415, -5.3350205421, -28.7662715912, -6.3925046921, - -28.7158069611, -7.4448385239, -28.6578006744, -8.4912004471, - -28.5923538208, -9.5307846069, -28.5195865631, -10.5628099442, - -28.4396305084, -11.5865163803, -28.3526420593, -12.6011743546, - -28.2587909698, -13.6060838699, -28.1582660675, -14.6005821228, - -28.0512752533, -15.5840415955, -27.9380435944, -16.5558776855, - -27.8188114166, -17.5155487061, -27.6938343048, -18.4625568390, - -27.5633678436, -19.3964443207, -27.4277153015, -20.3168258667, - -27.2871570587, -21.2233448029, -27.1419944763, -22.1156997681, - -26.9925327301, -22.9936389923, -26.8390789032, -23.8569583893, - -26.6819458008, -24.7055053711, -26.5214443207, -25.5391674042, - -26.3578815460, -26.3578815460, -26.1915645599, -27.1616210938, - -26.0227890015, -27.9504032135, -25.8518505096, -28.7242774963, - -25.0420761108, 28.8947029114, -25.2099876404, 28.1188316345, - -25.3757820129, 27.3277645111, -25.5391674042, 26.5214443207, - -25.6998462677, 25.6998462677, -25.8575134277, 24.8629932404, - -26.0118598938, 24.0109481812, -26.1625747681, 23.1438159943, - -26.3093490601, 22.2617568970, -26.4518718719, 21.3649730682, - -26.5898437500, 20.4537258148, -26.7229709625, 19.5283241272, - -26.8509502411, 18.5891189575, -26.9735183716, 17.6365318298, - -27.0904083252, 16.6710205078, -27.2013721466, 15.6930990219, - -27.3061733246, 14.7033243179, -27.4045982361, 13.7022991180, - -27.4964504242, 12.6906690598, -27.5815448761, 11.6691150665, - -27.6597232819, 10.6383552551, -27.7308425903, 9.5991382599, - -27.7947807312, 8.5522403717, -27.8514251709, 7.4984607697, - -27.9006881714, 6.4386205673, -27.9424934387, 5.3735561371, - -27.9767761230, 4.3041195869, -28.0034904480, 3.2311718464, - -28.0225963593, 2.1555843353, -28.0340709686, 1.0782334805, - -28.0378971100, -0.0000000000, -28.0340709686, -1.0782334805, - -28.0225963593, -2.1555843353, -28.0034904480, -3.2311718464, - -27.9767761230, -4.3041195869, -27.9424934387, -5.3735561371, - -27.9006881714, -6.4386205673, -27.8514251709, -7.4984607697, - -27.7947807312, -8.5522403717, -27.7308425903, -9.5991382599, - -27.6597232819, -10.6383552551, -27.5815448761, -11.6691150665, - -27.4964504242, -12.6906690598, -27.4045982361, -13.7022991180, - -27.3061733246, -14.7033243179, -27.2013721466, -15.6930990219, - -27.0904083252, -16.6710205078, -26.9735183716, -17.6365318298, - -26.8509502411, -18.5891189575, -26.7229709625, -19.5283241272, - -26.5898437500, -20.4537258148, -26.4518718719, -21.3649730682, - -26.3093490601, -22.2617568970, -26.1625747681, -23.1438159943, - -26.0118598938, -24.0109481812, -25.8575134277, -24.8629932404, - -25.6998462677, -25.6998462677, -25.5391674042, -26.5214443207, - -25.3757820129, -27.3277645111, -25.2099876404, -28.1188316345, - -25.0420761108, -28.8947029114, -24.2182693481, 29.0619239807, - -24.3828468323, 28.2841033936, -24.5453567505, 27.4907989502, - -24.7055053711, 26.6819458008, -24.8629932404, 25.8575134277, - -25.0175189972, 25.0175189972, -25.1687717438, 24.1620216370, - -25.3164443970, 23.2911281586, -25.4602241516, 22.4049987793, - -25.5998191833, 21.5038471222, -25.7349014282, 20.5879211426, - -25.8651943207, 19.6575489044, -25.9904155731, 18.7131004333, - -26.1102924347, 17.7549991608, -26.2245635986, 16.7837219238, - -26.3329906464, 15.7997951508, -26.4353485107, 14.8037958145, - -26.5314273834, 13.7963418961, -26.6210422516, 12.7781000137, - -26.7040214539, 11.7497692108, -26.7802181244, 10.7120866776, - -26.8494987488, 9.6658191681, -26.9117507935, 8.6117601395, - -26.9668769836, 7.5507259369, -27.0147991180, 6.4835519791, - -27.0554504395, 5.4110903740, -27.0887775421, 4.3342041969, - -27.1147384644, 3.2537686825, -27.1333026886, 2.1706643105, - -27.1444492340, 1.0857779980, -27.1481666565, -0.0000000000, - -27.1444492340, -1.0857779980, -27.1333026886, -2.1706643105, - -27.1147384644, -3.2537686825, -27.0887775421, -4.3342041969, - -27.0554504395, -5.4110903740, -27.0147991180, -6.4835519791, - -26.9668769836, -7.5507259369, -26.9117507935, -8.6117601395, - -26.8494987488, -9.6658191681, -26.7802181244, -10.7120866776, - -26.7040214539, -11.7497692108, -26.6210422516, -12.7781000137, - -26.5314273834, -13.7963418961, -26.4353485107, -14.8037958145, - -26.3329906464, -15.7997951508, -26.2245635986, -16.7837219238, - -26.1102924347, -17.7549991608, -25.9904155731, -18.7131004333, - -25.8651943207, -19.6575489044, -25.7349014282, -20.5879211426, - -25.5998191833, -21.5038471222, -25.4602241516, -22.4049987793, - -25.3164443970, -23.2911281586, -25.1687717438, -24.1620216370, - -25.0175189972, -25.0175189972, -24.8629932404, -25.8575134277, - -24.7055053711, -26.6819458008, -24.5453567505, -27.4907989502, - -24.3828468323, -28.2841033936, -24.2182693481, -29.0619239807, - -23.3805084229, 29.2256336212, -23.5414447784, 28.4459114075, - -23.7003574371, 27.6504173279, -23.8569583893, 26.8390789032, - -24.0109481812, 26.0118598938, -24.1620216370, 25.1687717438, - -24.3098735809, 24.3098735809, -24.4541950226, 23.4352703094, - -24.5946865082, 22.5451297760, -24.7310256958, 21.6396484375, - -24.8629302979, 20.7191085815, -24.9901065826, 19.7838344574, - -25.1122798920, 18.8342094421, -25.2291812897, 17.8706703186, - -25.3405628204, 16.8937091827, -25.4461898804, 15.9038677216, - -25.5458450317, 14.9017429352, -25.6393356323, 13.8879728317, - -25.7264804840, 12.8632402420, -25.8071269989, 11.8282670975, - -25.8811359406, 10.7838068008, -25.9483890533, 9.7306461334, - -26.0087852478, 8.6695947647, -26.0622406006, 7.6014866829, - -26.1086864471, 6.5271716118, -26.1480674744, 5.4475140572, - -26.1803417206, 4.3633904457, -26.2054748535, 3.2756843567, - -26.2234420776, 2.1852869987, -26.2342300415, 1.0930929184, - -26.2378273010, -0.0000000000, -26.2342300415, -1.0930929184, - -26.2234420776, -2.1852869987, -26.2054748535, -3.2756843567, - -26.1803417206, -4.3633904457, -26.1480674744, -5.4475140572, - -26.1086864471, -6.5271716118, -26.0622406006, -7.6014866829, - -26.0087852478, -8.6695947647, -25.9483890533, -9.7306461334, - -25.8811359406, -10.7838068008, -25.8071269989, -11.8282670975, - -25.7264804840, -12.8632402420, -25.6393356323, -13.8879728317, - -25.5458450317, -14.9017429352, -25.4461898804, -15.9038677216, - -25.3405628204, -16.8937091827, -25.2291812897, -17.8706703186, - -25.1122798920, -18.8342094421, -24.9901065826, -19.7838344574, - -24.8629302979, -20.7191085815, -24.7310256958, -21.6396484375, - -24.5946865082, -22.5451297760, -24.4541950226, -23.4352703094, - -24.3098735809, -24.3098735809, -24.1620216370, -25.1687717438, - -24.0109481812, -26.0118598938, -23.8569583893, -26.8390789032, - -23.7003574371, -27.6504173279, -23.5414447784, -28.4459114075, - -23.3805084229, -29.2256336212, -22.5289039612, 29.3855266571, - -22.6858882904, 28.6039466858, -22.8408966064, 27.8063087463, - -22.9936389923, 26.9925327301, -23.1438159943, 26.1625747681, - -23.2911281586, 25.3164443970, -23.4352703094, 24.4541950226, - -23.5759410858, 23.5759410858, -23.7128200531, 22.6818294525, - -23.8456211090, 21.7720890045, -23.9740486145, 20.8469982147, - -24.0978183746, 19.9068927765, -24.2166576385, 18.9521656036, - -24.3303070068, 17.9832706451, -24.4385280609, 17.0007152557, - -24.5410938263, 16.0050601959, -24.6378002167, 14.9969215393, - -24.7284622192, 13.9769563675, -24.8129138947, 12.9458684921, - -24.8910140991, 11.9043979645, -24.9626388550, 10.8533210754, - -25.0276813507, 9.7934408188, -25.0860557556, 8.7255840302, - -25.1376914978, 7.6506013870, -25.1825313568, 6.5693559647, - -25.2205314636, 5.4827241898, -25.2516613007, 4.3915929794, - -25.2758941650, 3.2968556881, -25.2932147980, 2.1994099617, - -25.3036098480, 1.1001570225, -25.3070774078, -0.0000000000, - -25.3036098480, -1.1001570225, -25.2932147980, -2.1994099617, - -25.2758941650, -3.2968556881, -25.2516613007, -4.3915929794, - -25.2205314636, -5.4827241898, -25.1825313568, -6.5693559647, - -25.1376914978, -7.6506013870, -25.0860557556, -8.7255840302, - -25.0276813507, -9.7934408188, -24.9626388550, -10.8533210754, - -24.8910140991, -11.9043979645, -24.8129138947, -12.9458684921, - -24.7284622192, -13.9769563675, -24.6378002167, -14.9969215393, - -24.5410938263, -16.0050601959, -24.4385280609, -17.0007152557, - -24.3303070068, -17.9832706451, -24.2166576385, -18.9521656036, - -24.0978183746, -19.9068927765, -23.9740486145, -20.8469982147, - -23.8456211090, -21.7720890045, -23.7128200531, -22.6818294525, - -23.5759410858, -23.5759410858, -23.4352703094, -24.4541950226, - -23.2911281586, -25.3164443970, -23.1438159943, -26.1625747681, - -22.9936389923, -26.9925327301, -22.8408966064, -27.8063087463, - -22.6858882904, -28.6039466858, -22.5289039612, -29.3855266571, - -21.6636123657, 29.5412883759, -21.8163356781, 28.7578964233, - -21.9671268463, 27.9581623077, -22.1156997681, 27.1419944763, - -22.2617568970, 26.3093490601, -22.4049987793, 25.4602241516, - -22.5451297760, 24.5946865082, -22.6818294525, 23.7128200531, - -22.8148078918, 22.8148078918, -22.9437732697, 21.9008731842, - -23.0684318542, 20.9713001251, -23.1885070801, 20.0264377594, - -23.3037357330, 19.0666923523, -23.4138622284, 18.0925292969, - -23.5186595917, 17.1044807434, -23.6179122925, 16.1031227112, - -23.7114276886, 15.0890903473, -23.7990322113, 14.0630645752, - -23.8805751801, 13.0257682800, -23.9559268951, 11.9779634476, - -24.0249786377, 10.9204444885, -24.0876388550, 9.8540334702, - -24.1438331604, 8.7795763016, -24.1935100555, 7.6979346275, - -24.2366218567, 6.6099877357, -24.2731380463, 5.5166220665, - -24.3030376434, 4.4187340736, -24.3263034821, 3.3172230721, - -24.3429279327, 2.2129933834, -24.3529033661, 1.1069501638, - -24.3562297821, -0.0000000000, -24.3529033661, -1.1069501638, - -24.3429279327, -2.2129933834, -24.3263034821, -3.3172230721, - -24.3030376434, -4.4187340736, -24.2731380463, -5.5166220665, - -24.2366218567, -6.6099877357, -24.1935100555, -7.6979346275, - -24.1438331604, -8.7795763016, -24.0876388550, -9.8540334702, - -24.0249786377, -10.9204444885, -23.9559268951, -11.9779634476, - -23.8805751801, -13.0257682800, -23.7990322113, -14.0630645752, - -23.7114276886, -15.0890903473, -23.6179122925, -16.1031227112, - -23.5186595917, -17.1044807434, -23.4138622284, -18.0925292969, - -23.3037357330, -19.0666923523, -23.1885070801, -20.0264377594, - -23.0684318542, -20.9713001251, -22.9437732697, -21.9008731842, - -22.8148078918, -22.8148078918, -22.6818294525, -23.7128200531, - -22.5451297760, -24.5946865082, -22.4049987793, -25.4602241516, - -22.2617568970, -26.3093490601, -22.1156997681, -27.1419944763, - -21.9671268463, -27.9581623077, -21.8163356781, -28.7578964233, - -21.6636123657, -29.5412883759, -20.7848300934, 29.6926136017, - -20.9329833984, 28.9074535370, -21.0792503357, 28.1056671143, - -21.2233448029, 27.2871570587, -21.3649730682, 26.4518718719, - -21.5038471222, 25.5998191833, -21.6396484375, 24.7310256958, - -21.7720890045, 23.8456211090, -21.9008731842, 22.9437732697, - -22.0257110596, 22.0257110596, -22.1463165283, 21.0917301178, - -22.2624206543, 20.1421909332, -22.3737659454, 19.1775150299, - -22.4801120758, 18.1981868744, -22.5812358856, 17.2047519684, - -22.6769371033, 16.1978111267, -22.7670307159, 15.1780204773, - -22.8513603210, 14.1460809708, -22.9297904968, 13.1027374268, - -23.0022029877, 12.0487728119, -23.0685062408, 10.9850025177, - -23.1286220551, 9.9122667313, -23.1824951172, 8.8314266205, - -23.2300834656, 7.7433609962, -23.2713546753, 6.6489582062, - -23.3062915802, 5.5491170883, -23.3348808289, 4.4447393417, - -23.3571205139, 3.3367314339, -23.3730049133, 2.2260005474, - -23.3825359344, 1.1134541035, -23.3857116699, -0.0000000000, - -23.3825359344, -1.1134541035, -23.3730049133, -2.2260005474, - -23.3571205139, -3.3367314339, -23.3348808289, -4.4447393417, - -23.3062915802, -5.5491170883, -23.2713546753, -6.6489582062, - -23.2300834656, -7.7433609962, -23.1824951172, -8.8314266205, - -23.1286220551, -9.9122667313, -23.0685062408, -10.9850025177, - -23.0022029877, -12.0487728119, -22.9297904968, -13.1027374268, - -22.8513603210, -14.1460809708, -22.7670307159, -15.1780204773, - -22.6769371033, -16.1978111267, -22.5812358856, -17.2047519684, - -22.4801120758, -18.1981868744, -22.3737659454, -19.1775150299, - -22.2624206543, -20.1421909332, -22.1463165283, -21.0917301178, - -22.0257110596, -22.0257110596, -21.9008731842, -22.9437732697, - -21.7720890045, -23.8456211090, -21.6396484375, -24.7310256958, - -21.5038471222, -25.5998191833, -21.3649730682, -26.4518718719, - -21.2233448029, -27.2871570587, -21.0792503357, -28.1056671143, - -20.9329833984, -28.9074535370, -20.7848300934, -29.6926136017, - -19.8927974701, 29.8391952515, -20.0360755920, 29.0523090363, - -20.1775131226, 28.2485179901, -20.3168258667, 27.4277153015, - -20.4537258148, 26.5898437500, -20.5879211426, 25.7349014282, - -20.7191085815, 24.8629302979, -20.8469982147, 23.9740486145, - -20.9713001251, 23.0684318542, -21.0917301178, 22.1463165283, - -21.2080097198, 21.2080097198, -21.3198776245, 20.2538833618, - -21.4270839691, 19.2843761444, -21.5293998718, 18.2999897003, - -21.6266136169, 17.3012905121, -21.7185325623, 16.2889003754, - -21.8049926758, 15.2634954453, -21.8858470917, 14.2258014679, - -21.9609756470, 13.1765851974, -22.0302753448, 12.1166515350, - -22.0936698914, 11.0468349457, -22.1510982513, 9.9679937363, - -22.2025184631, 8.8810071945, -22.2479019165, 7.7867655754, - -22.2872333527, 6.6861701012, -22.3205051422, 5.5801262856, - -22.3477191925, 4.4695439339, -22.3688755035, 3.3553314209, - -22.3839836121, 2.2383983135, -22.3930454254, 1.1196522713, - -22.3960647583, -0.0000000000, -22.3930454254, -1.1196522713, - -22.3839836121, -2.2383983135, -22.3688755035, -3.3553314209, - -22.3477191925, -4.4695439339, -22.3205051422, -5.5801262856, - -22.2872333527, -6.6861701012, -22.2479019165, -7.7867655754, - -22.2025184631, -8.8810071945, -22.1510982513, -9.9679937363, - -22.0936698914, -11.0468349457, -22.0302753448, -12.1166515350, - -21.9609756470, -13.1765851974, -21.8858470917, -14.2258014679, - -21.8049926758, -15.2634954453, -21.7185325623, -16.2889003754, - -21.6266136169, -17.3012905121, -21.5293998718, -18.2999897003, - -21.4270839691, -19.2843761444, -21.3198776245, -20.2538833618, - -21.2080097198, -21.2080097198, -21.0917301178, -22.1463165283, - -20.9713001251, -23.0684318542, -20.8469982147, -23.9740486145, - -20.7191085815, -24.8629302979, -20.5879211426, -25.7349014282, - -20.4537258148, -26.5898437500, -20.3168258667, -27.4277153015, - -20.1775131226, -28.2485179901, -20.0360755920, -29.0523090363, - -19.8927974701, -29.8391952515, -18.9877948761, 29.9807300568, - -19.1259002686, 29.1921634674, -19.2622089386, 28.3864135742, - -19.3964443207, 27.5633678436, -19.5283241272, 26.7229709625, - -19.6575489044, 25.8651943207, -19.7838344574, 24.9901065826, - -19.9068927765, 24.0978183746, -20.0264377594, 23.1885070801, - -20.1421909332, 22.2624206543, -20.2538833618, 21.3198776245, - -20.3612632751, 20.3612632751, -20.4640884399, 19.3870315552, - -20.5621433258, 18.3977069855, -20.6552238464, 17.3938732147, - -20.7431564331, 16.3761768341, -20.8257865906, 15.3453159332, - -20.9029827118, 14.3020410538, -20.9746379852, 13.2471399307, - -21.0406703949, 12.1814413071, -21.1010150909, 11.1057968140, - -21.1556262970, 10.0210866928, -21.2044811249, 8.9282026291, - -21.2475624084, 7.8280491829, -21.2848682404, 6.7215371132, - -21.3164043427, 5.6095800400, -21.3421802521, 4.4930906296, - -21.3622112274, 3.3729808331, -21.3765087128, 2.2501587868, - -21.3850822449, 1.1255306005, -21.3879394531, -0.0000000000, - -21.3850822449, -1.1255306005, -21.3765087128, -2.2501587868, - -21.3622112274, -3.3729808331, -21.3421802521, -4.4930906296, - -21.3164043427, -5.6095800400, -21.2848682404, -6.7215371132, - -21.2475624084, -7.8280491829, -21.2044811249, -8.9282026291, - -21.1556262970, -10.0210866928, -21.1010150909, -11.1057968140, - -21.0406703949, -12.1814413071, -20.9746379852, -13.2471399307, - -20.9029827118, -14.3020410538, -20.8257865906, -15.3453159332, - -20.7431564331, -16.3761768341, -20.6552238464, -17.3938732147, - -20.5621433258, -18.3977069855, -20.4640884399, -19.3870315552, - -20.3612632751, -20.3612632751, -20.2538833618, -21.3198776245, - -20.1421909332, -22.2624206543, -20.0264377594, -23.1885070801, - -19.9068927765, -24.0978183746, -19.7838344574, -24.9901065826, - -19.6575489044, -25.8651943207, -19.5283241272, -26.7229709625, - -19.3964443207, -27.5633678436, -19.2622089386, -28.3864135742, - -19.1259002686, -29.1921634674, -18.9877948761, -29.9807300568, - -18.0701522827, 30.1169204712, -18.2027912140, 29.3267192841, - -18.3336811066, 28.5190601349, -18.4625568390, 27.6938343048, - -18.5891189575, 26.8509502411, -18.7131004333, 25.9904155731, - -18.8342094421, 25.1122798920, -18.9521656036, 24.2166576385, - -19.0666923523, 23.3037357330, -19.1775150299, 22.3737659454, - -19.2843761444, 21.4270839691, -19.3870315552, 20.4640884399, - -19.4852523804, 19.4852523804, -19.5788326263, 18.4911193848, - -19.6675815582, 17.4822959900, -19.7513389587, 16.4594497681, - -19.8299636841, 15.4233045578, -19.9033393860, 14.3746337891, - -19.9713764191, 13.3142499924, -20.0340042114, 12.2430028915, - -20.0911769867, 11.1617650986, -20.1428661346, 10.0714330673, - -20.1890583038, 8.9729146957, -20.2297534943, 7.8671264648, - -20.2649650574, 6.7549881935, -20.2947063446, 5.6374182701, - -20.3190002441, 4.5153336525, -20.3378696442, 3.3896448612, - -20.3513298035, 2.2612588406, -20.3593997955, 1.1310777664, - -20.3620891571, -0.0000000000, -20.3593997955, -1.1310777664, - -20.3513298035, -2.2612588406, -20.3378696442, -3.3896448612, - -20.3190002441, -4.5153336525, -20.2947063446, -5.6374182701, - -20.2649650574, -6.7549881935, -20.2297534943, -7.8671264648, - -20.1890583038, -8.9729146957, -20.1428661346, -10.0714330673, - -20.0911769867, -11.1617650986, -20.0340042114, -12.2430028915, - -19.9713764191, -13.3142499924, -19.9033393860, -14.3746337891, - -19.8299636841, -15.4233045578, -19.7513389587, -16.4594497681, - -19.6675815582, -17.4822959900, -19.5788326263, -18.4911193848, - -19.4852523804, -19.4852523804, -19.3870315552, -20.4640884399, - -19.2843761444, -21.4270839691, -19.1775150299, -22.3737659454, - -19.0666923523, -23.3037357330, -18.9521656036, -24.2166576385, - -18.8342094421, -25.1122798920, -18.7131004333, -25.9904155731, - -18.5891189575, -26.8509502411, -18.4625568390, -27.6938343048, - -18.3336811066, -28.5190601349, -18.2027912140, -29.3267192841, - -18.0701522827, -30.1169204712, -17.1402397156, 30.2474803925, - -17.2671279907, 29.4556884766, -17.3923187256, 28.6461734772, - -17.5155487061, 27.8188114166, -17.6365318298, 26.9735183716, - -17.7549991608, 26.1102924347, -17.8706703186, 25.2291812897, - -17.9832706451, 24.3303070068, -18.0925292969, 23.4138622284, - -18.1981868744, 22.4801120758, -18.2999897003, 21.5293998718, - -18.3977069855, 20.5621433258, -18.4911193848, 19.5788326263, - -18.5800323486, 18.5800323486, -18.6642704010, 17.5663719177, - -18.7436866760, 16.5385475159, -18.8181533813, 15.4973020554, - -18.8875694275, 14.4434366226, -18.9518623352, 13.3777856827, - -19.0109767914, 12.3012199402, -19.0648784637, 11.2146348953, - -19.1135578156, 10.1189422607, -19.1570129395, 9.0150651932, - -19.1952610016, 7.9039311409, -19.2283229828, 6.7864665985, - -19.2562274933, 5.6635961533, -19.2790031433, 4.5362362862, - -19.2966842651, 3.4052970409, -19.3092918396, 2.2716813087, - -19.3168468475, 1.1362851858, -19.3193645477, -0.0000000000, - -19.3168468475, -1.1362851858, -19.3092918396, -2.2716813087, - -19.2966842651, -3.4052970409, -19.2790031433, -4.5362362862, - -19.2562274933, -5.6635961533, -19.2283229828, -6.7864665985, - -19.1952610016, -7.9039311409, -19.1570129395, -9.0150651932, - -19.1135578156, -10.1189422607, -19.0648784637, -11.2146348953, - -19.0109767914, -12.3012199402, -18.9518623352, -13.3777856827, - -18.8875694275, -14.4434366226, -18.8181533813, -15.4973020554, - -18.7436866760, -16.5385475159, -18.6642704010, -17.5663719177, - -18.5800323486, -18.5800323486, -18.4911193848, -19.5788326263, - -18.3977069855, -20.5621433258, -18.2999897003, -21.5293998718, - -18.1981868744, -22.4801120758, -18.0925292969, -23.4138622284, - -17.9832706451, -24.3303070068, -17.8706703186, -25.2291812897, - -17.7549991608, -26.1102924347, -17.6365318298, -26.9735183716, - -17.5155487061, -27.8188114166, -17.3923187256, -28.6461734772, - -17.2671279907, -29.4556884766, -17.1402397156, -30.2474803925, - -16.1984691620, 30.3721294403, -16.3193359375, 29.5787963867, - -16.4385623932, 28.7674865723, -16.5558776855, 27.9380435944, - -16.6710205078, 27.0904083252, -16.7837219238, 26.2245635986, - -16.8937091827, 25.3405628204, -17.0007152557, 24.4385280609, - -17.1044807434, 23.5186595917, -17.2047519684, 22.5812358856, - -17.3012905121, 21.6266136169, -17.3938732147, 20.6552238464, - -17.4822959900, 19.6675815582, -17.5663719177, 18.6642704010, - -17.6459465027, 17.6459465027, -17.7208786011, 16.6133232117, - -17.7910594940, 15.5671777725, -17.8564052582, 14.5083284378, - -17.9168510437, 13.4376392365, -17.9723625183, 12.3559989929, - -18.0229206085, 11.2643251419, -18.0685253143, 10.1635446548, - -18.1091899872, 9.0545949936, -18.1449451447, 7.9384136200, - -18.1758213043, 6.8159332275, -18.2018604279, 5.6880812645, - -18.2230987549, 4.5557746887, -18.2395725250, 3.4199199677, - -18.2513160706, 2.2814145088, -18.2583522797, 1.1411470175, - -18.2606964111, -0.0000000000, -18.2583522797, -1.1411470175, - -18.2513160706, -2.2814145088, -18.2395725250, -3.4199199677, - -18.2230987549, -4.5557746887, -18.2018604279, -5.6880812645, - -18.1758213043, -6.8159332275, -18.1449451447, -7.9384136200, - -18.1091899872, -9.0545949936, -18.0685253143, -10.1635446548, - -18.0229206085, -11.2643251419, -17.9723625183, -12.3559989929, - -17.9168510437, -13.4376392365, -17.8564052582, -14.5083284378, - -17.7910594940, -15.5671777725, -17.7208786011, -16.6133232117, - -17.6459465027, -17.6459465027, -17.5663719177, -18.6642704010, - -17.4822959900, -19.6675815582, -17.3938732147, -20.6552238464, - -17.3012905121, -21.6266136169, -17.2047519684, -22.5812358856, - -17.1044807434, -23.5186595917, -17.0007152557, -24.4385280609, - -16.8937091827, -25.3405628204, -16.7837219238, -26.2245635986, - -16.6710205078, -27.0904083252, -16.5558776855, -27.9380435944, - -16.4385623932, -28.7674865723, -16.3193359375, -29.5787963867, - -16.1984691620, -30.3721294403, -15.2453002930, 30.4906005859, - -15.3598909378, 29.6957893372, -15.4728860855, 28.8827209473, - -15.5840415955, 28.0512752533, -15.6930990219, 27.2013721466, - -15.7997951508, 26.3329906464, -15.9038677216, 25.4461898804, - -16.0050601959, 24.5410938263, -16.1031227112, 23.6179122925, - -16.1978111267, 22.6769371033, -16.2889003754, 21.7185325623, - -16.3761768341, 20.7431564331, -16.4594497681, 19.7513389587, - -16.5385475159, 18.7436866760, -16.6133232117, 17.7208786011, - -16.6836566925, 16.6836566925, -16.7494506836, 15.6328210831, - -16.8106346130, 14.5692167282, -16.8671627045, 13.4937295914, - -16.9190063477, 12.4072723389, -16.9661674500, 11.3107776642, - -17.0086555481, 10.2051935196, -17.0464992523, 9.0914659500, - -17.0797348022, 7.9705429077, -17.1084079742, 6.8433632851, - -17.1325664520, 5.7108559608, -17.1522560120, 4.5739350319, - -17.1675205231, 3.4335041046, -17.1783962250, 2.2904527187, - -17.1849098206, 1.1456606388, -17.1870784760, -0.0000000000, - -17.1849098206, -1.1456606388, -17.1783962250, -2.2904527187, - -17.1675205231, -3.4335041046, -17.1522560120, -4.5739350319, - -17.1325664520, -5.7108559608, -17.1084079742, -6.8433632851, - -17.0797348022, -7.9705429077, -17.0464992523, -9.0914659500, - -17.0086555481, -10.2051935196, -16.9661674500, -11.3107776642, - -16.9190063477, -12.4072723389, -16.8671627045, -13.4937295914, - -16.8106346130, -14.5692167282, -16.7494506836, -15.6328210831, - -16.6836566925, -16.6836566925, -16.6133232117, -17.7208786011, - -16.5385475159, -18.7436866760, -16.4594497681, -19.7513389587, - -16.3761768341, -20.7431564331, -16.2889003754, -21.7185325623, - -16.1978111267, -22.6769371033, -16.1031227112, -23.6179122925, - -16.0050601959, -24.5410938263, -15.9038677216, -25.4461898804, - -15.7997951508, -26.3329906464, -15.6930990219, -27.2013721466, - -15.5840415955, -28.0512752533, -15.4728860855, -28.8827209473, - -15.3598909378, -29.6957893372, -15.2453002930, -30.4906005859, - -14.2812309265, 30.6026382446, -14.3892917633, 29.8063888550, - -14.4958209991, 28.9916419983, -14.6005821228, 28.1582660675, - -14.7033243179, 27.3061733246, -14.8037958145, 26.4353485107, - -14.9017429352, 25.5458450317, -14.9969215393, 24.6378002167, - -15.0890903473, 23.7114276886, -15.1780204773, 22.7670307159, - -15.2634954453, 21.8049926758, -15.3453159332, 20.8257865906, - -15.4233045578, 19.8299636841, -15.4973020554, 18.8181533813, - -15.5671777725, 17.7910594940, -15.6328210831, 16.7494506836, - -15.6941518784, 15.6941518784, -15.7511110306, 14.6260318756, - -15.8036670685, 13.5460004807, -15.8518075943, 12.4549913406, - -15.8955411911, 11.3539581299, -15.9348936081, 10.2438602448, - -15.9699020386, 9.1256580353, -16.0006141663, 8.0003070831, - -16.0270824432, 6.8687496185, -16.0493621826, 5.7319149971, - -16.0675067902, 4.5907158852, -16.0815639496, 3.4460492134, - -16.0915718079, 2.2987961769, -16.0975666046, 1.1498261690, - -16.0995616913, -0.0000000000, -16.0975666046, -1.1498261690, - -16.0915718079, -2.2987961769, -16.0815639496, -3.4460492134, - -16.0675067902, -4.5907158852, -16.0493621826, -5.7319149971, - -16.0270824432, -6.8687496185, -16.0006141663, -8.0003070831, - -15.9699020386, -9.1256580353, -15.9348936081, -10.2438602448, - -15.8955411911, -11.3539581299, -15.8518075943, -12.4549913406, - -15.8036670685, -13.5460004807, -15.7511110306, -14.6260318756, - -15.6941518784, -15.6941518784, -15.6328210831, -16.7494506836, - -15.5671777725, -17.7910594940, -15.4973020554, -18.8181533813, - -15.4233045578, -19.8299636841, -15.3453159332, -20.8257865906, - -15.2634954453, -21.8049926758, -15.1780204773, -22.7670307159, - -15.0890903473, -23.7114276886, -14.9969215393, -24.6378002167, - -14.9017429352, -25.5458450317, -14.8037958145, -26.4353485107, - -14.7033243179, -27.3061733246, -14.6005821228, -28.1582660675, - -14.4958209991, -28.9916419983, -14.3892917633, -29.8063888550, - -14.2812309265, -30.6026382446, -13.3068008423, 30.7080020905, - -13.4081001282, 29.9103755951, -13.5079383850, 29.0940208435, - -13.6060838699, 28.2587909698, -13.7022991180, 27.4045982361, - -13.7963418961, 26.5314273834, -13.8879728317, 25.6393356323, - -13.9769563675, 24.7284622192, -14.0630645752, 23.7990322113, - -14.1460809708, 22.8513603210, -14.2258014679, 21.8858470917, - -14.3020410538, 20.9029827118, -14.3746337891, 19.9033393860, - -14.4434366226, 18.8875694275, -14.5083284378, 17.8564052582, - -14.5692167282, 16.8106346130, -14.6260318756, 15.7511110306, - -14.6787290573, 14.6787290573, -14.7272872925, 13.5944185257, - -14.7717065811, 12.4991369247, -14.8120079041, 11.3938522339, - -14.8482246399, 10.2795400620, -14.8804044724, 9.1571722031, - -14.9086036682, 8.0277090073, -14.9328804016, 6.8920984268, - -14.9532957077, 5.7512674332, -14.9699077606, 4.6061258316, - -14.9827699661, 3.4575622082, -14.9919233322, 2.3064496517, - -14.9974021912, 1.1536463499, -14.9992265701, -0.0000000000, - -14.9974021912, -1.1536463499, -14.9919233322, -2.3064496517, - -14.9827699661, -3.4575622082, -14.9699077606, -4.6061258316, - -14.9532957077, -5.7512674332, -14.9328804016, -6.8920984268, - -14.9086036682, -8.0277090073, -14.8804044724, -9.1571722031, - -14.8482246399, -10.2795400620, -14.8120079041, -11.3938522339, - -14.7717065811, -12.4991369247, -14.7272872925, -13.5944185257, - -14.6787290573, -14.6787290573, -14.6260318756, -15.7511110306, - -14.5692167282, -16.8106346130, -14.5083284378, -17.8564052582, - -14.4434366226, -18.8875694275, -14.3746337891, -19.9033393860, - -14.3020410538, -20.9029827118, -14.2258014679, -21.8858470917, - -14.1460809708, -22.8513603210, -14.0630645752, -23.7990322113, - -13.9769563675, -24.7284622192, -13.8879728317, -25.6393356323, - -13.7963418961, -26.5314273834, -13.7022991180, -27.4045982361, - -13.6060838699, -28.2587909698, -13.5079383850, -29.0940208435, - -13.4081001282, -29.9103755951, -13.3068008423, -30.7080020905, - -12.3225889206, 30.8064727783, -12.4169082642, 30.0075283051, - -12.5098447800, 29.1896381378, -12.6011743546, 28.3526420593, - -12.6906690598, 27.4964504242, -12.7781000137, 26.6210422516, - -12.8632402420, 25.7264804840, -12.9458684921, 24.8129138947, - -13.0257682800, 23.8805751801, -13.1027374268, 22.9297904968, - -13.1765851974, 21.9609756470, -13.2471399307, 20.9746379852, - -13.3142499924, 19.9713764191, -13.3777856827, 18.9518623352, - -13.4376392365, 17.9168510437, -13.4937295914, 16.8671627045, - -13.5460004807, 15.8036670685, -13.5944185257, 14.7272872925, - -13.6389751434, 13.6389751434, -13.6796798706, 12.5397062302, - -13.7165613174, 11.4304676056, -13.7496623993, 10.3122472763, - -13.7790384293, 9.1860256195, -13.8047494888, 8.0527706146, - -13.8268623352, 6.9134311676, -13.8454399109, 5.7689332962, - -13.8605442047, 4.6201815605, -13.8722305298, 3.4680576324, - -13.8805427551, 2.3134238720, -13.8855171204, 1.1571264267, - -13.8871726990, -0.0000000000, -13.8855171204, -1.1571264267, - -13.8805427551, -2.3134238720, -13.8722305298, -3.4680576324, - -13.8605442047, -4.6201815605, -13.8454399109, -5.7689332962, - -13.8268623352, -6.9134311676, -13.8047494888, -8.0527706146, - -13.7790384293, -9.1860256195, -13.7496623993, -10.3122472763, - -13.7165613174, -11.4304676056, -13.6796798706, -12.5397062302, - -13.6389751434, -13.6389751434, -13.5944185257, -14.7272872925, - -13.5460004807, -15.8036670685, -13.4937295914, -16.8671627045, - -13.4376392365, -17.9168510437, -13.3777856827, -18.9518623352, - -13.3142499924, -19.9713764191, -13.2471399307, -20.9746379852, - -13.1765851974, -21.9609756470, -13.1027374268, -22.9297904968, - -13.0257682800, -23.8805751801, -12.9458684921, -24.8129138947, - -12.8632402420, -25.7264804840, -12.7781000137, -26.6210422516, - -12.6906690598, -27.4964504242, -12.6011743546, -28.3526420593, - -12.5098447800, -29.1896381378, -12.4169082642, -30.0075283051, - -12.3225889206, -30.8064727783, -11.3292007446, 30.8978214264, - -11.4163465500, 30.0976409912, -11.5021886826, 29.2782974243, - -11.5865163803, 28.4396305084, -11.6691150665, 27.5815448761, - -11.7497692108, 26.7040214539, -11.8282670975, 25.8071269989, - -11.9043979645, 24.8910140991, -11.9779634476, 23.9559268951, - -12.0487728119, 23.0022029877, -12.1166515350, 22.0302753448, - -12.1814413071, 21.0406703949, -12.2430028915, 20.0340042114, - -12.3012199402, 19.0109767914, -12.3559989929, 17.9723625183, - -12.4072723389, 16.9190063477, -12.4549913406, 15.8518075943, - -12.4991369247, 14.7717065811, -12.5397062302, 13.6796798706, - -12.5767202377, 12.5767202377, -12.6102132797, 11.4638299942, - -12.6402349472, 10.3420104980, -12.6668453217, 9.2122516632, - -12.6901092529, 8.0755243301, -12.7100963593, 6.9327797890, - -12.7268714905, 5.7849416733, -12.7405004501, 4.6329092979, - -12.7510375977, 3.4775555134, -12.7585287094, 2.3197324276, - -12.7630100250, 1.1602735519, -12.7645006180, -0.0000000000, - -12.7630100250, -1.1602735519, -12.7585287094, -2.3197324276, - -12.7510375977, -3.4775555134, -12.7405004501, -4.6329092979, - -12.7268714905, -5.7849416733, -12.7100963593, -6.9327797890, - -12.6901092529, -8.0755243301, -12.6668453217, -9.2122516632, - -12.6402349472, -10.3420104980, -12.6102132797, -11.4638299942, - -12.5767202377, -12.5767202377, -12.5397062302, -13.6796798706, - -12.4991369247, -14.7717065811, -12.4549913406, -15.8518075943, - -12.4072723389, -16.9190063477, -12.3559989929, -17.9723625183, - -12.3012199402, -19.0109767914, -12.2430028915, -20.0340042114, - -12.1814413071, -21.0406703949, -12.1166515350, -22.0302753448, - -12.0487728119, -23.0022029877, -11.9779634476, -23.9559268951, - -11.9043979645, -24.8910140991, -11.8282670975, -25.8071269989, - -11.7497692108, -26.7040214539, -11.6691150665, -27.5815448761, - -11.5865163803, -28.4396305084, -11.5021886826, -29.2782974243, - -11.4163465500, -30.0976409912, -11.3292007446, -30.8978214264, - -10.3272886276, 30.9818668365, -10.4070777893, 30.1805267334, - -10.4856500626, 29.3598213196, -10.5628099442, 28.5195865631, - -10.6383552551, 27.6597232819, -10.7120866776, 26.7802181244, - -10.7838068008, 25.8811359406, -10.8533210754, 24.9626388550, - -10.9204444885, 24.0249786377, -10.9850025177, 23.0685062408, - -11.0468349457, 22.0936698914, -11.1057968140, 21.1010150909, - -11.1617650986, 20.0911769867, -11.2146348953, 19.0648784637, - -11.2643251419, 18.0229206085, -11.3107776642, 16.9661674500, - -11.3539581299, 15.8955411911, -11.3938522339, 14.8120079041, - -11.4304676056, 13.7165613174, -11.4638299942, 12.6102132797, - -11.4939804077, 11.4939804077, -11.5209722519, 10.3688755035, - -11.5448684692, 9.2358942032, -11.5657358170, 8.0960149765, - -11.5836448669, 6.9501867294, -11.5986623764, 5.7993311882, - -11.6108531952, 4.6443414688, -11.6202726364, 3.4860816002, - -11.6269655228, 2.3253931999, -11.6309680939, 1.1630967855, - -11.6322994232, -0.0000000000, -11.6309680939, -1.1630967855, - -11.6269655228, -2.3253931999, -11.6202726364, -3.4860816002, - -11.6108531952, -4.6443414688, -11.5986623764, -5.7993311882, - -11.5836448669, -6.9501867294, -11.5657358170, -8.0960149765, - -11.5448684692, -9.2358942032, -11.5209722519, -10.3688755035, - -11.4939804077, -11.4939804077, -11.4638299942, -12.6102132797, - -11.4304676056, -13.7165613174, -11.3938522339, -14.8120079041, - -11.3539581299, -15.8955411911, -11.3107776642, -16.9661674500, - -11.2643251419, -18.0229206085, -11.2146348953, -19.0648784637, - -11.1617650986, -20.0911769867, -11.1057968140, -21.1010150909, - -11.0468349457, -22.0936698914, -10.9850025177, -23.0685062408, - -10.9204444885, -24.0249786377, -10.8533210754, -24.9626388550, - -10.7838068008, -25.8811359406, -10.7120866776, -26.7802181244, - -10.6383552551, -27.6597232819, -10.5628099442, -28.5195865631, - -10.4856500626, -29.3598213196, -10.4070777893, -30.1805267334, - -10.3272886276, -30.9818668365, -9.3175296783, 31.0584316254, - -9.3897972107, 30.2560138702, -9.4609432220, 29.4340457916, - -9.5307846069, 28.5923538208, -9.5991382599, 27.7308425903, - -9.6658191681, 26.8494987488, -9.7306461334, 25.9483890533, - -9.7934408188, 25.0276813507, -9.8540334702, 24.0876388550, - -9.9122667313, 23.1286220551, -9.9679937363, 22.1510982513, - -10.0210866928, 21.1556262970, -10.0714330673, 20.1428661346, - -10.1189422607, 19.1135578156, -10.1635446548, 18.0685253143, - -10.2051935196, 17.0086555481, -10.2438602448, 15.9348936081, - -10.2795400620, 14.8482246399, -10.3122472763, 13.7496623993, - -10.3420104980, 12.6402349472, -10.3688755035, 11.5209722519, - -10.3928966522, 10.3928966522, -10.4141378403, 9.2570114136, - -10.4326667786, 8.1142959595, -10.4485530853, 6.9657020569, - -10.4618625641, 5.8121461868, -10.4726581573, 4.6545147896, - -10.4809942245, 3.4936647415, -10.4869146347, 2.3304255009, - -10.4904632568, 1.1656070948, -10.4916400909, -0.0000000000, - -10.4904632568, -1.1656070948, -10.4869146347, -2.3304255009, - -10.4809942245, -3.4936647415, -10.4726581573, -4.6545147896, - -10.4618625641, -5.8121461868, -10.4485530853, -6.9657020569, - -10.4326667786, -8.1142959595, -10.4141378403, -9.2570114136, - -10.3928966522, -10.3928966522, -10.3688755035, -11.5209722519, - -10.3420104980, -12.6402349472, -10.3122472763, -13.7496623993, - -10.2795400620, -14.8482246399, -10.2438602448, -15.9348936081, - -10.2051935196, -17.0086555481, -10.1635446548, -18.0685253143, - -10.1189422607, -19.1135578156, -10.0714330673, -20.1428661346, - -10.0210866928, -21.1556262970, -9.9679937363, -22.1510982513, - -9.9122667313, -23.1286220551, -9.8540334702, -24.0876388550, - -9.7934408188, -25.0276813507, -9.7306461334, -25.9483890533, - -9.6658191681, -26.8494987488, -9.5991382599, -27.7308425903, - -9.5307846069, -28.5923538208, -9.4609432220, -29.4340457916, - -9.3897972107, -30.2560138702, -9.3175296783, -31.0584316254, - -8.3006286621, 31.1273555756, -8.3652276993, 30.3239517212, - -8.4288072586, 29.5008239746, -8.4912004471, 28.6578006744, - -8.5522403717, 27.7947807312, -8.6117601395, 26.9117507935, - -8.6695947647, 26.0087852478, -8.7255840302, 25.0860557556, - -8.7795763016, 24.1438331604, -8.8314266205, 23.1824951172, - -8.8810071945, 22.2025184631, -8.9282026291, 21.2044811249, - -8.9729146957, 20.1890583038, -9.0150651932, 19.1570129395, - -9.0545949936, 18.1091899872, -9.0914659500, 17.0464992523, - -9.1256580353, 15.9699020386, -9.1571722031, 14.8804044724, - -9.1860256195, 13.7790384293, -9.2122516632, 12.6668453217, - -9.2358942032, 11.5448684692, -9.2570114136, 10.4141378403, - -9.2756633759, 9.2756633759, -9.2919178009, 8.1304273605, - -9.3058395386, 6.9793796539, -9.3174943924, 5.8234338760, - -9.3269481659, 4.6634740829, -9.3342361450, 3.5003383160, - -9.3394098282, 2.3348524570, -9.3425016403, 1.1678127050, - -9.3435297012, -0.0000000000, -9.3425016403, -1.1678127050, - -9.3394098282, -2.3348524570, -9.3342361450, -3.5003383160, - -9.3269481659, -4.6634740829, -9.3174943924, -5.8234338760, - -9.3058395386, -6.9793796539, -9.2919178009, -8.1304273605, - -9.2756633759, -9.2756633759, -9.2570114136, -10.4141378403, - -9.2358942032, -11.5448684692, -9.2122516632, -12.6668453217, - -9.1860256195, -13.7790384293, -9.1571722031, -14.8804044724, - -9.1256580353, -15.9699020386, -9.0914659500, -17.0464992523, - -9.0545949936, -18.1091899872, -9.0150651932, -19.1570129395, - -8.9729146957, -20.1890583038, -8.9282026291, -21.2044811249, - -8.8810071945, -22.2025184631, -8.8314266205, -23.1824951172, - -8.7795763016, -24.1438331604, -8.7255840302, -25.0860557556, - -8.6695947647, -26.0087852478, -8.6117601395, -26.9117507935, - -8.5522403717, -27.7947807312, -8.4912004471, -28.6578006744, - -8.4288072586, -29.5008239746, -8.3652276993, -30.3239517212, - -8.3006286621, -31.1273555756, -7.2773165703, 31.1884994507, - -7.3341183662, 30.3842048645, -7.3900084496, 29.5600337982, - -7.4448385239, 28.7158069611, -7.4984607697, 27.8514251709, - -7.5507259369, 26.9668769836, -7.6014866829, 26.0622406006, - -7.6506013870, 25.1376914978, -7.6979346275, 24.1935100555, - -7.7433609962, 23.2300834656, -7.7867655754, 22.2479019165, - -7.8280491829, 21.2475624084, -7.8671264648, 20.2297534943, - -7.9039311409, 19.1952610016, -7.9384136200, 18.1449451447, - -7.9705429077, 17.0797348022, -8.0003070831, 16.0006141663, - -8.0277090073, 14.9086036682, -8.0527706146, 13.8047494888, - -8.0755243301, 12.6901092529, -8.0960149765, 11.5657358170, - -8.1142959595, 10.4326667786, -8.1304273605, 9.2919178009, - -8.1444711685, 8.1444711685, -8.1564893723, 6.9912767410, - -8.1665477753, 5.8332486153, -8.1746883392, 4.6712508202, - -8.1809673309, 3.5061287880, -8.1854228973, 2.3386921883, - -8.1880846024, 1.1697263718, -8.1889696121, -0.0000000000, - -8.1880846024, -1.1697263718, -8.1854228973, -2.3386921883, - -8.1809673309, -3.5061287880, -8.1746883392, -4.6712508202, - -8.1665477753, -5.8332486153, -8.1564893723, -6.9912767410, - -8.1444711685, -8.1444711685, -8.1304273605, -9.2919178009, - -8.1142959595, -10.4326667786, -8.0960149765, -11.5657358170, - -8.0755243301, -12.6901092529, -8.0527706146, -13.8047494888, - -8.0277090073, -14.9086036682, -8.0003070831, -16.0006141663, - -7.9705429077, -17.0797348022, -7.9384136200, -18.1449451447, - -7.9039311409, -19.1952610016, -7.8671264648, -20.2297534943, - -7.8280491829, -21.2475624084, -7.7867655754, -22.2479019165, - -7.7433609962, -23.2300834656, -7.6979346275, -24.1935100555, - -7.6506013870, -25.1376914978, -7.6014866829, -26.0622406006, - -7.5507259369, -26.9668769836, -7.4984607697, -27.8514251709, - -7.4448385239, -28.7158069611, -7.3900084496, -29.5600337982, - -7.3341183662, -30.3842048645, -7.2773165703, -31.1884994507, - -6.2483472824, 31.2417373657, -6.2972393036, 30.4366550446, - -6.3453345299, 29.6115608215, -6.3925046921, 28.7662715912, - -6.4386205673, 27.9006881714, -6.4835519791, 27.0147991180, - -6.5271716118, 26.1086864471, -6.5693559647, 25.1825313568, - -6.6099877357, 24.2366218567, -6.6489582062, 23.2713546753, - -6.6861701012, 22.2872333527, -6.7215371132, 21.2848682404, - -6.7549881935, 20.2649650574, -6.7864665985, 19.2283229828, - -6.8159332275, 18.1758213043, -6.8433632851, 17.1084079742, - -6.8687496185, 16.0270824432, -6.8920984268, 14.9328804016, - -6.9134311676, 13.8268623352, -6.9327797890, 12.7100963593, - -6.9501867294, 11.5836448669, -6.9657020569, 10.4485530853, - -6.9793796539, 9.3058395386, -6.9912767410, 8.1564893723, - -7.0014543533, 7.0014543533, -7.0099563599, 5.8416299820, - -7.0168380737, 4.6778917313, -7.0221428871, 3.5110714436, - -7.0259060860, 2.3419685364, -7.0281534195, 1.1713588238, - -7.0289006233, -0.0000000000, -7.0281534195, -1.1713588238, - -7.0259060860, -2.3419685364, -7.0221428871, -3.5110714436, - -7.0168380737, -4.6778917313, -7.0099563599, -5.8416299820, - -7.0014543533, -7.0014543533, -6.9912767410, -8.1564893723, - -6.9793796539, -9.3058395386, -6.9657020569, -10.4485530853, - -6.9501867294, -11.5836448669, -6.9327797890, -12.7100963593, - -6.9134311676, -13.8268623352, -6.8920984268, -14.9328804016, - -6.8687496185, -16.0270824432, -6.8433632851, -17.1084079742, - -6.8159332275, -18.1758213043, -6.7864665985, -19.2283229828, - -6.7549881935, -20.2649650574, -6.7215371132, -21.2848682404, - -6.6861701012, -22.2872333527, -6.6489582062, -23.2713546753, - -6.6099877357, -24.2366218567, -6.5693559647, -25.1825313568, - -6.5271716118, -26.1086864471, -6.4835519791, -27.0147991180, - -6.4386205673, -27.9006881714, -6.3925046921, -28.7662715912, - -6.3453345299, -29.6115608215, -6.2972393036, -30.4366550446, - -6.2483472824, -31.2417373657, -5.2144937515, 31.2869625092, - -5.2553801537, 30.4812030792, -5.2955918312, 29.6553134918, - -5.3350205421, 28.8091106415, -5.3735561371, 27.9424934387, - -5.4110903740, 27.0554504395, -5.4475140572, 26.1480674744, - -5.4827241898, 25.2205314636, -5.5166220665, 24.2731380463, - -5.5491170883, 23.3062915802, -5.5801262856, 22.3205051422, - -5.6095800400, 21.3164043427, -5.6374182701, 20.2947063446, - -5.6635961533, 19.2562274933, -5.6880812645, 18.2018604279, - -5.7108559608, 17.1325664520, -5.7319149971, 16.0493621826, - -5.7512674332, 14.9532957077, -5.7689332962, 13.8454399109, - -5.7849416733, 12.7268714905, -5.7993311882, 11.5986623764, - -5.8121461868, 10.4618625641, -5.8234338760, 9.3174943924, - -5.8332486153, 8.1665477753, -5.8416299820, 7.0099563599, - -5.8486313820, 5.8486313820, -5.8542957306, 4.6834368706, - -5.8586602211, 3.5151960850, -5.8617553711, 2.3447020054, - -5.8636031151, 1.1727205515, -5.8642172813, -0.0000000000, - -5.8636031151, -1.1727205515, -5.8617553711, -2.3447020054, - -5.8586602211, -3.5151960850, -5.8542957306, -4.6834368706, - -5.8486313820, -5.8486313820, -5.8416299820, -7.0099563599, - -5.8332486153, -8.1665477753, -5.8234338760, -9.3174943924, - -5.8121461868, -10.4618625641, -5.7993311882, -11.5986623764, - -5.7849416733, -12.7268714905, -5.7689332962, -13.8454399109, - -5.7512674332, -14.9532957077, -5.7319149971, -16.0493621826, - -5.7108559608, -17.1325664520, -5.6880812645, -18.2018604279, - -5.6635961533, -19.2562274933, -5.6374182701, -20.2947063446, - -5.6095800400, -21.3164043427, -5.5801262856, -22.3205051422, - -5.5491170883, -23.3062915802, -5.5166220665, -24.2731380463, - -5.4827241898, -25.2205314636, -5.4475140572, -26.1480674744, - -5.4110903740, -27.0554504395, -5.3735561371, -27.9424934387, - -5.3350205421, -28.8091106415, -5.2955918312, -29.6553134918, - -5.2553801537, -30.4812030792, -5.2144937515, -31.2869625092, - -4.1765446663, 31.3240852356, -4.2093467712, 30.5177650452, - -4.2416019440, 29.6912136078, -4.2732224464, 28.8442516327, - -4.3041195869, 27.9767761230, -4.3342041969, 27.0887775421, - -4.3633904457, 26.1803417206, -4.3915929794, 25.2516613007, - -4.4187340736, 24.3030376434, -4.4447393417, 23.3348808289, - -4.4695439339, 22.3477191925, -4.4930906296, 21.3421802521, - -4.5153336525, 20.3190002441, -4.5362362862, 19.2790031433, - -4.5557746887, 18.2230987549, -4.5739350319, 17.1522560120, - -4.5907158852, 16.0675067902, -4.6061258316, 14.9699077606, - -4.6201815605, 13.8605442047, -4.6329092979, 12.7405004501, - -4.6443414688, 11.6108531952, -4.6545147896, 10.4726581573, - -4.6634740829, 9.3269481659, -4.6712508202, 8.1746883392, - -4.6778917313, 7.0168380737, -4.6834368706, 5.8542957306, - -4.6879205704, 4.6879205704, -4.6913738251, 3.5185303688, - -4.6938219070, 2.3469109535, -4.6952834129, 1.1738208532, - -4.6957693100, -0.0000000000, -4.6952834129, -1.1738208532, - -4.6938219070, -2.3469109535, -4.6913738251, -3.5185303688, - -4.6879205704, -4.6879205704, -4.6834368706, -5.8542957306, - -4.6778917313, -7.0168380737, -4.6712508202, -8.1746883392, - -4.6634740829, -9.3269481659, -4.6545147896, -10.4726581573, - -4.6443414688, -11.6108531952, -4.6329092979, -12.7405004501, - -4.6201815605, -13.8605442047, -4.6061258316, -14.9699077606, - -4.5907158852, -16.0675067902, -4.5739350319, -17.1522560120, - -4.5557746887, -18.2230987549, -4.5362362862, -19.2790031433, - -4.5153336525, -20.3190002441, -4.4930906296, -21.3421802521, - -4.4695439339, -22.3477191925, -4.4447393417, -23.3348808289, - -4.4187340736, -24.3030376434, -4.3915929794, -25.2516613007, - -4.3633904457, -26.1803417206, -4.3342041969, -27.0887775421, - -4.3041195869, -27.9767761230, -4.2732224464, -28.8442516327, - -4.2416019440, -29.6912136078, -4.2093467712, -30.5177650452, - -4.1765446663, -31.3240852356, -3.1353034973, 31.3530349731, - -3.1599588394, 30.5462703705, -3.1842000484, 29.7192001343, - -3.2079601288, 28.8716411591, -3.2311718464, 28.0034904480, - -3.2537686825, 27.1147384644, -3.2756843567, 26.2054748535, - -3.2968556881, 25.2758941650, -3.3172230721, 24.3263034821, - -3.3367314339, 23.3571205139, -3.3553314209, 22.3688755035, - -3.3729808331, 21.3622112274, -3.3896448612, 20.3378696442, - -3.4052970409, 19.2966842651, -3.4199199677, 18.2395725250, - -3.4335041046, 17.1675205231, -3.4460492134, 16.0815639496, - -3.4575622082, 14.9827699661, -3.4680576324, 13.8722305298, - -3.4775555134, 12.7510375977, -3.4860816002, 11.6202726364, - -3.4936647415, 10.4809942245, -3.5003383160, 9.3342361450, - -3.5061287880, 8.1809673309, -3.5110714436, 7.0221428871, - -3.5151960850, 5.8586602211, -3.5185303688, 4.6913738251, - -3.5210976601, 3.5210976601, -3.5229170322, 2.3486113548, - -3.5240030289, 1.1746675968, -3.5243639946, -0.0000000000, - -3.5240030289, -1.1746675968, -3.5229170322, -2.3486113548, - -3.5210976601, -3.5210976601, -3.5185303688, -4.6913738251, - -3.5151960850, -5.8586602211, -3.5110714436, -7.0221428871, - -3.5061287880, -8.1809673309, -3.5003383160, -9.3342361450, - -3.4936647415, -10.4809942245, -3.4860816002, -11.6202726364, - -3.4775555134, -12.7510375977, -3.4680576324, -13.8722305298, - -3.4575622082, -14.9827699661, -3.4460492134, -16.0815639496, - -3.4335041046, -17.1675205231, -3.4199199677, -18.2395725250, - -3.4052970409, -19.2966842651, -3.3896448612, -20.3378696442, - -3.3729808331, -21.3622112274, -3.3553314209, -22.3688755035, - -3.3367314339, -23.3571205139, -3.3172230721, -24.3263034821, - -3.2968556881, -25.2758941650, -3.2756843567, -26.2054748535, - -3.2537686825, -27.1147384644, -3.2311718464, -28.0034904480, - -3.2079601288, -28.8716411591, -3.1842000484, -29.7192001343, - -3.1599588394, -30.5462703705, -3.1353034973, -31.3530349731, - -2.0915834904, 31.3737506866, -2.1080460548, 30.5666675568, - -2.1242303848, 29.7392234802, -2.1400914192, 28.8912334442, - -2.1555843353, 28.0225963593, -2.1706643105, 27.1333026886, - -2.1852869987, 26.2234420776, -2.1994099617, 25.2932147980, - -2.2129933834, 24.3429279327, -2.2260005474, 23.3730049133, - -2.2383983135, 22.3839836121, -2.2501587868, 21.3765087128, - -2.2612588406, 20.3513298035, -2.2716813087, 19.3092918396, - -2.2814145088, 18.2513160706, -2.2904527187, 17.1783962250, - -2.2987961769, 16.0915718079, -2.3064496517, 14.9919233322, - -2.3134238720, 13.8805427551, -2.3197324276, 12.7585287094, - -2.3253931999, 11.6269655228, -2.3304255009, 10.4869146347, - -2.3348524570, 9.3394098282, -2.3386921883, 8.1854228973, - -2.3419685364, 7.0259060860, -2.3447020054, 5.8617553711, - -2.3469109535, 4.6938219070, -2.3486113548, 3.5229170322, - -2.3498163223, 2.3498163223, -2.3505353928, 1.1752676964, - -2.3507742882, -0.0000000000, -2.3505353928, -1.1752676964, - -2.3498163223, -2.3498163223, -2.3486113548, -3.5229170322, - -2.3469109535, -4.6938219070, -2.3447020054, -5.8617553711, - -2.3419685364, -7.0259060860, -2.3386921883, -8.1854228973, - -2.3348524570, -9.3394098282, -2.3304255009, -10.4869146347, - -2.3253931999, -11.6269655228, -2.3197324276, -12.7585287094, - -2.3134238720, -13.8805427551, -2.3064496517, -14.9919233322, - -2.2987961769, -16.0915718079, -2.2904527187, -17.1783962250, - -2.2814145088, -18.2513160706, -2.2716813087, -19.3092918396, - -2.2612588406, -20.3513298035, -2.2501587868, -21.3765087128, - -2.2383983135, -22.3839836121, -2.2260005474, -23.3730049133, - -2.2129933834, -24.3429279327, -2.1994099617, -25.2932147980, - -2.1852869987, -26.2234420776, -2.1706643105, -27.1333026886, - -2.1555843353, -28.0225963593, -2.1400914192, -28.8912334442, - -2.1242303848, -29.7392234802, -2.1080460548, -30.5666675568, - -2.0915834904, -31.3737506866, -1.0462065935, 31.3861980438, - -1.0544456244, 30.5789222717, -1.0625447035, 29.7512512207, - -1.0704815388, 28.9030017853, -1.0782334805, 28.0340709686, - -1.0857779980, 27.1444492340, -1.0930929184, 26.2342300415, - -1.1001570225, 25.3036098480, -1.1069501638, 24.3529033661, - -1.1134541035, 23.3825359344, -1.1196522713, 22.3930454254, - -1.1255306005, 21.3850822449, -1.1310777664, 20.3593997955, - -1.1362851858, 19.3168468475, -1.1411470175, 18.2583522797, - -1.1456606388, 17.1849098206, -1.1498261690, 16.0975666046, - -1.1536463499, 14.9974021912, -1.1571264267, 13.8855171204, - -1.1602735519, 12.7630100250, -1.1630967855, 11.6309680939, - -1.1656070948, 10.4904632568, -1.1678127050, 9.3425016403, - -1.1697263718, 8.1880846024, -1.1713588238, 7.0281534195, - -1.1727205515, 5.8636031151, -1.1738208532, 4.6952834129, - -1.1746675968, 3.5240030289, -1.1752676964, 2.3505353928, - -1.1756256819, 1.1756256819, -1.1757446527, -0.0000000000, - -1.1756256819, -1.1756256819, -1.1752676964, -2.3505353928, - -1.1746675968, -3.5240030289, -1.1738208532, -4.6952834129, - -1.1727205515, -5.8636031151, -1.1713588238, -7.0281534195, - -1.1697263718, -8.1880846024, -1.1678127050, -9.3425016403, - -1.1656070948, -10.4904632568, -1.1630967855, -11.6309680939, - -1.1602735519, -12.7630100250, -1.1571264267, -13.8855171204, - -1.1536463499, -14.9974021912, -1.1498261690, -16.0975666046, - -1.1456606388, -17.1849098206, -1.1411470175, -18.2583522797, - -1.1362851858, -19.3168468475, -1.1310777664, -20.3593997955, - -1.1255306005, -21.3850822449, -1.1196522713, -22.3930454254, - -1.1134541035, -23.3825359344, -1.1069501638, -24.3529033661, - -1.1001570225, -25.3036098480, -1.0930929184, -26.2342300415, - -1.0857779980, -27.1444492340, -1.0782334805, -28.0340709686, - -1.0704815388, -28.9030017853, -1.0625447035, -29.7512512207, - -1.0544456244, -30.5789222717, -1.0462065935, -31.3861980438, - 0.0000000000, 31.3903484344, 0.0000000000, 30.5830078125, - 0.0000000000, 29.7552623749, 0.0000000000, 28.9069271088, - 0.0000000000, 28.0378971100, 0.0000000000, 27.1481666565, - 0.0000000000, 26.2378273010, 0.0000000000, 25.3070774078, - 0.0000000000, 24.3562297821, 0.0000000000, 23.3857116699, - 0.0000000000, 22.3960647583, 0.0000000000, 21.3879394531, - 0.0000000000, 20.3620891571, 0.0000000000, 19.3193645477, - 0.0000000000, 18.2606964111, 0.0000000000, 17.1870784760, - 0.0000000000, 16.0995616913, 0.0000000000, 14.9992265701, - 0.0000000000, 13.8871726990, 0.0000000000, 12.7645006180, - 0.0000000000, 11.6322994232, 0.0000000000, 10.4916400909, - 0.0000000000, 9.3435297012, 0.0000000000, 8.1889696121, - 0.0000000000, 7.0289006233, 0.0000000000, 5.8642172813, - 0.0000000000, 4.6957693100, 0.0000000000, 3.5243639946, - 0.0000000000, 2.3507742882, 0.0000000000, 1.1757446527, - 0.0000000000, -0.0000000000, 0.0000000000, -1.1757446527, - 0.0000000000, -2.3507742882, 0.0000000000, -3.5243639946, - 0.0000000000, -4.6957693100, 0.0000000000, -5.8642172813, - 0.0000000000, -7.0289006233, 0.0000000000, -8.1889696121, - 0.0000000000, -9.3435297012, 0.0000000000, -10.4916400909, - 0.0000000000, -11.6322994232, 0.0000000000, -12.7645006180, - 0.0000000000, -13.8871726990, 0.0000000000, -14.9992265701, - 0.0000000000, -16.0995616913, 0.0000000000, -17.1870784760, - 0.0000000000, -18.2606964111, 0.0000000000, -19.3193645477, - 0.0000000000, -20.3620891571, 0.0000000000, -21.3879394531, - 0.0000000000, -22.3960647583, 0.0000000000, -23.3857116699, - 0.0000000000, -24.3562297821, 0.0000000000, -25.3070774078, - 0.0000000000, -26.2378273010, 0.0000000000, -27.1481666565, - 0.0000000000, -28.0378971100, 0.0000000000, -28.9069271088, - 0.0000000000, -29.7552623749, 0.0000000000, -30.5830078125, - 0.0000000000, -31.3903484344, 1.0462065935, 31.3861980438, - 1.0544456244, 30.5789222717, 1.0625447035, 29.7512512207, - 1.0704815388, 28.9030017853, 1.0782334805, 28.0340709686, - 1.0857779980, 27.1444492340, 1.0930929184, 26.2342300415, - 1.1001570225, 25.3036098480, 1.1069501638, 24.3529033661, - 1.1134541035, 23.3825359344, 1.1196522713, 22.3930454254, - 1.1255306005, 21.3850822449, 1.1310777664, 20.3593997955, - 1.1362851858, 19.3168468475, 1.1411470175, 18.2583522797, - 1.1456606388, 17.1849098206, 1.1498261690, 16.0975666046, - 1.1536463499, 14.9974021912, 1.1571264267, 13.8855171204, - 1.1602735519, 12.7630100250, 1.1630967855, 11.6309680939, - 1.1656070948, 10.4904632568, 1.1678127050, 9.3425016403, - 1.1697263718, 8.1880846024, 1.1713588238, 7.0281534195, - 1.1727205515, 5.8636031151, 1.1738208532, 4.6952834129, - 1.1746675968, 3.5240030289, 1.1752676964, 2.3505353928, - 1.1756256819, 1.1756256819, 1.1757446527, -0.0000000000, - 1.1756256819, -1.1756256819, 1.1752676964, -2.3505353928, - 1.1746675968, -3.5240030289, 1.1738208532, -4.6952834129, - 1.1727205515, -5.8636031151, 1.1713588238, -7.0281534195, - 1.1697263718, -8.1880846024, 1.1678127050, -9.3425016403, - 1.1656070948, -10.4904632568, 1.1630967855, -11.6309680939, - 1.1602735519, -12.7630100250, 1.1571264267, -13.8855171204, - 1.1536463499, -14.9974021912, 1.1498261690, -16.0975666046, - 1.1456606388, -17.1849098206, 1.1411470175, -18.2583522797, - 1.1362851858, -19.3168468475, 1.1310777664, -20.3593997955, - 1.1255306005, -21.3850822449, 1.1196522713, -22.3930454254, - 1.1134541035, -23.3825359344, 1.1069501638, -24.3529033661, - 1.1001570225, -25.3036098480, 1.0930929184, -26.2342300415, - 1.0857779980, -27.1444492340, 1.0782334805, -28.0340709686, - 1.0704815388, -28.9030017853, 1.0625447035, -29.7512512207, - 1.0544456244, -30.5789222717, 1.0462065935, -31.3861980438, - 2.0915834904, 31.3737506866, 2.1080460548, 30.5666675568, - 2.1242303848, 29.7392234802, 2.1400914192, 28.8912334442, - 2.1555843353, 28.0225963593, 2.1706643105, 27.1333026886, - 2.1852869987, 26.2234420776, 2.1994099617, 25.2932147980, - 2.2129933834, 24.3429279327, 2.2260005474, 23.3730049133, - 2.2383983135, 22.3839836121, 2.2501587868, 21.3765087128, - 2.2612588406, 20.3513298035, 2.2716813087, 19.3092918396, - 2.2814145088, 18.2513160706, 2.2904527187, 17.1783962250, - 2.2987961769, 16.0915718079, 2.3064496517, 14.9919233322, - 2.3134238720, 13.8805427551, 2.3197324276, 12.7585287094, - 2.3253931999, 11.6269655228, 2.3304255009, 10.4869146347, - 2.3348524570, 9.3394098282, 2.3386921883, 8.1854228973, - 2.3419685364, 7.0259060860, 2.3447020054, 5.8617553711, - 2.3469109535, 4.6938219070, 2.3486113548, 3.5229170322, - 2.3498163223, 2.3498163223, 2.3505353928, 1.1752676964, - 2.3507742882, -0.0000000000, 2.3505353928, -1.1752676964, - 2.3498163223, -2.3498163223, 2.3486113548, -3.5229170322, - 2.3469109535, -4.6938219070, 2.3447020054, -5.8617553711, - 2.3419685364, -7.0259060860, 2.3386921883, -8.1854228973, - 2.3348524570, -9.3394098282, 2.3304255009, -10.4869146347, - 2.3253931999, -11.6269655228, 2.3197324276, -12.7585287094, - 2.3134238720, -13.8805427551, 2.3064496517, -14.9919233322, - 2.2987961769, -16.0915718079, 2.2904527187, -17.1783962250, - 2.2814145088, -18.2513160706, 2.2716813087, -19.3092918396, - 2.2612588406, -20.3513298035, 2.2501587868, -21.3765087128, - 2.2383983135, -22.3839836121, 2.2260005474, -23.3730049133, - 2.2129933834, -24.3429279327, 2.1994099617, -25.2932147980, - 2.1852869987, -26.2234420776, 2.1706643105, -27.1333026886, - 2.1555843353, -28.0225963593, 2.1400914192, -28.8912334442, - 2.1242303848, -29.7392234802, 2.1080460548, -30.5666675568, - 2.0915834904, -31.3737506866, 3.1353034973, 31.3530349731, - 3.1599588394, 30.5462703705, 3.1842000484, 29.7192001343, - 3.2079601288, 28.8716411591, 3.2311718464, 28.0034904480, - 3.2537686825, 27.1147384644, 3.2756843567, 26.2054748535, - 3.2968556881, 25.2758941650, 3.3172230721, 24.3263034821, - 3.3367314339, 23.3571205139, 3.3553314209, 22.3688755035, - 3.3729808331, 21.3622112274, 3.3896448612, 20.3378696442, - 3.4052970409, 19.2966842651, 3.4199199677, 18.2395725250, - 3.4335041046, 17.1675205231, 3.4460492134, 16.0815639496, - 3.4575622082, 14.9827699661, 3.4680576324, 13.8722305298, - 3.4775555134, 12.7510375977, 3.4860816002, 11.6202726364, - 3.4936647415, 10.4809942245, 3.5003383160, 9.3342361450, - 3.5061287880, 8.1809673309, 3.5110714436, 7.0221428871, - 3.5151960850, 5.8586602211, 3.5185303688, 4.6913738251, - 3.5210976601, 3.5210976601, 3.5229170322, 2.3486113548, - 3.5240030289, 1.1746675968, 3.5243639946, -0.0000000000, - 3.5240030289, -1.1746675968, 3.5229170322, -2.3486113548, - 3.5210976601, -3.5210976601, 3.5185303688, -4.6913738251, - 3.5151960850, -5.8586602211, 3.5110714436, -7.0221428871, - 3.5061287880, -8.1809673309, 3.5003383160, -9.3342361450, - 3.4936647415, -10.4809942245, 3.4860816002, -11.6202726364, - 3.4775555134, -12.7510375977, 3.4680576324, -13.8722305298, - 3.4575622082, -14.9827699661, 3.4460492134, -16.0815639496, - 3.4335041046, -17.1675205231, 3.4199199677, -18.2395725250, - 3.4052970409, -19.2966842651, 3.3896448612, -20.3378696442, - 3.3729808331, -21.3622112274, 3.3553314209, -22.3688755035, - 3.3367314339, -23.3571205139, 3.3172230721, -24.3263034821, - 3.2968556881, -25.2758941650, 3.2756843567, -26.2054748535, - 3.2537686825, -27.1147384644, 3.2311718464, -28.0034904480, - 3.2079601288, -28.8716411591, 3.1842000484, -29.7192001343, - 3.1599588394, -30.5462703705, 3.1353034973, -31.3530349731, - 4.1765446663, 31.3240852356, 4.2093467712, 30.5177650452, - 4.2416019440, 29.6912136078, 4.2732224464, 28.8442516327, - 4.3041195869, 27.9767761230, 4.3342041969, 27.0887775421, - 4.3633904457, 26.1803417206, 4.3915929794, 25.2516613007, - 4.4187340736, 24.3030376434, 4.4447393417, 23.3348808289, - 4.4695439339, 22.3477191925, 4.4930906296, 21.3421802521, - 4.5153336525, 20.3190002441, 4.5362362862, 19.2790031433, - 4.5557746887, 18.2230987549, 4.5739350319, 17.1522560120, - 4.5907158852, 16.0675067902, 4.6061258316, 14.9699077606, - 4.6201815605, 13.8605442047, 4.6329092979, 12.7405004501, - 4.6443414688, 11.6108531952, 4.6545147896, 10.4726581573, - 4.6634740829, 9.3269481659, 4.6712508202, 8.1746883392, - 4.6778917313, 7.0168380737, 4.6834368706, 5.8542957306, - 4.6879205704, 4.6879205704, 4.6913738251, 3.5185303688, - 4.6938219070, 2.3469109535, 4.6952834129, 1.1738208532, - 4.6957693100, -0.0000000000, 4.6952834129, -1.1738208532, - 4.6938219070, -2.3469109535, 4.6913738251, -3.5185303688, - 4.6879205704, -4.6879205704, 4.6834368706, -5.8542957306, - 4.6778917313, -7.0168380737, 4.6712508202, -8.1746883392, - 4.6634740829, -9.3269481659, 4.6545147896, -10.4726581573, - 4.6443414688, -11.6108531952, 4.6329092979, -12.7405004501, - 4.6201815605, -13.8605442047, 4.6061258316, -14.9699077606, - 4.5907158852, -16.0675067902, 4.5739350319, -17.1522560120, - 4.5557746887, -18.2230987549, 4.5362362862, -19.2790031433, - 4.5153336525, -20.3190002441, 4.4930906296, -21.3421802521, - 4.4695439339, -22.3477191925, 4.4447393417, -23.3348808289, - 4.4187340736, -24.3030376434, 4.3915929794, -25.2516613007, - 4.3633904457, -26.1803417206, 4.3342041969, -27.0887775421, - 4.3041195869, -27.9767761230, 4.2732224464, -28.8442516327, - 4.2416019440, -29.6912136078, 4.2093467712, -30.5177650452, - 4.1765446663, -31.3240852356, 5.2144937515, 31.2869625092, - 5.2553801537, 30.4812030792, 5.2955918312, 29.6553134918, - 5.3350205421, 28.8091106415, 5.3735561371, 27.9424934387, - 5.4110903740, 27.0554504395, 5.4475140572, 26.1480674744, - 5.4827241898, 25.2205314636, 5.5166220665, 24.2731380463, - 5.5491170883, 23.3062915802, 5.5801262856, 22.3205051422, - 5.6095800400, 21.3164043427, 5.6374182701, 20.2947063446, - 5.6635961533, 19.2562274933, 5.6880812645, 18.2018604279, - 5.7108559608, 17.1325664520, 5.7319149971, 16.0493621826, - 5.7512674332, 14.9532957077, 5.7689332962, 13.8454399109, - 5.7849416733, 12.7268714905, 5.7993311882, 11.5986623764, - 5.8121461868, 10.4618625641, 5.8234338760, 9.3174943924, - 5.8332486153, 8.1665477753, 5.8416299820, 7.0099563599, - 5.8486313820, 5.8486313820, 5.8542957306, 4.6834368706, - 5.8586602211, 3.5151960850, 5.8617553711, 2.3447020054, - 5.8636031151, 1.1727205515, 5.8642172813, -0.0000000000, - 5.8636031151, -1.1727205515, 5.8617553711, -2.3447020054, - 5.8586602211, -3.5151960850, 5.8542957306, -4.6834368706, - 5.8486313820, -5.8486313820, 5.8416299820, -7.0099563599, - 5.8332486153, -8.1665477753, 5.8234338760, -9.3174943924, - 5.8121461868, -10.4618625641, 5.7993311882, -11.5986623764, - 5.7849416733, -12.7268714905, 5.7689332962, -13.8454399109, - 5.7512674332, -14.9532957077, 5.7319149971, -16.0493621826, - 5.7108559608, -17.1325664520, 5.6880812645, -18.2018604279, - 5.6635961533, -19.2562274933, 5.6374182701, -20.2947063446, - 5.6095800400, -21.3164043427, 5.5801262856, -22.3205051422, - 5.5491170883, -23.3062915802, 5.5166220665, -24.2731380463, - 5.4827241898, -25.2205314636, 5.4475140572, -26.1480674744, - 5.4110903740, -27.0554504395, 5.3735561371, -27.9424934387, - 5.3350205421, -28.8091106415, 5.2955918312, -29.6553134918, - 5.2553801537, -30.4812030792, 5.2144937515, -31.2869625092, - 6.2483472824, 31.2417373657, 6.2972393036, 30.4366550446, - 6.3453345299, 29.6115608215, 6.3925046921, 28.7662715912, - 6.4386205673, 27.9006881714, 6.4835519791, 27.0147991180, - 6.5271716118, 26.1086864471, 6.5693559647, 25.1825313568, - 6.6099877357, 24.2366218567, 6.6489582062, 23.2713546753, - 6.6861701012, 22.2872333527, 6.7215371132, 21.2848682404, - 6.7549881935, 20.2649650574, 6.7864665985, 19.2283229828, - 6.8159332275, 18.1758213043, 6.8433632851, 17.1084079742, - 6.8687496185, 16.0270824432, 6.8920984268, 14.9328804016, - 6.9134311676, 13.8268623352, 6.9327797890, 12.7100963593, - 6.9501867294, 11.5836448669, 6.9657020569, 10.4485530853, - 6.9793796539, 9.3058395386, 6.9912767410, 8.1564893723, - 7.0014543533, 7.0014543533, 7.0099563599, 5.8416299820, - 7.0168380737, 4.6778917313, 7.0221428871, 3.5110714436, - 7.0259060860, 2.3419685364, 7.0281534195, 1.1713588238, - 7.0289006233, -0.0000000000, 7.0281534195, -1.1713588238, - 7.0259060860, -2.3419685364, 7.0221428871, -3.5110714436, - 7.0168380737, -4.6778917313, 7.0099563599, -5.8416299820, - 7.0014543533, -7.0014543533, 6.9912767410, -8.1564893723, - 6.9793796539, -9.3058395386, 6.9657020569, -10.4485530853, - 6.9501867294, -11.5836448669, 6.9327797890, -12.7100963593, - 6.9134311676, -13.8268623352, 6.8920984268, -14.9328804016, - 6.8687496185, -16.0270824432, 6.8433632851, -17.1084079742, - 6.8159332275, -18.1758213043, 6.7864665985, -19.2283229828, - 6.7549881935, -20.2649650574, 6.7215371132, -21.2848682404, - 6.6861701012, -22.2872333527, 6.6489582062, -23.2713546753, - 6.6099877357, -24.2366218567, 6.5693559647, -25.1825313568, - 6.5271716118, -26.1086864471, 6.4835519791, -27.0147991180, - 6.4386205673, -27.9006881714, 6.3925046921, -28.7662715912, - 6.3453345299, -29.6115608215, 6.2972393036, -30.4366550446, - 6.2483472824, -31.2417373657, 7.2773165703, 31.1884994507, - 7.3341183662, 30.3842048645, 7.3900084496, 29.5600337982, - 7.4448385239, 28.7158069611, 7.4984607697, 27.8514251709, - 7.5507259369, 26.9668769836, 7.6014866829, 26.0622406006, - 7.6506013870, 25.1376914978, 7.6979346275, 24.1935100555, - 7.7433609962, 23.2300834656, 7.7867655754, 22.2479019165, - 7.8280491829, 21.2475624084, 7.8671264648, 20.2297534943, - 7.9039311409, 19.1952610016, 7.9384136200, 18.1449451447, - 7.9705429077, 17.0797348022, 8.0003070831, 16.0006141663, - 8.0277090073, 14.9086036682, 8.0527706146, 13.8047494888, - 8.0755243301, 12.6901092529, 8.0960149765, 11.5657358170, - 8.1142959595, 10.4326667786, 8.1304273605, 9.2919178009, - 8.1444711685, 8.1444711685, 8.1564893723, 6.9912767410, - 8.1665477753, 5.8332486153, 8.1746883392, 4.6712508202, - 8.1809673309, 3.5061287880, 8.1854228973, 2.3386921883, - 8.1880846024, 1.1697263718, 8.1889696121, -0.0000000000, - 8.1880846024, -1.1697263718, 8.1854228973, -2.3386921883, - 8.1809673309, -3.5061287880, 8.1746883392, -4.6712508202, - 8.1665477753, -5.8332486153, 8.1564893723, -6.9912767410, - 8.1444711685, -8.1444711685, 8.1304273605, -9.2919178009, - 8.1142959595, -10.4326667786, 8.0960149765, -11.5657358170, - 8.0755243301, -12.6901092529, 8.0527706146, -13.8047494888, - 8.0277090073, -14.9086036682, 8.0003070831, -16.0006141663, - 7.9705429077, -17.0797348022, 7.9384136200, -18.1449451447, - 7.9039311409, -19.1952610016, 7.8671264648, -20.2297534943, - 7.8280491829, -21.2475624084, 7.7867655754, -22.2479019165, - 7.7433609962, -23.2300834656, 7.6979346275, -24.1935100555, - 7.6506013870, -25.1376914978, 7.6014866829, -26.0622406006, - 7.5507259369, -26.9668769836, 7.4984607697, -27.8514251709, - 7.4448385239, -28.7158069611, 7.3900084496, -29.5600337982, - 7.3341183662, -30.3842048645, 7.2773165703, -31.1884994507, - 8.3006286621, 31.1273555756, 8.3652276993, 30.3239517212, - 8.4288072586, 29.5008239746, 8.4912004471, 28.6578006744, - 8.5522403717, 27.7947807312, 8.6117601395, 26.9117507935, - 8.6695947647, 26.0087852478, 8.7255840302, 25.0860557556, - 8.7795763016, 24.1438331604, 8.8314266205, 23.1824951172, - 8.8810071945, 22.2025184631, 8.9282026291, 21.2044811249, - 8.9729146957, 20.1890583038, 9.0150651932, 19.1570129395, - 9.0545949936, 18.1091899872, 9.0914659500, 17.0464992523, - 9.1256580353, 15.9699020386, 9.1571722031, 14.8804044724, - 9.1860256195, 13.7790384293, 9.2122516632, 12.6668453217, - 9.2358942032, 11.5448684692, 9.2570114136, 10.4141378403, - 9.2756633759, 9.2756633759, 9.2919178009, 8.1304273605, - 9.3058395386, 6.9793796539, 9.3174943924, 5.8234338760, - 9.3269481659, 4.6634740829, 9.3342361450, 3.5003383160, - 9.3394098282, 2.3348524570, 9.3425016403, 1.1678127050, - 9.3435297012, -0.0000000000, 9.3425016403, -1.1678127050, - 9.3394098282, -2.3348524570, 9.3342361450, -3.5003383160, - 9.3269481659, -4.6634740829, 9.3174943924, -5.8234338760, - 9.3058395386, -6.9793796539, 9.2919178009, -8.1304273605, - 9.2756633759, -9.2756633759, 9.2570114136, -10.4141378403, - 9.2358942032, -11.5448684692, 9.2122516632, -12.6668453217, - 9.1860256195, -13.7790384293, 9.1571722031, -14.8804044724, - 9.1256580353, -15.9699020386, 9.0914659500, -17.0464992523, - 9.0545949936, -18.1091899872, 9.0150651932, -19.1570129395, - 8.9729146957, -20.1890583038, 8.9282026291, -21.2044811249, - 8.8810071945, -22.2025184631, 8.8314266205, -23.1824951172, - 8.7795763016, -24.1438331604, 8.7255840302, -25.0860557556, - 8.6695947647, -26.0087852478, 8.6117601395, -26.9117507935, - 8.5522403717, -27.7947807312, 8.4912004471, -28.6578006744, - 8.4288072586, -29.5008239746, 8.3652276993, -30.3239517212, - 8.3006286621, -31.1273555756, 9.3175296783, 31.0584316254, - 9.3897972107, 30.2560138702, 9.4609432220, 29.4340457916, - 9.5307846069, 28.5923538208, 9.5991382599, 27.7308425903, - 9.6658191681, 26.8494987488, 9.7306461334, 25.9483890533, - 9.7934408188, 25.0276813507, 9.8540334702, 24.0876388550, - 9.9122667313, 23.1286220551, 9.9679937363, 22.1510982513, - 10.0210866928, 21.1556262970, 10.0714330673, 20.1428661346, - 10.1189422607, 19.1135578156, 10.1635446548, 18.0685253143, - 10.2051935196, 17.0086555481, 10.2438602448, 15.9348936081, - 10.2795400620, 14.8482246399, 10.3122472763, 13.7496623993, - 10.3420104980, 12.6402349472, 10.3688755035, 11.5209722519, - 10.3928966522, 10.3928966522, 10.4141378403, 9.2570114136, - 10.4326667786, 8.1142959595, 10.4485530853, 6.9657020569, - 10.4618625641, 5.8121461868, 10.4726581573, 4.6545147896, - 10.4809942245, 3.4936647415, 10.4869146347, 2.3304255009, - 10.4904632568, 1.1656070948, 10.4916400909, -0.0000000000, - 10.4904632568, -1.1656070948, 10.4869146347, -2.3304255009, - 10.4809942245, -3.4936647415, 10.4726581573, -4.6545147896, - 10.4618625641, -5.8121461868, 10.4485530853, -6.9657020569, - 10.4326667786, -8.1142959595, 10.4141378403, -9.2570114136, - 10.3928966522, -10.3928966522, 10.3688755035, -11.5209722519, - 10.3420104980, -12.6402349472, 10.3122472763, -13.7496623993, - 10.2795400620, -14.8482246399, 10.2438602448, -15.9348936081, - 10.2051935196, -17.0086555481, 10.1635446548, -18.0685253143, - 10.1189422607, -19.1135578156, 10.0714330673, -20.1428661346, - 10.0210866928, -21.1556262970, 9.9679937363, -22.1510982513, - 9.9122667313, -23.1286220551, 9.8540334702, -24.0876388550, - 9.7934408188, -25.0276813507, 9.7306461334, -25.9483890533, - 9.6658191681, -26.8494987488, 9.5991382599, -27.7308425903, - 9.5307846069, -28.5923538208, 9.4609432220, -29.4340457916, - 9.3897972107, -30.2560138702, 9.3175296783, -31.0584316254, - 10.3272886276, 30.9818668365, 10.4070777893, 30.1805267334, - 10.4856500626, 29.3598213196, 10.5628099442, 28.5195865631, - 10.6383552551, 27.6597232819, 10.7120866776, 26.7802181244, - 10.7838068008, 25.8811359406, 10.8533210754, 24.9626388550, - 10.9204444885, 24.0249786377, 10.9850025177, 23.0685062408, - 11.0468349457, 22.0936698914, 11.1057968140, 21.1010150909, - 11.1617650986, 20.0911769867, 11.2146348953, 19.0648784637, - 11.2643251419, 18.0229206085, 11.3107776642, 16.9661674500, - 11.3539581299, 15.8955411911, 11.3938522339, 14.8120079041, - 11.4304676056, 13.7165613174, 11.4638299942, 12.6102132797, - 11.4939804077, 11.4939804077, 11.5209722519, 10.3688755035, - 11.5448684692, 9.2358942032, 11.5657358170, 8.0960149765, - 11.5836448669, 6.9501867294, 11.5986623764, 5.7993311882, - 11.6108531952, 4.6443414688, 11.6202726364, 3.4860816002, - 11.6269655228, 2.3253931999, 11.6309680939, 1.1630967855, - 11.6322994232, -0.0000000000, 11.6309680939, -1.1630967855, - 11.6269655228, -2.3253931999, 11.6202726364, -3.4860816002, - 11.6108531952, -4.6443414688, 11.5986623764, -5.7993311882, - 11.5836448669, -6.9501867294, 11.5657358170, -8.0960149765, - 11.5448684692, -9.2358942032, 11.5209722519, -10.3688755035, - 11.4939804077, -11.4939804077, 11.4638299942, -12.6102132797, - 11.4304676056, -13.7165613174, 11.3938522339, -14.8120079041, - 11.3539581299, -15.8955411911, 11.3107776642, -16.9661674500, - 11.2643251419, -18.0229206085, 11.2146348953, -19.0648784637, - 11.1617650986, -20.0911769867, 11.1057968140, -21.1010150909, - 11.0468349457, -22.0936698914, 10.9850025177, -23.0685062408, - 10.9204444885, -24.0249786377, 10.8533210754, -24.9626388550, - 10.7838068008, -25.8811359406, 10.7120866776, -26.7802181244, - 10.6383552551, -27.6597232819, 10.5628099442, -28.5195865631, - 10.4856500626, -29.3598213196, 10.4070777893, -30.1805267334, - 10.3272886276, -30.9818668365, 11.3292007446, 30.8978214264, - 11.4163465500, 30.0976409912, 11.5021886826, 29.2782974243, - 11.5865163803, 28.4396305084, 11.6691150665, 27.5815448761, - 11.7497692108, 26.7040214539, 11.8282670975, 25.8071269989, - 11.9043979645, 24.8910140991, 11.9779634476, 23.9559268951, - 12.0487728119, 23.0022029877, 12.1166515350, 22.0302753448, - 12.1814413071, 21.0406703949, 12.2430028915, 20.0340042114, - 12.3012199402, 19.0109767914, 12.3559989929, 17.9723625183, - 12.4072723389, 16.9190063477, 12.4549913406, 15.8518075943, - 12.4991369247, 14.7717065811, 12.5397062302, 13.6796798706, - 12.5767202377, 12.5767202377, 12.6102132797, 11.4638299942, - 12.6402349472, 10.3420104980, 12.6668453217, 9.2122516632, - 12.6901092529, 8.0755243301, 12.7100963593, 6.9327797890, - 12.7268714905, 5.7849416733, 12.7405004501, 4.6329092979, - 12.7510375977, 3.4775555134, 12.7585287094, 2.3197324276, - 12.7630100250, 1.1602735519, 12.7645006180, -0.0000000000, - 12.7630100250, -1.1602735519, 12.7585287094, -2.3197324276, - 12.7510375977, -3.4775555134, 12.7405004501, -4.6329092979, - 12.7268714905, -5.7849416733, 12.7100963593, -6.9327797890, - 12.6901092529, -8.0755243301, 12.6668453217, -9.2122516632, - 12.6402349472, -10.3420104980, 12.6102132797, -11.4638299942, - 12.5767202377, -12.5767202377, 12.5397062302, -13.6796798706, - 12.4991369247, -14.7717065811, 12.4549913406, -15.8518075943, - 12.4072723389, -16.9190063477, 12.3559989929, -17.9723625183, - 12.3012199402, -19.0109767914, 12.2430028915, -20.0340042114, - 12.1814413071, -21.0406703949, 12.1166515350, -22.0302753448, - 12.0487728119, -23.0022029877, 11.9779634476, -23.9559268951, - 11.9043979645, -24.8910140991, 11.8282670975, -25.8071269989, - 11.7497692108, -26.7040214539, 11.6691150665, -27.5815448761, - 11.5865163803, -28.4396305084, 11.5021886826, -29.2782974243, - 11.4163465500, -30.0976409912, 11.3292007446, -30.8978214264, - 12.3225889206, 30.8064727783, 12.4169082642, 30.0075283051, - 12.5098447800, 29.1896381378, 12.6011743546, 28.3526420593, - 12.6906690598, 27.4964504242, 12.7781000137, 26.6210422516, - 12.8632402420, 25.7264804840, 12.9458684921, 24.8129138947, - 13.0257682800, 23.8805751801, 13.1027374268, 22.9297904968, - 13.1765851974, 21.9609756470, 13.2471399307, 20.9746379852, - 13.3142499924, 19.9713764191, 13.3777856827, 18.9518623352, - 13.4376392365, 17.9168510437, 13.4937295914, 16.8671627045, - 13.5460004807, 15.8036670685, 13.5944185257, 14.7272872925, - 13.6389751434, 13.6389751434, 13.6796798706, 12.5397062302, - 13.7165613174, 11.4304676056, 13.7496623993, 10.3122472763, - 13.7790384293, 9.1860256195, 13.8047494888, 8.0527706146, - 13.8268623352, 6.9134311676, 13.8454399109, 5.7689332962, - 13.8605442047, 4.6201815605, 13.8722305298, 3.4680576324, - 13.8805427551, 2.3134238720, 13.8855171204, 1.1571264267, - 13.8871726990, -0.0000000000, 13.8855171204, -1.1571264267, - 13.8805427551, -2.3134238720, 13.8722305298, -3.4680576324, - 13.8605442047, -4.6201815605, 13.8454399109, -5.7689332962, - 13.8268623352, -6.9134311676, 13.8047494888, -8.0527706146, - 13.7790384293, -9.1860256195, 13.7496623993, -10.3122472763, - 13.7165613174, -11.4304676056, 13.6796798706, -12.5397062302, - 13.6389751434, -13.6389751434, 13.5944185257, -14.7272872925, - 13.5460004807, -15.8036670685, 13.4937295914, -16.8671627045, - 13.4376392365, -17.9168510437, 13.3777856827, -18.9518623352, - 13.3142499924, -19.9713764191, 13.2471399307, -20.9746379852, - 13.1765851974, -21.9609756470, 13.1027374268, -22.9297904968, - 13.0257682800, -23.8805751801, 12.9458684921, -24.8129138947, - 12.8632402420, -25.7264804840, 12.7781000137, -26.6210422516, - 12.6906690598, -27.4964504242, 12.6011743546, -28.3526420593, - 12.5098447800, -29.1896381378, 12.4169082642, -30.0075283051, - 12.3225889206, -30.8064727783, 13.3068008423, 30.7080020905, - 13.4081001282, 29.9103755951, 13.5079383850, 29.0940208435, - 13.6060838699, 28.2587909698, 13.7022991180, 27.4045982361, - 13.7963418961, 26.5314273834, 13.8879728317, 25.6393356323, - 13.9769563675, 24.7284622192, 14.0630645752, 23.7990322113, - 14.1460809708, 22.8513603210, 14.2258014679, 21.8858470917, - 14.3020410538, 20.9029827118, 14.3746337891, 19.9033393860, - 14.4434366226, 18.8875694275, 14.5083284378, 17.8564052582, - 14.5692167282, 16.8106346130, 14.6260318756, 15.7511110306, - 14.6787290573, 14.6787290573, 14.7272872925, 13.5944185257, - 14.7717065811, 12.4991369247, 14.8120079041, 11.3938522339, - 14.8482246399, 10.2795400620, 14.8804044724, 9.1571722031, - 14.9086036682, 8.0277090073, 14.9328804016, 6.8920984268, - 14.9532957077, 5.7512674332, 14.9699077606, 4.6061258316, - 14.9827699661, 3.4575622082, 14.9919233322, 2.3064496517, - 14.9974021912, 1.1536463499, 14.9992265701, -0.0000000000, - 14.9974021912, -1.1536463499, 14.9919233322, -2.3064496517, - 14.9827699661, -3.4575622082, 14.9699077606, -4.6061258316, - 14.9532957077, -5.7512674332, 14.9328804016, -6.8920984268, - 14.9086036682, -8.0277090073, 14.8804044724, -9.1571722031, - 14.8482246399, -10.2795400620, 14.8120079041, -11.3938522339, - 14.7717065811, -12.4991369247, 14.7272872925, -13.5944185257, - 14.6787290573, -14.6787290573, 14.6260318756, -15.7511110306, - 14.5692167282, -16.8106346130, 14.5083284378, -17.8564052582, - 14.4434366226, -18.8875694275, 14.3746337891, -19.9033393860, - 14.3020410538, -20.9029827118, 14.2258014679, -21.8858470917, - 14.1460809708, -22.8513603210, 14.0630645752, -23.7990322113, - 13.9769563675, -24.7284622192, 13.8879728317, -25.6393356323, - 13.7963418961, -26.5314273834, 13.7022991180, -27.4045982361, - 13.6060838699, -28.2587909698, 13.5079383850, -29.0940208435, - 13.4081001282, -29.9103755951, 13.3068008423, -30.7080020905, - 14.2812309265, 30.6026382446, 14.3892917633, 29.8063888550, - 14.4958209991, 28.9916419983, 14.6005821228, 28.1582660675, - 14.7033243179, 27.3061733246, 14.8037958145, 26.4353485107, - 14.9017429352, 25.5458450317, 14.9969215393, 24.6378002167, - 15.0890903473, 23.7114276886, 15.1780204773, 22.7670307159, - 15.2634954453, 21.8049926758, 15.3453159332, 20.8257865906, - 15.4233045578, 19.8299636841, 15.4973020554, 18.8181533813, - 15.5671777725, 17.7910594940, 15.6328210831, 16.7494506836, - 15.6941518784, 15.6941518784, 15.7511110306, 14.6260318756, - 15.8036670685, 13.5460004807, 15.8518075943, 12.4549913406, - 15.8955411911, 11.3539581299, 15.9348936081, 10.2438602448, - 15.9699020386, 9.1256580353, 16.0006141663, 8.0003070831, - 16.0270824432, 6.8687496185, 16.0493621826, 5.7319149971, - 16.0675067902, 4.5907158852, 16.0815639496, 3.4460492134, - 16.0915718079, 2.2987961769, 16.0975666046, 1.1498261690, - 16.0995616913, -0.0000000000, 16.0975666046, -1.1498261690, - 16.0915718079, -2.2987961769, 16.0815639496, -3.4460492134, - 16.0675067902, -4.5907158852, 16.0493621826, -5.7319149971, - 16.0270824432, -6.8687496185, 16.0006141663, -8.0003070831, - 15.9699020386, -9.1256580353, 15.9348936081, -10.2438602448, - 15.8955411911, -11.3539581299, 15.8518075943, -12.4549913406, - 15.8036670685, -13.5460004807, 15.7511110306, -14.6260318756, - 15.6941518784, -15.6941518784, 15.6328210831, -16.7494506836, - 15.5671777725, -17.7910594940, 15.4973020554, -18.8181533813, - 15.4233045578, -19.8299636841, 15.3453159332, -20.8257865906, - 15.2634954453, -21.8049926758, 15.1780204773, -22.7670307159, - 15.0890903473, -23.7114276886, 14.9969215393, -24.6378002167, - 14.9017429352, -25.5458450317, 14.8037958145, -26.4353485107, - 14.7033243179, -27.3061733246, 14.6005821228, -28.1582660675, - 14.4958209991, -28.9916419983, 14.3892917633, -29.8063888550, - 14.2812309265, -30.6026382446, 15.2453002930, 30.4906005859, - 15.3598909378, 29.6957893372, 15.4728860855, 28.8827209473, - 15.5840415955, 28.0512752533, 15.6930990219, 27.2013721466, - 15.7997951508, 26.3329906464, 15.9038677216, 25.4461898804, - 16.0050601959, 24.5410938263, 16.1031227112, 23.6179122925, - 16.1978111267, 22.6769371033, 16.2889003754, 21.7185325623, - 16.3761768341, 20.7431564331, 16.4594497681, 19.7513389587, - 16.5385475159, 18.7436866760, 16.6133232117, 17.7208786011, - 16.6836566925, 16.6836566925, 16.7494506836, 15.6328210831, - 16.8106346130, 14.5692167282, 16.8671627045, 13.4937295914, - 16.9190063477, 12.4072723389, 16.9661674500, 11.3107776642, - 17.0086555481, 10.2051935196, 17.0464992523, 9.0914659500, - 17.0797348022, 7.9705429077, 17.1084079742, 6.8433632851, - 17.1325664520, 5.7108559608, 17.1522560120, 4.5739350319, - 17.1675205231, 3.4335041046, 17.1783962250, 2.2904527187, - 17.1849098206, 1.1456606388, 17.1870784760, -0.0000000000, - 17.1849098206, -1.1456606388, 17.1783962250, -2.2904527187, - 17.1675205231, -3.4335041046, 17.1522560120, -4.5739350319, - 17.1325664520, -5.7108559608, 17.1084079742, -6.8433632851, - 17.0797348022, -7.9705429077, 17.0464992523, -9.0914659500, - 17.0086555481, -10.2051935196, 16.9661674500, -11.3107776642, - 16.9190063477, -12.4072723389, 16.8671627045, -13.4937295914, - 16.8106346130, -14.5692167282, 16.7494506836, -15.6328210831, - 16.6836566925, -16.6836566925, 16.6133232117, -17.7208786011, - 16.5385475159, -18.7436866760, 16.4594497681, -19.7513389587, - 16.3761768341, -20.7431564331, 16.2889003754, -21.7185325623, - 16.1978111267, -22.6769371033, 16.1031227112, -23.6179122925, - 16.0050601959, -24.5410938263, 15.9038677216, -25.4461898804, - 15.7997951508, -26.3329906464, 15.6930990219, -27.2013721466, - 15.5840415955, -28.0512752533, 15.4728860855, -28.8827209473, - 15.3598909378, -29.6957893372, 15.2453002930, -30.4906005859, - 16.1984691620, 30.3721294403, 16.3193359375, 29.5787963867, - 16.4385623932, 28.7674865723, 16.5558776855, 27.9380435944, - 16.6710205078, 27.0904083252, 16.7837219238, 26.2245635986, - 16.8937091827, 25.3405628204, 17.0007152557, 24.4385280609, - 17.1044807434, 23.5186595917, 17.2047519684, 22.5812358856, - 17.3012905121, 21.6266136169, 17.3938732147, 20.6552238464, - 17.4822959900, 19.6675815582, 17.5663719177, 18.6642704010, - 17.6459465027, 17.6459465027, 17.7208786011, 16.6133232117, - 17.7910594940, 15.5671777725, 17.8564052582, 14.5083284378, - 17.9168510437, 13.4376392365, 17.9723625183, 12.3559989929, - 18.0229206085, 11.2643251419, 18.0685253143, 10.1635446548, - 18.1091899872, 9.0545949936, 18.1449451447, 7.9384136200, - 18.1758213043, 6.8159332275, 18.2018604279, 5.6880812645, - 18.2230987549, 4.5557746887, 18.2395725250, 3.4199199677, - 18.2513160706, 2.2814145088, 18.2583522797, 1.1411470175, - 18.2606964111, -0.0000000000, 18.2583522797, -1.1411470175, - 18.2513160706, -2.2814145088, 18.2395725250, -3.4199199677, - 18.2230987549, -4.5557746887, 18.2018604279, -5.6880812645, - 18.1758213043, -6.8159332275, 18.1449451447, -7.9384136200, - 18.1091899872, -9.0545949936, 18.0685253143, -10.1635446548, - 18.0229206085, -11.2643251419, 17.9723625183, -12.3559989929, - 17.9168510437, -13.4376392365, 17.8564052582, -14.5083284378, - 17.7910594940, -15.5671777725, 17.7208786011, -16.6133232117, - 17.6459465027, -17.6459465027, 17.5663719177, -18.6642704010, - 17.4822959900, -19.6675815582, 17.3938732147, -20.6552238464, - 17.3012905121, -21.6266136169, 17.2047519684, -22.5812358856, - 17.1044807434, -23.5186595917, 17.0007152557, -24.4385280609, - 16.8937091827, -25.3405628204, 16.7837219238, -26.2245635986, - 16.6710205078, -27.0904083252, 16.5558776855, -27.9380435944, - 16.4385623932, -28.7674865723, 16.3193359375, -29.5787963867, - 16.1984691620, -30.3721294403, 17.1402397156, 30.2474803925, - 17.2671279907, 29.4556884766, 17.3923187256, 28.6461734772, - 17.5155487061, 27.8188114166, 17.6365318298, 26.9735183716, - 17.7549991608, 26.1102924347, 17.8706703186, 25.2291812897, - 17.9832706451, 24.3303070068, 18.0925292969, 23.4138622284, - 18.1981868744, 22.4801120758, 18.2999897003, 21.5293998718, - 18.3977069855, 20.5621433258, 18.4911193848, 19.5788326263, - 18.5800323486, 18.5800323486, 18.6642704010, 17.5663719177, - 18.7436866760, 16.5385475159, 18.8181533813, 15.4973020554, - 18.8875694275, 14.4434366226, 18.9518623352, 13.3777856827, - 19.0109767914, 12.3012199402, 19.0648784637, 11.2146348953, - 19.1135578156, 10.1189422607, 19.1570129395, 9.0150651932, - 19.1952610016, 7.9039311409, 19.2283229828, 6.7864665985, - 19.2562274933, 5.6635961533, 19.2790031433, 4.5362362862, - 19.2966842651, 3.4052970409, 19.3092918396, 2.2716813087, - 19.3168468475, 1.1362851858, 19.3193645477, -0.0000000000, - 19.3168468475, -1.1362851858, 19.3092918396, -2.2716813087, - 19.2966842651, -3.4052970409, 19.2790031433, -4.5362362862, - 19.2562274933, -5.6635961533, 19.2283229828, -6.7864665985, - 19.1952610016, -7.9039311409, 19.1570129395, -9.0150651932, - 19.1135578156, -10.1189422607, 19.0648784637, -11.2146348953, - 19.0109767914, -12.3012199402, 18.9518623352, -13.3777856827, - 18.8875694275, -14.4434366226, 18.8181533813, -15.4973020554, - 18.7436866760, -16.5385475159, 18.6642704010, -17.5663719177, - 18.5800323486, -18.5800323486, 18.4911193848, -19.5788326263, - 18.3977069855, -20.5621433258, 18.2999897003, -21.5293998718, - 18.1981868744, -22.4801120758, 18.0925292969, -23.4138622284, - 17.9832706451, -24.3303070068, 17.8706703186, -25.2291812897, - 17.7549991608, -26.1102924347, 17.6365318298, -26.9735183716, - 17.5155487061, -27.8188114166, 17.3923187256, -28.6461734772, - 17.2671279907, -29.4556884766, 17.1402397156, -30.2474803925, - 18.0701522827, 30.1169204712, 18.2027912140, 29.3267192841, - 18.3336811066, 28.5190601349, 18.4625568390, 27.6938343048, - 18.5891189575, 26.8509502411, 18.7131004333, 25.9904155731, - 18.8342094421, 25.1122798920, 18.9521656036, 24.2166576385, - 19.0666923523, 23.3037357330, 19.1775150299, 22.3737659454, - 19.2843761444, 21.4270839691, 19.3870315552, 20.4640884399, - 19.4852523804, 19.4852523804, 19.5788326263, 18.4911193848, - 19.6675815582, 17.4822959900, 19.7513389587, 16.4594497681, - 19.8299636841, 15.4233045578, 19.9033393860, 14.3746337891, - 19.9713764191, 13.3142499924, 20.0340042114, 12.2430028915, - 20.0911769867, 11.1617650986, 20.1428661346, 10.0714330673, - 20.1890583038, 8.9729146957, 20.2297534943, 7.8671264648, - 20.2649650574, 6.7549881935, 20.2947063446, 5.6374182701, - 20.3190002441, 4.5153336525, 20.3378696442, 3.3896448612, - 20.3513298035, 2.2612588406, 20.3593997955, 1.1310777664, - 20.3620891571, -0.0000000000, 20.3593997955, -1.1310777664, - 20.3513298035, -2.2612588406, 20.3378696442, -3.3896448612, - 20.3190002441, -4.5153336525, 20.2947063446, -5.6374182701, - 20.2649650574, -6.7549881935, 20.2297534943, -7.8671264648, - 20.1890583038, -8.9729146957, 20.1428661346, -10.0714330673, - 20.0911769867, -11.1617650986, 20.0340042114, -12.2430028915, - 19.9713764191, -13.3142499924, 19.9033393860, -14.3746337891, - 19.8299636841, -15.4233045578, 19.7513389587, -16.4594497681, - 19.6675815582, -17.4822959900, 19.5788326263, -18.4911193848, - 19.4852523804, -19.4852523804, 19.3870315552, -20.4640884399, - 19.2843761444, -21.4270839691, 19.1775150299, -22.3737659454, - 19.0666923523, -23.3037357330, 18.9521656036, -24.2166576385, - 18.8342094421, -25.1122798920, 18.7131004333, -25.9904155731, - 18.5891189575, -26.8509502411, 18.4625568390, -27.6938343048, - 18.3336811066, -28.5190601349, 18.2027912140, -29.3267192841, - 18.0701522827, -30.1169204712, 18.9877948761, 29.9807300568, - 19.1259002686, 29.1921634674, 19.2622089386, 28.3864135742, - 19.3964443207, 27.5633678436, 19.5283241272, 26.7229709625, - 19.6575489044, 25.8651943207, 19.7838344574, 24.9901065826, - 19.9068927765, 24.0978183746, 20.0264377594, 23.1885070801, - 20.1421909332, 22.2624206543, 20.2538833618, 21.3198776245, - 20.3612632751, 20.3612632751, 20.4640884399, 19.3870315552, - 20.5621433258, 18.3977069855, 20.6552238464, 17.3938732147, - 20.7431564331, 16.3761768341, 20.8257865906, 15.3453159332, - 20.9029827118, 14.3020410538, 20.9746379852, 13.2471399307, - 21.0406703949, 12.1814413071, 21.1010150909, 11.1057968140, - 21.1556262970, 10.0210866928, 21.2044811249, 8.9282026291, - 21.2475624084, 7.8280491829, 21.2848682404, 6.7215371132, - 21.3164043427, 5.6095800400, 21.3421802521, 4.4930906296, - 21.3622112274, 3.3729808331, 21.3765087128, 2.2501587868, - 21.3850822449, 1.1255306005, 21.3879394531, -0.0000000000, - 21.3850822449, -1.1255306005, 21.3765087128, -2.2501587868, - 21.3622112274, -3.3729808331, 21.3421802521, -4.4930906296, - 21.3164043427, -5.6095800400, 21.2848682404, -6.7215371132, - 21.2475624084, -7.8280491829, 21.2044811249, -8.9282026291, - 21.1556262970, -10.0210866928, 21.1010150909, -11.1057968140, - 21.0406703949, -12.1814413071, 20.9746379852, -13.2471399307, - 20.9029827118, -14.3020410538, 20.8257865906, -15.3453159332, - 20.7431564331, -16.3761768341, 20.6552238464, -17.3938732147, - 20.5621433258, -18.3977069855, 20.4640884399, -19.3870315552, - 20.3612632751, -20.3612632751, 20.2538833618, -21.3198776245, - 20.1421909332, -22.2624206543, 20.0264377594, -23.1885070801, - 19.9068927765, -24.0978183746, 19.7838344574, -24.9901065826, - 19.6575489044, -25.8651943207, 19.5283241272, -26.7229709625, - 19.3964443207, -27.5633678436, 19.2622089386, -28.3864135742, - 19.1259002686, -29.1921634674, 18.9877948761, -29.9807300568, - 19.8927974701, 29.8391952515, 20.0360755920, 29.0523090363, - 20.1775131226, 28.2485179901, 20.3168258667, 27.4277153015, - 20.4537258148, 26.5898437500, 20.5879211426, 25.7349014282, - 20.7191085815, 24.8629302979, 20.8469982147, 23.9740486145, - 20.9713001251, 23.0684318542, 21.0917301178, 22.1463165283, - 21.2080097198, 21.2080097198, 21.3198776245, 20.2538833618, - 21.4270839691, 19.2843761444, 21.5293998718, 18.2999897003, - 21.6266136169, 17.3012905121, 21.7185325623, 16.2889003754, - 21.8049926758, 15.2634954453, 21.8858470917, 14.2258014679, - 21.9609756470, 13.1765851974, 22.0302753448, 12.1166515350, - 22.0936698914, 11.0468349457, 22.1510982513, 9.9679937363, - 22.2025184631, 8.8810071945, 22.2479019165, 7.7867655754, - 22.2872333527, 6.6861701012, 22.3205051422, 5.5801262856, - 22.3477191925, 4.4695439339, 22.3688755035, 3.3553314209, - 22.3839836121, 2.2383983135, 22.3930454254, 1.1196522713, - 22.3960647583, -0.0000000000, 22.3930454254, -1.1196522713, - 22.3839836121, -2.2383983135, 22.3688755035, -3.3553314209, - 22.3477191925, -4.4695439339, 22.3205051422, -5.5801262856, - 22.2872333527, -6.6861701012, 22.2479019165, -7.7867655754, - 22.2025184631, -8.8810071945, 22.1510982513, -9.9679937363, - 22.0936698914, -11.0468349457, 22.0302753448, -12.1166515350, - 21.9609756470, -13.1765851974, 21.8858470917, -14.2258014679, - 21.8049926758, -15.2634954453, 21.7185325623, -16.2889003754, - 21.6266136169, -17.3012905121, 21.5293998718, -18.2999897003, - 21.4270839691, -19.2843761444, 21.3198776245, -20.2538833618, - 21.2080097198, -21.2080097198, 21.0917301178, -22.1463165283, - 20.9713001251, -23.0684318542, 20.8469982147, -23.9740486145, - 20.7191085815, -24.8629302979, 20.5879211426, -25.7349014282, - 20.4537258148, -26.5898437500, 20.3168258667, -27.4277153015, - 20.1775131226, -28.2485179901, 20.0360755920, -29.0523090363, - 19.8927974701, -29.8391952515, 20.7848300934, 29.6926136017, - 20.9329833984, 28.9074535370, 21.0792503357, 28.1056671143, - 21.2233448029, 27.2871570587, 21.3649730682, 26.4518718719, - 21.5038471222, 25.5998191833, 21.6396484375, 24.7310256958, - 21.7720890045, 23.8456211090, 21.9008731842, 22.9437732697, - 22.0257110596, 22.0257110596, 22.1463165283, 21.0917301178, - 22.2624206543, 20.1421909332, 22.3737659454, 19.1775150299, - 22.4801120758, 18.1981868744, 22.5812358856, 17.2047519684, - 22.6769371033, 16.1978111267, 22.7670307159, 15.1780204773, - 22.8513603210, 14.1460809708, 22.9297904968, 13.1027374268, - 23.0022029877, 12.0487728119, 23.0685062408, 10.9850025177, - 23.1286220551, 9.9122667313, 23.1824951172, 8.8314266205, - 23.2300834656, 7.7433609962, 23.2713546753, 6.6489582062, - 23.3062915802, 5.5491170883, 23.3348808289, 4.4447393417, - 23.3571205139, 3.3367314339, 23.3730049133, 2.2260005474, - 23.3825359344, 1.1134541035, 23.3857116699, -0.0000000000, - 23.3825359344, -1.1134541035, 23.3730049133, -2.2260005474, - 23.3571205139, -3.3367314339, 23.3348808289, -4.4447393417, - 23.3062915802, -5.5491170883, 23.2713546753, -6.6489582062, - 23.2300834656, -7.7433609962, 23.1824951172, -8.8314266205, - 23.1286220551, -9.9122667313, 23.0685062408, -10.9850025177, - 23.0022029877, -12.0487728119, 22.9297904968, -13.1027374268, - 22.8513603210, -14.1460809708, 22.7670307159, -15.1780204773, - 22.6769371033, -16.1978111267, 22.5812358856, -17.2047519684, - 22.4801120758, -18.1981868744, 22.3737659454, -19.1775150299, - 22.2624206543, -20.1421909332, 22.1463165283, -21.0917301178, - 22.0257110596, -22.0257110596, 21.9008731842, -22.9437732697, - 21.7720890045, -23.8456211090, 21.6396484375, -24.7310256958, - 21.5038471222, -25.5998191833, 21.3649730682, -26.4518718719, - 21.2233448029, -27.2871570587, 21.0792503357, -28.1056671143, - 20.9329833984, -28.9074535370, 20.7848300934, -29.6926136017, - 21.6636123657, 29.5412883759, 21.8163356781, 28.7578964233, - 21.9671268463, 27.9581623077, 22.1156997681, 27.1419944763, - 22.2617568970, 26.3093490601, 22.4049987793, 25.4602241516, - 22.5451297760, 24.5946865082, 22.6818294525, 23.7128200531, - 22.8148078918, 22.8148078918, 22.9437732697, 21.9008731842, - 23.0684318542, 20.9713001251, 23.1885070801, 20.0264377594, - 23.3037357330, 19.0666923523, 23.4138622284, 18.0925292969, - 23.5186595917, 17.1044807434, 23.6179122925, 16.1031227112, - 23.7114276886, 15.0890903473, 23.7990322113, 14.0630645752, - 23.8805751801, 13.0257682800, 23.9559268951, 11.9779634476, - 24.0249786377, 10.9204444885, 24.0876388550, 9.8540334702, - 24.1438331604, 8.7795763016, 24.1935100555, 7.6979346275, - 24.2366218567, 6.6099877357, 24.2731380463, 5.5166220665, - 24.3030376434, 4.4187340736, 24.3263034821, 3.3172230721, - 24.3429279327, 2.2129933834, 24.3529033661, 1.1069501638, - 24.3562297821, -0.0000000000, 24.3529033661, -1.1069501638, - 24.3429279327, -2.2129933834, 24.3263034821, -3.3172230721, - 24.3030376434, -4.4187340736, 24.2731380463, -5.5166220665, - 24.2366218567, -6.6099877357, 24.1935100555, -7.6979346275, - 24.1438331604, -8.7795763016, 24.0876388550, -9.8540334702, - 24.0249786377, -10.9204444885, 23.9559268951, -11.9779634476, - 23.8805751801, -13.0257682800, 23.7990322113, -14.0630645752, - 23.7114276886, -15.0890903473, 23.6179122925, -16.1031227112, - 23.5186595917, -17.1044807434, 23.4138622284, -18.0925292969, - 23.3037357330, -19.0666923523, 23.1885070801, -20.0264377594, - 23.0684318542, -20.9713001251, 22.9437732697, -21.9008731842, - 22.8148078918, -22.8148078918, 22.6818294525, -23.7128200531, - 22.5451297760, -24.5946865082, 22.4049987793, -25.4602241516, - 22.2617568970, -26.3093490601, 22.1156997681, -27.1419944763, - 21.9671268463, -27.9581623077, 21.8163356781, -28.7578964233, - 21.6636123657, -29.5412883759, 22.5289039612, 29.3855266571, - 22.6858882904, 28.6039466858, 22.8408966064, 27.8063087463, - 22.9936389923, 26.9925327301, 23.1438159943, 26.1625747681, - 23.2911281586, 25.3164443970, 23.4352703094, 24.4541950226, - 23.5759410858, 23.5759410858, 23.7128200531, 22.6818294525, - 23.8456211090, 21.7720890045, 23.9740486145, 20.8469982147, - 24.0978183746, 19.9068927765, 24.2166576385, 18.9521656036, - 24.3303070068, 17.9832706451, 24.4385280609, 17.0007152557, - 24.5410938263, 16.0050601959, 24.6378002167, 14.9969215393, - 24.7284622192, 13.9769563675, 24.8129138947, 12.9458684921, - 24.8910140991, 11.9043979645, 24.9626388550, 10.8533210754, - 25.0276813507, 9.7934408188, 25.0860557556, 8.7255840302, - 25.1376914978, 7.6506013870, 25.1825313568, 6.5693559647, - 25.2205314636, 5.4827241898, 25.2516613007, 4.3915929794, - 25.2758941650, 3.2968556881, 25.2932147980, 2.1994099617, - 25.3036098480, 1.1001570225, 25.3070774078, -0.0000000000, - 25.3036098480, -1.1001570225, 25.2932147980, -2.1994099617, - 25.2758941650, -3.2968556881, 25.2516613007, -4.3915929794, - 25.2205314636, -5.4827241898, 25.1825313568, -6.5693559647, - 25.1376914978, -7.6506013870, 25.0860557556, -8.7255840302, - 25.0276813507, -9.7934408188, 24.9626388550, -10.8533210754, - 24.8910140991, -11.9043979645, 24.8129138947, -12.9458684921, - 24.7284622192, -13.9769563675, 24.6378002167, -14.9969215393, - 24.5410938263, -16.0050601959, 24.4385280609, -17.0007152557, - 24.3303070068, -17.9832706451, 24.2166576385, -18.9521656036, - 24.0978183746, -19.9068927765, 23.9740486145, -20.8469982147, - 23.8456211090, -21.7720890045, 23.7128200531, -22.6818294525, - 23.5759410858, -23.5759410858, 23.4352703094, -24.4541950226, - 23.2911281586, -25.3164443970, 23.1438159943, -26.1625747681, - 22.9936389923, -26.9925327301, 22.8408966064, -27.8063087463, - 22.6858882904, -28.6039466858, 22.5289039612, -29.3855266571, - 23.3805084229, 29.2256336212, 23.5414447784, 28.4459114075, - 23.7003574371, 27.6504173279, 23.8569583893, 26.8390789032, - 24.0109481812, 26.0118598938, 24.1620216370, 25.1687717438, - 24.3098735809, 24.3098735809, 24.4541950226, 23.4352703094, - 24.5946865082, 22.5451297760, 24.7310256958, 21.6396484375, - 24.8629302979, 20.7191085815, 24.9901065826, 19.7838344574, - 25.1122798920, 18.8342094421, 25.2291812897, 17.8706703186, - 25.3405628204, 16.8937091827, 25.4461898804, 15.9038677216, - 25.5458450317, 14.9017429352, 25.6393356323, 13.8879728317, - 25.7264804840, 12.8632402420, 25.8071269989, 11.8282670975, - 25.8811359406, 10.7838068008, 25.9483890533, 9.7306461334, - 26.0087852478, 8.6695947647, 26.0622406006, 7.6014866829, - 26.1086864471, 6.5271716118, 26.1480674744, 5.4475140572, - 26.1803417206, 4.3633904457, 26.2054748535, 3.2756843567, - 26.2234420776, 2.1852869987, 26.2342300415, 1.0930929184, - 26.2378273010, -0.0000000000, 26.2342300415, -1.0930929184, - 26.2234420776, -2.1852869987, 26.2054748535, -3.2756843567, - 26.1803417206, -4.3633904457, 26.1480674744, -5.4475140572, - 26.1086864471, -6.5271716118, 26.0622406006, -7.6014866829, - 26.0087852478, -8.6695947647, 25.9483890533, -9.7306461334, - 25.8811359406, -10.7838068008, 25.8071269989, -11.8282670975, - 25.7264804840, -12.8632402420, 25.6393356323, -13.8879728317, - 25.5458450317, -14.9017429352, 25.4461898804, -15.9038677216, - 25.3405628204, -16.8937091827, 25.2291812897, -17.8706703186, - 25.1122798920, -18.8342094421, 24.9901065826, -19.7838344574, - 24.8629302979, -20.7191085815, 24.7310256958, -21.6396484375, - 24.5946865082, -22.5451297760, 24.4541950226, -23.4352703094, - 24.3098735809, -24.3098735809, 24.1620216370, -25.1687717438, - 24.0109481812, -26.0118598938, 23.8569583893, -26.8390789032, - 23.7003574371, -27.6504173279, 23.5414447784, -28.4459114075, - 23.3805084229, -29.2256336212, 24.2182693481, 29.0619239807, - 24.3828468323, 28.2841033936, 24.5453567505, 27.4907989502, - 24.7055053711, 26.6819458008, 24.8629932404, 25.8575134277, - 25.0175189972, 25.0175189972, 25.1687717438, 24.1620216370, - 25.3164443970, 23.2911281586, 25.4602241516, 22.4049987793, - 25.5998191833, 21.5038471222, 25.7349014282, 20.5879211426, - 25.8651943207, 19.6575489044, 25.9904155731, 18.7131004333, - 26.1102924347, 17.7549991608, 26.2245635986, 16.7837219238, - 26.3329906464, 15.7997951508, 26.4353485107, 14.8037958145, - 26.5314273834, 13.7963418961, 26.6210422516, 12.7781000137, - 26.7040214539, 11.7497692108, 26.7802181244, 10.7120866776, - 26.8494987488, 9.6658191681, 26.9117507935, 8.6117601395, - 26.9668769836, 7.5507259369, 27.0147991180, 6.4835519791, - 27.0554504395, 5.4110903740, 27.0887775421, 4.3342041969, - 27.1147384644, 3.2537686825, 27.1333026886, 2.1706643105, - 27.1444492340, 1.0857779980, 27.1481666565, -0.0000000000, - 27.1444492340, -1.0857779980, 27.1333026886, -2.1706643105, - 27.1147384644, -3.2537686825, 27.0887775421, -4.3342041969, - 27.0554504395, -5.4110903740, 27.0147991180, -6.4835519791, - 26.9668769836, -7.5507259369, 26.9117507935, -8.6117601395, - 26.8494987488, -9.6658191681, 26.7802181244, -10.7120866776, - 26.7040214539, -11.7497692108, 26.6210422516, -12.7781000137, - 26.5314273834, -13.7963418961, 26.4353485107, -14.8037958145, - 26.3329906464, -15.7997951508, 26.2245635986, -16.7837219238, - 26.1102924347, -17.7549991608, 25.9904155731, -18.7131004333, - 25.8651943207, -19.6575489044, 25.7349014282, -20.5879211426, - 25.5998191833, -21.5038471222, 25.4602241516, -22.4049987793, - 25.3164443970, -23.2911281586, 25.1687717438, -24.1620216370, - 25.0175189972, -25.0175189972, 24.8629932404, -25.8575134277, - 24.7055053711, -26.6819458008, 24.5453567505, -27.4907989502, - 24.3828468323, -28.2841033936, 24.2182693481, -29.0619239807, - 25.0420761108, 28.8947029114, 25.2099876404, 28.1188316345, - 25.3757820129, 27.3277645111, 25.5391674042, 26.5214443207, - 25.6998462677, 25.6998462677, 25.8575134277, 24.8629932404, - 26.0118598938, 24.0109481812, 26.1625747681, 23.1438159943, - 26.3093490601, 22.2617568970, 26.4518718719, 21.3649730682, - 26.5898437500, 20.4537258148, 26.7229709625, 19.5283241272, - 26.8509502411, 18.5891189575, 26.9735183716, 17.6365318298, - 27.0904083252, 16.6710205078, 27.2013721466, 15.6930990219, - 27.3061733246, 14.7033243179, 27.4045982361, 13.7022991180, - 27.4964504242, 12.6906690598, 27.5815448761, 11.6691150665, - 27.6597232819, 10.6383552551, 27.7308425903, 9.5991382599, - 27.7947807312, 8.5522403717, 27.8514251709, 7.4984607697, - 27.9006881714, 6.4386205673, 27.9424934387, 5.3735561371, - 27.9767761230, 4.3041195869, 28.0034904480, 3.2311718464, - 28.0225963593, 2.1555843353, 28.0340709686, 1.0782334805, - 28.0378971100, -0.0000000000, 28.0340709686, -1.0782334805, - 28.0225963593, -2.1555843353, 28.0034904480, -3.2311718464, - 27.9767761230, -4.3041195869, 27.9424934387, -5.3735561371, - 27.9006881714, -6.4386205673, 27.8514251709, -7.4984607697, - 27.7947807312, -8.5522403717, 27.7308425903, -9.5991382599, - 27.6597232819, -10.6383552551, 27.5815448761, -11.6691150665, - 27.4964504242, -12.6906690598, 27.4045982361, -13.7022991180, - 27.3061733246, -14.7033243179, 27.2013721466, -15.6930990219, - 27.0904083252, -16.6710205078, 26.9735183716, -17.6365318298, - 26.8509502411, -18.5891189575, 26.7229709625, -19.5283241272, - 26.5898437500, -20.4537258148, 26.4518718719, -21.3649730682, - 26.3093490601, -22.2617568970, 26.1625747681, -23.1438159943, - 26.0118598938, -24.0109481812, 25.8575134277, -24.8629932404, - 25.6998462677, -25.6998462677, 25.5391674042, -26.5214443207, - 25.3757820129, -27.3277645111, 25.2099876404, -28.1188316345, - 25.0420761108, -28.8947029114, 25.8518505096, 28.7242774963, - 26.0227890015, 27.9504032135, 26.1915645599, 27.1616210938, - 26.3578815460, 26.3578815460, 26.5214443207, 25.5391674042, - 26.6819458008, 24.7055053711, 26.8390789032, 23.8569583893, - 26.9925327301, 22.9936389923, 27.1419944763, 22.1156997681, - 27.2871570587, 21.2233448029, 27.4277153015, 20.3168258667, - 27.5633678436, 19.3964443207, 27.6938343048, 18.4625568390, - 27.8188114166, 17.5155487061, 27.9380435944, 16.5558776855, - 28.0512752533, 15.5840415955, 28.1582660675, 14.6005821228, - 28.2587909698, 13.6060838699, 28.3526420593, 12.6011743546, - 28.4396305084, 11.5865163803, 28.5195865631, 10.5628099442, - 28.5923538208, 9.5307846069, 28.6578006744, 8.4912004471, - 28.7158069611, 7.4448385239, 28.7662715912, 6.3925046921, - 28.8091106415, 5.3350205421, 28.8442516327, 4.2732224464, - 28.8716411591, 3.2079601288, 28.8912334442, 2.1400914192, - 28.9030017853, 1.0704815388, 28.9069271088, -0.0000000000, - 28.9030017853, -1.0704815388, 28.8912334442, -2.1400914192, - 28.8716411591, -3.2079601288, 28.8442516327, -4.2732224464, - 28.8091106415, -5.3350205421, 28.7662715912, -6.3925046921, - 28.7158069611, -7.4448385239, 28.6578006744, -8.4912004471, - 28.5923538208, -9.5307846069, 28.5195865631, -10.5628099442, - 28.4396305084, -11.5865163803, 28.3526420593, -12.6011743546, - 28.2587909698, -13.6060838699, 28.1582660675, -14.6005821228, - 28.0512752533, -15.5840415955, 27.9380435944, -16.5558776855, - 27.8188114166, -17.5155487061, 27.6938343048, -18.4625568390, - 27.5633678436, -19.3964443207, 27.4277153015, -20.3168258667, - 27.2871570587, -21.2233448029, 27.1419944763, -22.1156997681, - 26.9925327301, -22.9936389923, 26.8390789032, -23.8569583893, - 26.6819458008, -24.7055053711, 26.5214443207, -25.5391674042, - 26.3578815460, -26.3578815460, 26.1915645599, -27.1616210938, - 26.0227890015, -27.9504032135, 25.8518505096, -28.7242774963, - 26.6475524902, 28.5509490967, 26.8212203979, 27.7791213989, - 26.9926738739, 26.9926738739, 27.1616210938, 26.1915645599, - 27.3277645111, 25.3757820129, 27.4907989502, 24.5453567505, - 27.6504173279, 23.7003574371, 27.8063087463, 22.8408966064, - 27.9581623077, 21.9671268463, 28.1056671143, 21.0792503357, - 28.2485179901, 20.1775131226, 28.3864135742, 19.2622089386, - 28.5190601349, 18.3336811066, 28.6461734772, 17.3923187256, - 28.7674865723, 16.4385623932, 28.8827209473, 15.4728860855, - 28.9916419983, 14.4958209991, 29.0940208435, 13.5079383850, - 29.1896381378, 12.5098447800, 29.2782974243, 11.5021886826, - 29.3598213196, 10.4856500626, 29.4340457916, 9.4609432220, - 29.5008239746, 8.4288072586, 29.5600337982, 7.3900084496, - 29.6115608215, 6.3453345299, 29.6553134918, 5.2955918312, - 29.6912136078, 4.2416019440, 29.7192001343, 3.1842000484, - 29.7392234802, 2.1242303848, 29.7512512207, 1.0625447035, - 29.7552623749, -0.0000000000, 29.7512512207, -1.0625447035, - 29.7392234802, -2.1242303848, 29.7192001343, -3.1842000484, - 29.6912136078, -4.2416019440, 29.6553134918, -5.2955918312, - 29.6115608215, -6.3453345299, 29.5600337982, -7.3900084496, - 29.5008239746, -8.4288072586, 29.4340457916, -9.4609432220, - 29.3598213196, -10.4856500626, 29.2782974243, -11.5021886826, - 29.1896381378, -12.5098447800, 29.0940208435, -13.5079383850, - 28.9916419983, -14.4958209991, 28.8827209473, -15.4728860855, - 28.7674865723, -16.4385623932, 28.6461734772, -17.3923187256, - 28.5190601349, -18.3336811066, 28.3864135742, -19.2622089386, - 28.2485179901, -20.1775131226, 28.1056671143, -21.0792503357, - 27.9581623077, -21.9671268463, 27.8063087463, -22.8408966064, - 27.6504173279, -23.7003574371, 27.4907989502, -24.5453567505, - 27.3277645111, -25.3757820129, 27.1616210938, -26.1915645599, - 26.9926738739, -26.9926738739, 26.8212203979, -27.7791213989, - 26.6475524902, -28.5509490967, 27.4291725159, 28.3750057220, - 27.6052837372, 27.6052837372, 27.7791213989, 26.8212203979, - 27.9504032135, 26.0227890015, 28.1188316345, 25.2099876404, - 28.2841033936, 24.3828468323, 28.4459114075, 23.5414447784, - 28.6039466858, 22.6858882904, 28.7578964233, 21.8163356781, - 28.9074535370, 20.9329833984, 29.0523090363, 20.0360755920, - 29.1921634674, 19.1259002686, 29.3267192841, 18.2027912140, - 29.4556884766, 17.2671279907, 29.5787963867, 16.3193359375, - 29.6957893372, 15.3598909378, 29.8063888550, 14.3892917633, - 29.9103755951, 13.4081001282, 30.0075283051, 12.4169082642, - 30.0976409912, 11.4163465500, 30.1805267334, 10.4070777893, - 30.2560138702, 9.3897972107, 30.3239517212, 8.3652276993, - 30.3842048645, 7.3341183662, 30.4366550446, 6.2972393036, - 30.4812030792, 5.2553801537, 30.5177650452, 4.2093467712, - 30.5462703705, 3.1599588394, 30.5666675568, 2.1080460548, - 30.5789222717, 1.0544456244, 30.5830078125, -0.0000000000, - 30.5789222717, -1.0544456244, 30.5666675568, -2.1080460548, - 30.5462703705, -3.1599588394, 30.5177650452, -4.2093467712, - 30.4812030792, -5.2553801537, 30.4366550446, -6.2972393036, - 30.3842048645, -7.3341183662, 30.3239517212, -8.3652276993, - 30.2560138702, -9.3897972107, 30.1805267334, -10.4070777893, - 30.0976409912, -11.4163465500, 30.0075283051, -12.4169082642, - 29.9103755951, -13.4081001282, 29.8063888550, -14.3892917633, - 29.6957893372, -15.3598909378, 29.5787963867, -16.3193359375, - 29.4556884766, -17.2671279907, 29.3267192841, -18.2027912140, - 29.1921634674, -19.1259002686, 29.0523090363, -20.0360755920, - 28.9074535370, -20.9329833984, 28.7578964233, -21.8163356781, - 28.6039466858, -22.6858882904, 28.4459114075, -23.5414447784, - 28.2841033936, -24.3828468323, 28.1188316345, -25.2099876404, - 27.9504032135, -26.0227890015, 27.7791213989, -26.8212203979, - 27.6052837372, -27.6052837372, 27.4291725159, -28.3750057220, - 28.1967468262, 28.1967468262, 28.3750057220, 27.4291725159, - 28.5509490967, 26.6475524902, 28.7242774963, 25.8518505096, - 28.8947029114, 25.0420761108, 29.0619239807, 24.2182693481, - 29.2256336212, 23.3805084229, 29.3855266571, 22.5289039612, - 29.5412883759, 21.6636123657, 29.6926136017, 20.7848300934, - 29.8391952515, 19.8927974701, 29.9807300568, 18.9877948761, - 30.1169204712, 18.0701522827, 30.2474803925, 17.1402397156, - 30.3721294403, 16.1984691620, 30.4906005859, 15.2453002930, - 30.6026382446, 14.2812309265, 30.7080020905, 13.3068008423, - 30.8064727783, 12.3225889206, 30.8978214264, 11.3292007446, - 30.9818668365, 10.3272886276, 31.0584316254, 9.3175296783, - 31.1273555756, 8.3006286621, 31.1884994507, 7.2773165703, - 31.2417373657, 6.2483472824, 31.2869625092, 5.2144937515, - 31.3240852356, 4.1765446663, 31.3530349731, 3.1353034973, - 31.3737506866, 2.0915834904, 31.3861980438, 1.0462065935, - 31.3903484344, -0.0000000000, 31.3861980438, -1.0462065935, - 31.3737506866, -2.0915834904, 31.3530349731, -3.1353034973, - 31.3240852356, -4.1765446663, 31.2869625092, -5.2144937515, - 31.2417373657, -6.2483472824, 31.1884994507, -7.2773165703, - 31.1273555756, -8.3006286621, 31.0584316254, -9.3175296783, - 30.9818668365, -10.3272886276, 30.8978214264, -11.3292007446, - 30.8064727783, -12.3225889206, 30.7080020905, -13.3068008423, - 30.6026382446, -14.2812309265, 30.4906005859, -15.2453002930, - 30.3721294403, -16.1984691620, 30.2474803925, -17.1402397156, - 30.1169204712, -18.0701522827, 29.9807300568, -18.9877948761, - 29.8391952515, -19.8927974701, 29.6926136017, -20.7848300934, - 29.5412883759, -21.6636123657, 29.3855266571, -22.5289039612, - 29.2256336212, -23.3805084229, 29.0619239807, -24.2182693481, - 28.8947029114, -25.0420761108, 28.7242774963, -25.8518505096, - 28.5509490967, -26.6475524902, 28.3750057220, -27.4291725159, - 28.1967468262, -28.1967468262, -}; - -} /* namespace Pdraw */ - -#endif /* USE_GLES2 */ +/** + * Parrot Drones Awesome Video Viewer Library + * OpenGL ES 2.0 HMD distortion correction + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef USE_GLES2 + +namespace Pdraw { + +extern const float pdraw_gles2HmdPositionsCockpitglasses2[7442] = { + -28.1967468262, 28.1967468262, -28.3750057220, 27.4291725159, + -28.5509490967, 26.6475524902, -28.7242774963, 25.8518505096, + -28.8947029114, 25.0420761108, -29.0619239807, 24.2182693481, + -29.2256336212, 23.3805084229, -29.3855266571, 22.5289039612, + -29.5412883759, 21.6636123657, -29.6926136017, 20.7848300934, + -29.8391952515, 19.8927974701, -29.9807300568, 18.9877948761, + -30.1169204712, 18.0701522827, -30.2474803925, 17.1402397156, + -30.3721294403, 16.1984691620, -30.4906005859, 15.2453002930, + -30.6026382446, 14.2812309265, -30.7080020905, 13.3068008423, + -30.8064727783, 12.3225889206, -30.8978214264, 11.3292007446, + -30.9818668365, 10.3272886276, -31.0584316254, 9.3175296783, + -31.1273555756, 8.3006286621, -31.1884994507, 7.2773165703, + -31.2417373657, 6.2483472824, -31.2869625092, 5.2144937515, + -31.3240852356, 4.1765446663, -31.3530349731, 3.1353034973, + -31.3737506866, 2.0915834904, -31.3861980438, 1.0462065935, + -31.3903484344, -0.0000000000, -31.3861980438, -1.0462065935, + -31.3737506866, -2.0915834904, -31.3530349731, -3.1353034973, + -31.3240852356, -4.1765446663, -31.2869625092, -5.2144937515, + -31.2417373657, -6.2483472824, -31.1884994507, -7.2773165703, + -31.1273555756, -8.3006286621, -31.0584316254, -9.3175296783, + -30.9818668365, -10.3272886276, -30.8978214264, -11.3292007446, + -30.8064727783, -12.3225889206, -30.7080020905, -13.3068008423, + -30.6026382446, -14.2812309265, -30.4906005859, -15.2453002930, + -30.3721294403, -16.1984691620, -30.2474803925, -17.1402397156, + -30.1169204712, -18.0701522827, -29.9807300568, -18.9877948761, + -29.8391952515, -19.8927974701, -29.6926136017, -20.7848300934, + -29.5412883759, -21.6636123657, -29.3855266571, -22.5289039612, + -29.2256336212, -23.3805084229, -29.0619239807, -24.2182693481, + -28.8947029114, -25.0420761108, -28.7242774963, -25.8518505096, + -28.5509490967, -26.6475524902, -28.3750057220, -27.4291725159, + -28.1967468262, -28.1967468262, -27.4291725159, 28.3750057220, + -27.6052837372, 27.6052837372, -27.7791213989, 26.8212203979, + -27.9504032135, 26.0227890015, -28.1188316345, 25.2099876404, + -28.2841033936, 24.3828468323, -28.4459114075, 23.5414447784, + -28.6039466858, 22.6858882904, -28.7578964233, 21.8163356781, + -28.9074535370, 20.9329833984, -29.0523090363, 20.0360755920, + -29.1921634674, 19.1259002686, -29.3267192841, 18.2027912140, + -29.4556884766, 17.2671279907, -29.5787963867, 16.3193359375, + -29.6957893372, 15.3598909378, -29.8063888550, 14.3892917633, + -29.9103755951, 13.4081001282, -30.0075283051, 12.4169082642, + -30.0976409912, 11.4163465500, -30.1805267334, 10.4070777893, + -30.2560138702, 9.3897972107, -30.3239517212, 8.3652276993, + -30.3842048645, 7.3341183662, -30.4366550446, 6.2972393036, + -30.4812030792, 5.2553801537, -30.5177650452, 4.2093467712, + -30.5462703705, 3.1599588394, -30.5666675568, 2.1080460548, + -30.5789222717, 1.0544456244, -30.5830078125, -0.0000000000, + -30.5789222717, -1.0544456244, -30.5666675568, -2.1080460548, + -30.5462703705, -3.1599588394, -30.5177650452, -4.2093467712, + -30.4812030792, -5.2553801537, -30.4366550446, -6.2972393036, + -30.3842048645, -7.3341183662, -30.3239517212, -8.3652276993, + -30.2560138702, -9.3897972107, -30.1805267334, -10.4070777893, + -30.0976409912, -11.4163465500, -30.0075283051, -12.4169082642, + -29.9103755951, -13.4081001282, -29.8063888550, -14.3892917633, + -29.6957893372, -15.3598909378, -29.5787963867, -16.3193359375, + -29.4556884766, -17.2671279907, -29.3267192841, -18.2027912140, + -29.1921634674, -19.1259002686, -29.0523090363, -20.0360755920, + -28.9074535370, -20.9329833984, -28.7578964233, -21.8163356781, + -28.6039466858, -22.6858882904, -28.4459114075, -23.5414447784, + -28.2841033936, -24.3828468323, -28.1188316345, -25.2099876404, + -27.9504032135, -26.0227890015, -27.7791213989, -26.8212203979, + -27.6052837372, -27.6052837372, -27.4291725159, -28.3750057220, + -26.6475524902, 28.5509490967, -26.8212203979, 27.7791213989, + -26.9926738739, 26.9926738739, -27.1616210938, 26.1915645599, + -27.3277645111, 25.3757820129, -27.4907989502, 24.5453567505, + -27.6504173279, 23.7003574371, -27.8063087463, 22.8408966064, + -27.9581623077, 21.9671268463, -28.1056671143, 21.0792503357, + -28.2485179901, 20.1775131226, -28.3864135742, 19.2622089386, + -28.5190601349, 18.3336811066, -28.6461734772, 17.3923187256, + -28.7674865723, 16.4385623932, -28.8827209473, 15.4728860855, + -28.9916419983, 14.4958209991, -29.0940208435, 13.5079383850, + -29.1896381378, 12.5098447800, -29.2782974243, 11.5021886826, + -29.3598213196, 10.4856500626, -29.4340457916, 9.4609432220, + -29.5008239746, 8.4288072586, -29.5600337982, 7.3900084496, + -29.6115608215, 6.3453345299, -29.6553134918, 5.2955918312, + -29.6912136078, 4.2416019440, -29.7192001343, 3.1842000484, + -29.7392234802, 2.1242303848, -29.7512512207, 1.0625447035, + -29.7552623749, -0.0000000000, -29.7512512207, -1.0625447035, + -29.7392234802, -2.1242303848, -29.7192001343, -3.1842000484, + -29.6912136078, -4.2416019440, -29.6553134918, -5.2955918312, + -29.6115608215, -6.3453345299, -29.5600337982, -7.3900084496, + -29.5008239746, -8.4288072586, -29.4340457916, -9.4609432220, + -29.3598213196, -10.4856500626, -29.2782974243, -11.5021886826, + -29.1896381378, -12.5098447800, -29.0940208435, -13.5079383850, + -28.9916419983, -14.4958209991, -28.8827209473, -15.4728860855, + -28.7674865723, -16.4385623932, -28.6461734772, -17.3923187256, + -28.5190601349, -18.3336811066, -28.3864135742, -19.2622089386, + -28.2485179901, -20.1775131226, -28.1056671143, -21.0792503357, + -27.9581623077, -21.9671268463, -27.8063087463, -22.8408966064, + -27.6504173279, -23.7003574371, -27.4907989502, -24.5453567505, + -27.3277645111, -25.3757820129, -27.1616210938, -26.1915645599, + -26.9926738739, -26.9926738739, -26.8212203979, -27.7791213989, + -26.6475524902, -28.5509490967, -25.8518505096, 28.7242774963, + -26.0227890015, 27.9504032135, -26.1915645599, 27.1616210938, + -26.3578815460, 26.3578815460, -26.5214443207, 25.5391674042, + -26.6819458008, 24.7055053711, -26.8390789032, 23.8569583893, + -26.9925327301, 22.9936389923, -27.1419944763, 22.1156997681, + -27.2871570587, 21.2233448029, -27.4277153015, 20.3168258667, + -27.5633678436, 19.3964443207, -27.6938343048, 18.4625568390, + -27.8188114166, 17.5155487061, -27.9380435944, 16.5558776855, + -28.0512752533, 15.5840415955, -28.1582660675, 14.6005821228, + -28.2587909698, 13.6060838699, -28.3526420593, 12.6011743546, + -28.4396305084, 11.5865163803, -28.5195865631, 10.5628099442, + -28.5923538208, 9.5307846069, -28.6578006744, 8.4912004471, + -28.7158069611, 7.4448385239, -28.7662715912, 6.3925046921, + -28.8091106415, 5.3350205421, -28.8442516327, 4.2732224464, + -28.8716411591, 3.2079601288, -28.8912334442, 2.1400914192, + -28.9030017853, 1.0704815388, -28.9069271088, -0.0000000000, + -28.9030017853, -1.0704815388, -28.8912334442, -2.1400914192, + -28.8716411591, -3.2079601288, -28.8442516327, -4.2732224464, + -28.8091106415, -5.3350205421, -28.7662715912, -6.3925046921, + -28.7158069611, -7.4448385239, -28.6578006744, -8.4912004471, + -28.5923538208, -9.5307846069, -28.5195865631, -10.5628099442, + -28.4396305084, -11.5865163803, -28.3526420593, -12.6011743546, + -28.2587909698, -13.6060838699, -28.1582660675, -14.6005821228, + -28.0512752533, -15.5840415955, -27.9380435944, -16.5558776855, + -27.8188114166, -17.5155487061, -27.6938343048, -18.4625568390, + -27.5633678436, -19.3964443207, -27.4277153015, -20.3168258667, + -27.2871570587, -21.2233448029, -27.1419944763, -22.1156997681, + -26.9925327301, -22.9936389923, -26.8390789032, -23.8569583893, + -26.6819458008, -24.7055053711, -26.5214443207, -25.5391674042, + -26.3578815460, -26.3578815460, -26.1915645599, -27.1616210938, + -26.0227890015, -27.9504032135, -25.8518505096, -28.7242774963, + -25.0420761108, 28.8947029114, -25.2099876404, 28.1188316345, + -25.3757820129, 27.3277645111, -25.5391674042, 26.5214443207, + -25.6998462677, 25.6998462677, -25.8575134277, 24.8629932404, + -26.0118598938, 24.0109481812, -26.1625747681, 23.1438159943, + -26.3093490601, 22.2617568970, -26.4518718719, 21.3649730682, + -26.5898437500, 20.4537258148, -26.7229709625, 19.5283241272, + -26.8509502411, 18.5891189575, -26.9735183716, 17.6365318298, + -27.0904083252, 16.6710205078, -27.2013721466, 15.6930990219, + -27.3061733246, 14.7033243179, -27.4045982361, 13.7022991180, + -27.4964504242, 12.6906690598, -27.5815448761, 11.6691150665, + -27.6597232819, 10.6383552551, -27.7308425903, 9.5991382599, + -27.7947807312, 8.5522403717, -27.8514251709, 7.4984607697, + -27.9006881714, 6.4386205673, -27.9424934387, 5.3735561371, + -27.9767761230, 4.3041195869, -28.0034904480, 3.2311718464, + -28.0225963593, 2.1555843353, -28.0340709686, 1.0782334805, + -28.0378971100, -0.0000000000, -28.0340709686, -1.0782334805, + -28.0225963593, -2.1555843353, -28.0034904480, -3.2311718464, + -27.9767761230, -4.3041195869, -27.9424934387, -5.3735561371, + -27.9006881714, -6.4386205673, -27.8514251709, -7.4984607697, + -27.7947807312, -8.5522403717, -27.7308425903, -9.5991382599, + -27.6597232819, -10.6383552551, -27.5815448761, -11.6691150665, + -27.4964504242, -12.6906690598, -27.4045982361, -13.7022991180, + -27.3061733246, -14.7033243179, -27.2013721466, -15.6930990219, + -27.0904083252, -16.6710205078, -26.9735183716, -17.6365318298, + -26.8509502411, -18.5891189575, -26.7229709625, -19.5283241272, + -26.5898437500, -20.4537258148, -26.4518718719, -21.3649730682, + -26.3093490601, -22.2617568970, -26.1625747681, -23.1438159943, + -26.0118598938, -24.0109481812, -25.8575134277, -24.8629932404, + -25.6998462677, -25.6998462677, -25.5391674042, -26.5214443207, + -25.3757820129, -27.3277645111, -25.2099876404, -28.1188316345, + -25.0420761108, -28.8947029114, -24.2182693481, 29.0619239807, + -24.3828468323, 28.2841033936, -24.5453567505, 27.4907989502, + -24.7055053711, 26.6819458008, -24.8629932404, 25.8575134277, + -25.0175189972, 25.0175189972, -25.1687717438, 24.1620216370, + -25.3164443970, 23.2911281586, -25.4602241516, 22.4049987793, + -25.5998191833, 21.5038471222, -25.7349014282, 20.5879211426, + -25.8651943207, 19.6575489044, -25.9904155731, 18.7131004333, + -26.1102924347, 17.7549991608, -26.2245635986, 16.7837219238, + -26.3329906464, 15.7997951508, -26.4353485107, 14.8037958145, + -26.5314273834, 13.7963418961, -26.6210422516, 12.7781000137, + -26.7040214539, 11.7497692108, -26.7802181244, 10.7120866776, + -26.8494987488, 9.6658191681, -26.9117507935, 8.6117601395, + -26.9668769836, 7.5507259369, -27.0147991180, 6.4835519791, + -27.0554504395, 5.4110903740, -27.0887775421, 4.3342041969, + -27.1147384644, 3.2537686825, -27.1333026886, 2.1706643105, + -27.1444492340, 1.0857779980, -27.1481666565, -0.0000000000, + -27.1444492340, -1.0857779980, -27.1333026886, -2.1706643105, + -27.1147384644, -3.2537686825, -27.0887775421, -4.3342041969, + -27.0554504395, -5.4110903740, -27.0147991180, -6.4835519791, + -26.9668769836, -7.5507259369, -26.9117507935, -8.6117601395, + -26.8494987488, -9.6658191681, -26.7802181244, -10.7120866776, + -26.7040214539, -11.7497692108, -26.6210422516, -12.7781000137, + -26.5314273834, -13.7963418961, -26.4353485107, -14.8037958145, + -26.3329906464, -15.7997951508, -26.2245635986, -16.7837219238, + -26.1102924347, -17.7549991608, -25.9904155731, -18.7131004333, + -25.8651943207, -19.6575489044, -25.7349014282, -20.5879211426, + -25.5998191833, -21.5038471222, -25.4602241516, -22.4049987793, + -25.3164443970, -23.2911281586, -25.1687717438, -24.1620216370, + -25.0175189972, -25.0175189972, -24.8629932404, -25.8575134277, + -24.7055053711, -26.6819458008, -24.5453567505, -27.4907989502, + -24.3828468323, -28.2841033936, -24.2182693481, -29.0619239807, + -23.3805084229, 29.2256336212, -23.5414447784, 28.4459114075, + -23.7003574371, 27.6504173279, -23.8569583893, 26.8390789032, + -24.0109481812, 26.0118598938, -24.1620216370, 25.1687717438, + -24.3098735809, 24.3098735809, -24.4541950226, 23.4352703094, + -24.5946865082, 22.5451297760, -24.7310256958, 21.6396484375, + -24.8629302979, 20.7191085815, -24.9901065826, 19.7838344574, + -25.1122798920, 18.8342094421, -25.2291812897, 17.8706703186, + -25.3405628204, 16.8937091827, -25.4461898804, 15.9038677216, + -25.5458450317, 14.9017429352, -25.6393356323, 13.8879728317, + -25.7264804840, 12.8632402420, -25.8071269989, 11.8282670975, + -25.8811359406, 10.7838068008, -25.9483890533, 9.7306461334, + -26.0087852478, 8.6695947647, -26.0622406006, 7.6014866829, + -26.1086864471, 6.5271716118, -26.1480674744, 5.4475140572, + -26.1803417206, 4.3633904457, -26.2054748535, 3.2756843567, + -26.2234420776, 2.1852869987, -26.2342300415, 1.0930929184, + -26.2378273010, -0.0000000000, -26.2342300415, -1.0930929184, + -26.2234420776, -2.1852869987, -26.2054748535, -3.2756843567, + -26.1803417206, -4.3633904457, -26.1480674744, -5.4475140572, + -26.1086864471, -6.5271716118, -26.0622406006, -7.6014866829, + -26.0087852478, -8.6695947647, -25.9483890533, -9.7306461334, + -25.8811359406, -10.7838068008, -25.8071269989, -11.8282670975, + -25.7264804840, -12.8632402420, -25.6393356323, -13.8879728317, + -25.5458450317, -14.9017429352, -25.4461898804, -15.9038677216, + -25.3405628204, -16.8937091827, -25.2291812897, -17.8706703186, + -25.1122798920, -18.8342094421, -24.9901065826, -19.7838344574, + -24.8629302979, -20.7191085815, -24.7310256958, -21.6396484375, + -24.5946865082, -22.5451297760, -24.4541950226, -23.4352703094, + -24.3098735809, -24.3098735809, -24.1620216370, -25.1687717438, + -24.0109481812, -26.0118598938, -23.8569583893, -26.8390789032, + -23.7003574371, -27.6504173279, -23.5414447784, -28.4459114075, + -23.3805084229, -29.2256336212, -22.5289039612, 29.3855266571, + -22.6858882904, 28.6039466858, -22.8408966064, 27.8063087463, + -22.9936389923, 26.9925327301, -23.1438159943, 26.1625747681, + -23.2911281586, 25.3164443970, -23.4352703094, 24.4541950226, + -23.5759410858, 23.5759410858, -23.7128200531, 22.6818294525, + -23.8456211090, 21.7720890045, -23.9740486145, 20.8469982147, + -24.0978183746, 19.9068927765, -24.2166576385, 18.9521656036, + -24.3303070068, 17.9832706451, -24.4385280609, 17.0007152557, + -24.5410938263, 16.0050601959, -24.6378002167, 14.9969215393, + -24.7284622192, 13.9769563675, -24.8129138947, 12.9458684921, + -24.8910140991, 11.9043979645, -24.9626388550, 10.8533210754, + -25.0276813507, 9.7934408188, -25.0860557556, 8.7255840302, + -25.1376914978, 7.6506013870, -25.1825313568, 6.5693559647, + -25.2205314636, 5.4827241898, -25.2516613007, 4.3915929794, + -25.2758941650, 3.2968556881, -25.2932147980, 2.1994099617, + -25.3036098480, 1.1001570225, -25.3070774078, -0.0000000000, + -25.3036098480, -1.1001570225, -25.2932147980, -2.1994099617, + -25.2758941650, -3.2968556881, -25.2516613007, -4.3915929794, + -25.2205314636, -5.4827241898, -25.1825313568, -6.5693559647, + -25.1376914978, -7.6506013870, -25.0860557556, -8.7255840302, + -25.0276813507, -9.7934408188, -24.9626388550, -10.8533210754, + -24.8910140991, -11.9043979645, -24.8129138947, -12.9458684921, + -24.7284622192, -13.9769563675, -24.6378002167, -14.9969215393, + -24.5410938263, -16.0050601959, -24.4385280609, -17.0007152557, + -24.3303070068, -17.9832706451, -24.2166576385, -18.9521656036, + -24.0978183746, -19.9068927765, -23.9740486145, -20.8469982147, + -23.8456211090, -21.7720890045, -23.7128200531, -22.6818294525, + -23.5759410858, -23.5759410858, -23.4352703094, -24.4541950226, + -23.2911281586, -25.3164443970, -23.1438159943, -26.1625747681, + -22.9936389923, -26.9925327301, -22.8408966064, -27.8063087463, + -22.6858882904, -28.6039466858, -22.5289039612, -29.3855266571, + -21.6636123657, 29.5412883759, -21.8163356781, 28.7578964233, + -21.9671268463, 27.9581623077, -22.1156997681, 27.1419944763, + -22.2617568970, 26.3093490601, -22.4049987793, 25.4602241516, + -22.5451297760, 24.5946865082, -22.6818294525, 23.7128200531, + -22.8148078918, 22.8148078918, -22.9437732697, 21.9008731842, + -23.0684318542, 20.9713001251, -23.1885070801, 20.0264377594, + -23.3037357330, 19.0666923523, -23.4138622284, 18.0925292969, + -23.5186595917, 17.1044807434, -23.6179122925, 16.1031227112, + -23.7114276886, 15.0890903473, -23.7990322113, 14.0630645752, + -23.8805751801, 13.0257682800, -23.9559268951, 11.9779634476, + -24.0249786377, 10.9204444885, -24.0876388550, 9.8540334702, + -24.1438331604, 8.7795763016, -24.1935100555, 7.6979346275, + -24.2366218567, 6.6099877357, -24.2731380463, 5.5166220665, + -24.3030376434, 4.4187340736, -24.3263034821, 3.3172230721, + -24.3429279327, 2.2129933834, -24.3529033661, 1.1069501638, + -24.3562297821, -0.0000000000, -24.3529033661, -1.1069501638, + -24.3429279327, -2.2129933834, -24.3263034821, -3.3172230721, + -24.3030376434, -4.4187340736, -24.2731380463, -5.5166220665, + -24.2366218567, -6.6099877357, -24.1935100555, -7.6979346275, + -24.1438331604, -8.7795763016, -24.0876388550, -9.8540334702, + -24.0249786377, -10.9204444885, -23.9559268951, -11.9779634476, + -23.8805751801, -13.0257682800, -23.7990322113, -14.0630645752, + -23.7114276886, -15.0890903473, -23.6179122925, -16.1031227112, + -23.5186595917, -17.1044807434, -23.4138622284, -18.0925292969, + -23.3037357330, -19.0666923523, -23.1885070801, -20.0264377594, + -23.0684318542, -20.9713001251, -22.9437732697, -21.9008731842, + -22.8148078918, -22.8148078918, -22.6818294525, -23.7128200531, + -22.5451297760, -24.5946865082, -22.4049987793, -25.4602241516, + -22.2617568970, -26.3093490601, -22.1156997681, -27.1419944763, + -21.9671268463, -27.9581623077, -21.8163356781, -28.7578964233, + -21.6636123657, -29.5412883759, -20.7848300934, 29.6926136017, + -20.9329833984, 28.9074535370, -21.0792503357, 28.1056671143, + -21.2233448029, 27.2871570587, -21.3649730682, 26.4518718719, + -21.5038471222, 25.5998191833, -21.6396484375, 24.7310256958, + -21.7720890045, 23.8456211090, -21.9008731842, 22.9437732697, + -22.0257110596, 22.0257110596, -22.1463165283, 21.0917301178, + -22.2624206543, 20.1421909332, -22.3737659454, 19.1775150299, + -22.4801120758, 18.1981868744, -22.5812358856, 17.2047519684, + -22.6769371033, 16.1978111267, -22.7670307159, 15.1780204773, + -22.8513603210, 14.1460809708, -22.9297904968, 13.1027374268, + -23.0022029877, 12.0487728119, -23.0685062408, 10.9850025177, + -23.1286220551, 9.9122667313, -23.1824951172, 8.8314266205, + -23.2300834656, 7.7433609962, -23.2713546753, 6.6489582062, + -23.3062915802, 5.5491170883, -23.3348808289, 4.4447393417, + -23.3571205139, 3.3367314339, -23.3730049133, 2.2260005474, + -23.3825359344, 1.1134541035, -23.3857116699, -0.0000000000, + -23.3825359344, -1.1134541035, -23.3730049133, -2.2260005474, + -23.3571205139, -3.3367314339, -23.3348808289, -4.4447393417, + -23.3062915802, -5.5491170883, -23.2713546753, -6.6489582062, + -23.2300834656, -7.7433609962, -23.1824951172, -8.8314266205, + -23.1286220551, -9.9122667313, -23.0685062408, -10.9850025177, + -23.0022029877, -12.0487728119, -22.9297904968, -13.1027374268, + -22.8513603210, -14.1460809708, -22.7670307159, -15.1780204773, + -22.6769371033, -16.1978111267, -22.5812358856, -17.2047519684, + -22.4801120758, -18.1981868744, -22.3737659454, -19.1775150299, + -22.2624206543, -20.1421909332, -22.1463165283, -21.0917301178, + -22.0257110596, -22.0257110596, -21.9008731842, -22.9437732697, + -21.7720890045, -23.8456211090, -21.6396484375, -24.7310256958, + -21.5038471222, -25.5998191833, -21.3649730682, -26.4518718719, + -21.2233448029, -27.2871570587, -21.0792503357, -28.1056671143, + -20.9329833984, -28.9074535370, -20.7848300934, -29.6926136017, + -19.8927974701, 29.8391952515, -20.0360755920, 29.0523090363, + -20.1775131226, 28.2485179901, -20.3168258667, 27.4277153015, + -20.4537258148, 26.5898437500, -20.5879211426, 25.7349014282, + -20.7191085815, 24.8629302979, -20.8469982147, 23.9740486145, + -20.9713001251, 23.0684318542, -21.0917301178, 22.1463165283, + -21.2080097198, 21.2080097198, -21.3198776245, 20.2538833618, + -21.4270839691, 19.2843761444, -21.5293998718, 18.2999897003, + -21.6266136169, 17.3012905121, -21.7185325623, 16.2889003754, + -21.8049926758, 15.2634954453, -21.8858470917, 14.2258014679, + -21.9609756470, 13.1765851974, -22.0302753448, 12.1166515350, + -22.0936698914, 11.0468349457, -22.1510982513, 9.9679937363, + -22.2025184631, 8.8810071945, -22.2479019165, 7.7867655754, + -22.2872333527, 6.6861701012, -22.3205051422, 5.5801262856, + -22.3477191925, 4.4695439339, -22.3688755035, 3.3553314209, + -22.3839836121, 2.2383983135, -22.3930454254, 1.1196522713, + -22.3960647583, -0.0000000000, -22.3930454254, -1.1196522713, + -22.3839836121, -2.2383983135, -22.3688755035, -3.3553314209, + -22.3477191925, -4.4695439339, -22.3205051422, -5.5801262856, + -22.2872333527, -6.6861701012, -22.2479019165, -7.7867655754, + -22.2025184631, -8.8810071945, -22.1510982513, -9.9679937363, + -22.0936698914, -11.0468349457, -22.0302753448, -12.1166515350, + -21.9609756470, -13.1765851974, -21.8858470917, -14.2258014679, + -21.8049926758, -15.2634954453, -21.7185325623, -16.2889003754, + -21.6266136169, -17.3012905121, -21.5293998718, -18.2999897003, + -21.4270839691, -19.2843761444, -21.3198776245, -20.2538833618, + -21.2080097198, -21.2080097198, -21.0917301178, -22.1463165283, + -20.9713001251, -23.0684318542, -20.8469982147, -23.9740486145, + -20.7191085815, -24.8629302979, -20.5879211426, -25.7349014282, + -20.4537258148, -26.5898437500, -20.3168258667, -27.4277153015, + -20.1775131226, -28.2485179901, -20.0360755920, -29.0523090363, + -19.8927974701, -29.8391952515, -18.9877948761, 29.9807300568, + -19.1259002686, 29.1921634674, -19.2622089386, 28.3864135742, + -19.3964443207, 27.5633678436, -19.5283241272, 26.7229709625, + -19.6575489044, 25.8651943207, -19.7838344574, 24.9901065826, + -19.9068927765, 24.0978183746, -20.0264377594, 23.1885070801, + -20.1421909332, 22.2624206543, -20.2538833618, 21.3198776245, + -20.3612632751, 20.3612632751, -20.4640884399, 19.3870315552, + -20.5621433258, 18.3977069855, -20.6552238464, 17.3938732147, + -20.7431564331, 16.3761768341, -20.8257865906, 15.3453159332, + -20.9029827118, 14.3020410538, -20.9746379852, 13.2471399307, + -21.0406703949, 12.1814413071, -21.1010150909, 11.1057968140, + -21.1556262970, 10.0210866928, -21.2044811249, 8.9282026291, + -21.2475624084, 7.8280491829, -21.2848682404, 6.7215371132, + -21.3164043427, 5.6095800400, -21.3421802521, 4.4930906296, + -21.3622112274, 3.3729808331, -21.3765087128, 2.2501587868, + -21.3850822449, 1.1255306005, -21.3879394531, -0.0000000000, + -21.3850822449, -1.1255306005, -21.3765087128, -2.2501587868, + -21.3622112274, -3.3729808331, -21.3421802521, -4.4930906296, + -21.3164043427, -5.6095800400, -21.2848682404, -6.7215371132, + -21.2475624084, -7.8280491829, -21.2044811249, -8.9282026291, + -21.1556262970, -10.0210866928, -21.1010150909, -11.1057968140, + -21.0406703949, -12.1814413071, -20.9746379852, -13.2471399307, + -20.9029827118, -14.3020410538, -20.8257865906, -15.3453159332, + -20.7431564331, -16.3761768341, -20.6552238464, -17.3938732147, + -20.5621433258, -18.3977069855, -20.4640884399, -19.3870315552, + -20.3612632751, -20.3612632751, -20.2538833618, -21.3198776245, + -20.1421909332, -22.2624206543, -20.0264377594, -23.1885070801, + -19.9068927765, -24.0978183746, -19.7838344574, -24.9901065826, + -19.6575489044, -25.8651943207, -19.5283241272, -26.7229709625, + -19.3964443207, -27.5633678436, -19.2622089386, -28.3864135742, + -19.1259002686, -29.1921634674, -18.9877948761, -29.9807300568, + -18.0701522827, 30.1169204712, -18.2027912140, 29.3267192841, + -18.3336811066, 28.5190601349, -18.4625568390, 27.6938343048, + -18.5891189575, 26.8509502411, -18.7131004333, 25.9904155731, + -18.8342094421, 25.1122798920, -18.9521656036, 24.2166576385, + -19.0666923523, 23.3037357330, -19.1775150299, 22.3737659454, + -19.2843761444, 21.4270839691, -19.3870315552, 20.4640884399, + -19.4852523804, 19.4852523804, -19.5788326263, 18.4911193848, + -19.6675815582, 17.4822959900, -19.7513389587, 16.4594497681, + -19.8299636841, 15.4233045578, -19.9033393860, 14.3746337891, + -19.9713764191, 13.3142499924, -20.0340042114, 12.2430028915, + -20.0911769867, 11.1617650986, -20.1428661346, 10.0714330673, + -20.1890583038, 8.9729146957, -20.2297534943, 7.8671264648, + -20.2649650574, 6.7549881935, -20.2947063446, 5.6374182701, + -20.3190002441, 4.5153336525, -20.3378696442, 3.3896448612, + -20.3513298035, 2.2612588406, -20.3593997955, 1.1310777664, + -20.3620891571, -0.0000000000, -20.3593997955, -1.1310777664, + -20.3513298035, -2.2612588406, -20.3378696442, -3.3896448612, + -20.3190002441, -4.5153336525, -20.2947063446, -5.6374182701, + -20.2649650574, -6.7549881935, -20.2297534943, -7.8671264648, + -20.1890583038, -8.9729146957, -20.1428661346, -10.0714330673, + -20.0911769867, -11.1617650986, -20.0340042114, -12.2430028915, + -19.9713764191, -13.3142499924, -19.9033393860, -14.3746337891, + -19.8299636841, -15.4233045578, -19.7513389587, -16.4594497681, + -19.6675815582, -17.4822959900, -19.5788326263, -18.4911193848, + -19.4852523804, -19.4852523804, -19.3870315552, -20.4640884399, + -19.2843761444, -21.4270839691, -19.1775150299, -22.3737659454, + -19.0666923523, -23.3037357330, -18.9521656036, -24.2166576385, + -18.8342094421, -25.1122798920, -18.7131004333, -25.9904155731, + -18.5891189575, -26.8509502411, -18.4625568390, -27.6938343048, + -18.3336811066, -28.5190601349, -18.2027912140, -29.3267192841, + -18.0701522827, -30.1169204712, -17.1402397156, 30.2474803925, + -17.2671279907, 29.4556884766, -17.3923187256, 28.6461734772, + -17.5155487061, 27.8188114166, -17.6365318298, 26.9735183716, + -17.7549991608, 26.1102924347, -17.8706703186, 25.2291812897, + -17.9832706451, 24.3303070068, -18.0925292969, 23.4138622284, + -18.1981868744, 22.4801120758, -18.2999897003, 21.5293998718, + -18.3977069855, 20.5621433258, -18.4911193848, 19.5788326263, + -18.5800323486, 18.5800323486, -18.6642704010, 17.5663719177, + -18.7436866760, 16.5385475159, -18.8181533813, 15.4973020554, + -18.8875694275, 14.4434366226, -18.9518623352, 13.3777856827, + -19.0109767914, 12.3012199402, -19.0648784637, 11.2146348953, + -19.1135578156, 10.1189422607, -19.1570129395, 9.0150651932, + -19.1952610016, 7.9039311409, -19.2283229828, 6.7864665985, + -19.2562274933, 5.6635961533, -19.2790031433, 4.5362362862, + -19.2966842651, 3.4052970409, -19.3092918396, 2.2716813087, + -19.3168468475, 1.1362851858, -19.3193645477, -0.0000000000, + -19.3168468475, -1.1362851858, -19.3092918396, -2.2716813087, + -19.2966842651, -3.4052970409, -19.2790031433, -4.5362362862, + -19.2562274933, -5.6635961533, -19.2283229828, -6.7864665985, + -19.1952610016, -7.9039311409, -19.1570129395, -9.0150651932, + -19.1135578156, -10.1189422607, -19.0648784637, -11.2146348953, + -19.0109767914, -12.3012199402, -18.9518623352, -13.3777856827, + -18.8875694275, -14.4434366226, -18.8181533813, -15.4973020554, + -18.7436866760, -16.5385475159, -18.6642704010, -17.5663719177, + -18.5800323486, -18.5800323486, -18.4911193848, -19.5788326263, + -18.3977069855, -20.5621433258, -18.2999897003, -21.5293998718, + -18.1981868744, -22.4801120758, -18.0925292969, -23.4138622284, + -17.9832706451, -24.3303070068, -17.8706703186, -25.2291812897, + -17.7549991608, -26.1102924347, -17.6365318298, -26.9735183716, + -17.5155487061, -27.8188114166, -17.3923187256, -28.6461734772, + -17.2671279907, -29.4556884766, -17.1402397156, -30.2474803925, + -16.1984691620, 30.3721294403, -16.3193359375, 29.5787963867, + -16.4385623932, 28.7674865723, -16.5558776855, 27.9380435944, + -16.6710205078, 27.0904083252, -16.7837219238, 26.2245635986, + -16.8937091827, 25.3405628204, -17.0007152557, 24.4385280609, + -17.1044807434, 23.5186595917, -17.2047519684, 22.5812358856, + -17.3012905121, 21.6266136169, -17.3938732147, 20.6552238464, + -17.4822959900, 19.6675815582, -17.5663719177, 18.6642704010, + -17.6459465027, 17.6459465027, -17.7208786011, 16.6133232117, + -17.7910594940, 15.5671777725, -17.8564052582, 14.5083284378, + -17.9168510437, 13.4376392365, -17.9723625183, 12.3559989929, + -18.0229206085, 11.2643251419, -18.0685253143, 10.1635446548, + -18.1091899872, 9.0545949936, -18.1449451447, 7.9384136200, + -18.1758213043, 6.8159332275, -18.2018604279, 5.6880812645, + -18.2230987549, 4.5557746887, -18.2395725250, 3.4199199677, + -18.2513160706, 2.2814145088, -18.2583522797, 1.1411470175, + -18.2606964111, -0.0000000000, -18.2583522797, -1.1411470175, + -18.2513160706, -2.2814145088, -18.2395725250, -3.4199199677, + -18.2230987549, -4.5557746887, -18.2018604279, -5.6880812645, + -18.1758213043, -6.8159332275, -18.1449451447, -7.9384136200, + -18.1091899872, -9.0545949936, -18.0685253143, -10.1635446548, + -18.0229206085, -11.2643251419, -17.9723625183, -12.3559989929, + -17.9168510437, -13.4376392365, -17.8564052582, -14.5083284378, + -17.7910594940, -15.5671777725, -17.7208786011, -16.6133232117, + -17.6459465027, -17.6459465027, -17.5663719177, -18.6642704010, + -17.4822959900, -19.6675815582, -17.3938732147, -20.6552238464, + -17.3012905121, -21.6266136169, -17.2047519684, -22.5812358856, + -17.1044807434, -23.5186595917, -17.0007152557, -24.4385280609, + -16.8937091827, -25.3405628204, -16.7837219238, -26.2245635986, + -16.6710205078, -27.0904083252, -16.5558776855, -27.9380435944, + -16.4385623932, -28.7674865723, -16.3193359375, -29.5787963867, + -16.1984691620, -30.3721294403, -15.2453002930, 30.4906005859, + -15.3598909378, 29.6957893372, -15.4728860855, 28.8827209473, + -15.5840415955, 28.0512752533, -15.6930990219, 27.2013721466, + -15.7997951508, 26.3329906464, -15.9038677216, 25.4461898804, + -16.0050601959, 24.5410938263, -16.1031227112, 23.6179122925, + -16.1978111267, 22.6769371033, -16.2889003754, 21.7185325623, + -16.3761768341, 20.7431564331, -16.4594497681, 19.7513389587, + -16.5385475159, 18.7436866760, -16.6133232117, 17.7208786011, + -16.6836566925, 16.6836566925, -16.7494506836, 15.6328210831, + -16.8106346130, 14.5692167282, -16.8671627045, 13.4937295914, + -16.9190063477, 12.4072723389, -16.9661674500, 11.3107776642, + -17.0086555481, 10.2051935196, -17.0464992523, 9.0914659500, + -17.0797348022, 7.9705429077, -17.1084079742, 6.8433632851, + -17.1325664520, 5.7108559608, -17.1522560120, 4.5739350319, + -17.1675205231, 3.4335041046, -17.1783962250, 2.2904527187, + -17.1849098206, 1.1456606388, -17.1870784760, -0.0000000000, + -17.1849098206, -1.1456606388, -17.1783962250, -2.2904527187, + -17.1675205231, -3.4335041046, -17.1522560120, -4.5739350319, + -17.1325664520, -5.7108559608, -17.1084079742, -6.8433632851, + -17.0797348022, -7.9705429077, -17.0464992523, -9.0914659500, + -17.0086555481, -10.2051935196, -16.9661674500, -11.3107776642, + -16.9190063477, -12.4072723389, -16.8671627045, -13.4937295914, + -16.8106346130, -14.5692167282, -16.7494506836, -15.6328210831, + -16.6836566925, -16.6836566925, -16.6133232117, -17.7208786011, + -16.5385475159, -18.7436866760, -16.4594497681, -19.7513389587, + -16.3761768341, -20.7431564331, -16.2889003754, -21.7185325623, + -16.1978111267, -22.6769371033, -16.1031227112, -23.6179122925, + -16.0050601959, -24.5410938263, -15.9038677216, -25.4461898804, + -15.7997951508, -26.3329906464, -15.6930990219, -27.2013721466, + -15.5840415955, -28.0512752533, -15.4728860855, -28.8827209473, + -15.3598909378, -29.6957893372, -15.2453002930, -30.4906005859, + -14.2812309265, 30.6026382446, -14.3892917633, 29.8063888550, + -14.4958209991, 28.9916419983, -14.6005821228, 28.1582660675, + -14.7033243179, 27.3061733246, -14.8037958145, 26.4353485107, + -14.9017429352, 25.5458450317, -14.9969215393, 24.6378002167, + -15.0890903473, 23.7114276886, -15.1780204773, 22.7670307159, + -15.2634954453, 21.8049926758, -15.3453159332, 20.8257865906, + -15.4233045578, 19.8299636841, -15.4973020554, 18.8181533813, + -15.5671777725, 17.7910594940, -15.6328210831, 16.7494506836, + -15.6941518784, 15.6941518784, -15.7511110306, 14.6260318756, + -15.8036670685, 13.5460004807, -15.8518075943, 12.4549913406, + -15.8955411911, 11.3539581299, -15.9348936081, 10.2438602448, + -15.9699020386, 9.1256580353, -16.0006141663, 8.0003070831, + -16.0270824432, 6.8687496185, -16.0493621826, 5.7319149971, + -16.0675067902, 4.5907158852, -16.0815639496, 3.4460492134, + -16.0915718079, 2.2987961769, -16.0975666046, 1.1498261690, + -16.0995616913, -0.0000000000, -16.0975666046, -1.1498261690, + -16.0915718079, -2.2987961769, -16.0815639496, -3.4460492134, + -16.0675067902, -4.5907158852, -16.0493621826, -5.7319149971, + -16.0270824432, -6.8687496185, -16.0006141663, -8.0003070831, + -15.9699020386, -9.1256580353, -15.9348936081, -10.2438602448, + -15.8955411911, -11.3539581299, -15.8518075943, -12.4549913406, + -15.8036670685, -13.5460004807, -15.7511110306, -14.6260318756, + -15.6941518784, -15.6941518784, -15.6328210831, -16.7494506836, + -15.5671777725, -17.7910594940, -15.4973020554, -18.8181533813, + -15.4233045578, -19.8299636841, -15.3453159332, -20.8257865906, + -15.2634954453, -21.8049926758, -15.1780204773, -22.7670307159, + -15.0890903473, -23.7114276886, -14.9969215393, -24.6378002167, + -14.9017429352, -25.5458450317, -14.8037958145, -26.4353485107, + -14.7033243179, -27.3061733246, -14.6005821228, -28.1582660675, + -14.4958209991, -28.9916419983, -14.3892917633, -29.8063888550, + -14.2812309265, -30.6026382446, -13.3068008423, 30.7080020905, + -13.4081001282, 29.9103755951, -13.5079383850, 29.0940208435, + -13.6060838699, 28.2587909698, -13.7022991180, 27.4045982361, + -13.7963418961, 26.5314273834, -13.8879728317, 25.6393356323, + -13.9769563675, 24.7284622192, -14.0630645752, 23.7990322113, + -14.1460809708, 22.8513603210, -14.2258014679, 21.8858470917, + -14.3020410538, 20.9029827118, -14.3746337891, 19.9033393860, + -14.4434366226, 18.8875694275, -14.5083284378, 17.8564052582, + -14.5692167282, 16.8106346130, -14.6260318756, 15.7511110306, + -14.6787290573, 14.6787290573, -14.7272872925, 13.5944185257, + -14.7717065811, 12.4991369247, -14.8120079041, 11.3938522339, + -14.8482246399, 10.2795400620, -14.8804044724, 9.1571722031, + -14.9086036682, 8.0277090073, -14.9328804016, 6.8920984268, + -14.9532957077, 5.7512674332, -14.9699077606, 4.6061258316, + -14.9827699661, 3.4575622082, -14.9919233322, 2.3064496517, + -14.9974021912, 1.1536463499, -14.9992265701, -0.0000000000, + -14.9974021912, -1.1536463499, -14.9919233322, -2.3064496517, + -14.9827699661, -3.4575622082, -14.9699077606, -4.6061258316, + -14.9532957077, -5.7512674332, -14.9328804016, -6.8920984268, + -14.9086036682, -8.0277090073, -14.8804044724, -9.1571722031, + -14.8482246399, -10.2795400620, -14.8120079041, -11.3938522339, + -14.7717065811, -12.4991369247, -14.7272872925, -13.5944185257, + -14.6787290573, -14.6787290573, -14.6260318756, -15.7511110306, + -14.5692167282, -16.8106346130, -14.5083284378, -17.8564052582, + -14.4434366226, -18.8875694275, -14.3746337891, -19.9033393860, + -14.3020410538, -20.9029827118, -14.2258014679, -21.8858470917, + -14.1460809708, -22.8513603210, -14.0630645752, -23.7990322113, + -13.9769563675, -24.7284622192, -13.8879728317, -25.6393356323, + -13.7963418961, -26.5314273834, -13.7022991180, -27.4045982361, + -13.6060838699, -28.2587909698, -13.5079383850, -29.0940208435, + -13.4081001282, -29.9103755951, -13.3068008423, -30.7080020905, + -12.3225889206, 30.8064727783, -12.4169082642, 30.0075283051, + -12.5098447800, 29.1896381378, -12.6011743546, 28.3526420593, + -12.6906690598, 27.4964504242, -12.7781000137, 26.6210422516, + -12.8632402420, 25.7264804840, -12.9458684921, 24.8129138947, + -13.0257682800, 23.8805751801, -13.1027374268, 22.9297904968, + -13.1765851974, 21.9609756470, -13.2471399307, 20.9746379852, + -13.3142499924, 19.9713764191, -13.3777856827, 18.9518623352, + -13.4376392365, 17.9168510437, -13.4937295914, 16.8671627045, + -13.5460004807, 15.8036670685, -13.5944185257, 14.7272872925, + -13.6389751434, 13.6389751434, -13.6796798706, 12.5397062302, + -13.7165613174, 11.4304676056, -13.7496623993, 10.3122472763, + -13.7790384293, 9.1860256195, -13.8047494888, 8.0527706146, + -13.8268623352, 6.9134311676, -13.8454399109, 5.7689332962, + -13.8605442047, 4.6201815605, -13.8722305298, 3.4680576324, + -13.8805427551, 2.3134238720, -13.8855171204, 1.1571264267, + -13.8871726990, -0.0000000000, -13.8855171204, -1.1571264267, + -13.8805427551, -2.3134238720, -13.8722305298, -3.4680576324, + -13.8605442047, -4.6201815605, -13.8454399109, -5.7689332962, + -13.8268623352, -6.9134311676, -13.8047494888, -8.0527706146, + -13.7790384293, -9.1860256195, -13.7496623993, -10.3122472763, + -13.7165613174, -11.4304676056, -13.6796798706, -12.5397062302, + -13.6389751434, -13.6389751434, -13.5944185257, -14.7272872925, + -13.5460004807, -15.8036670685, -13.4937295914, -16.8671627045, + -13.4376392365, -17.9168510437, -13.3777856827, -18.9518623352, + -13.3142499924, -19.9713764191, -13.2471399307, -20.9746379852, + -13.1765851974, -21.9609756470, -13.1027374268, -22.9297904968, + -13.0257682800, -23.8805751801, -12.9458684921, -24.8129138947, + -12.8632402420, -25.7264804840, -12.7781000137, -26.6210422516, + -12.6906690598, -27.4964504242, -12.6011743546, -28.3526420593, + -12.5098447800, -29.1896381378, -12.4169082642, -30.0075283051, + -12.3225889206, -30.8064727783, -11.3292007446, 30.8978214264, + -11.4163465500, 30.0976409912, -11.5021886826, 29.2782974243, + -11.5865163803, 28.4396305084, -11.6691150665, 27.5815448761, + -11.7497692108, 26.7040214539, -11.8282670975, 25.8071269989, + -11.9043979645, 24.8910140991, -11.9779634476, 23.9559268951, + -12.0487728119, 23.0022029877, -12.1166515350, 22.0302753448, + -12.1814413071, 21.0406703949, -12.2430028915, 20.0340042114, + -12.3012199402, 19.0109767914, -12.3559989929, 17.9723625183, + -12.4072723389, 16.9190063477, -12.4549913406, 15.8518075943, + -12.4991369247, 14.7717065811, -12.5397062302, 13.6796798706, + -12.5767202377, 12.5767202377, -12.6102132797, 11.4638299942, + -12.6402349472, 10.3420104980, -12.6668453217, 9.2122516632, + -12.6901092529, 8.0755243301, -12.7100963593, 6.9327797890, + -12.7268714905, 5.7849416733, -12.7405004501, 4.6329092979, + -12.7510375977, 3.4775555134, -12.7585287094, 2.3197324276, + -12.7630100250, 1.1602735519, -12.7645006180, -0.0000000000, + -12.7630100250, -1.1602735519, -12.7585287094, -2.3197324276, + -12.7510375977, -3.4775555134, -12.7405004501, -4.6329092979, + -12.7268714905, -5.7849416733, -12.7100963593, -6.9327797890, + -12.6901092529, -8.0755243301, -12.6668453217, -9.2122516632, + -12.6402349472, -10.3420104980, -12.6102132797, -11.4638299942, + -12.5767202377, -12.5767202377, -12.5397062302, -13.6796798706, + -12.4991369247, -14.7717065811, -12.4549913406, -15.8518075943, + -12.4072723389, -16.9190063477, -12.3559989929, -17.9723625183, + -12.3012199402, -19.0109767914, -12.2430028915, -20.0340042114, + -12.1814413071, -21.0406703949, -12.1166515350, -22.0302753448, + -12.0487728119, -23.0022029877, -11.9779634476, -23.9559268951, + -11.9043979645, -24.8910140991, -11.8282670975, -25.8071269989, + -11.7497692108, -26.7040214539, -11.6691150665, -27.5815448761, + -11.5865163803, -28.4396305084, -11.5021886826, -29.2782974243, + -11.4163465500, -30.0976409912, -11.3292007446, -30.8978214264, + -10.3272886276, 30.9818668365, -10.4070777893, 30.1805267334, + -10.4856500626, 29.3598213196, -10.5628099442, 28.5195865631, + -10.6383552551, 27.6597232819, -10.7120866776, 26.7802181244, + -10.7838068008, 25.8811359406, -10.8533210754, 24.9626388550, + -10.9204444885, 24.0249786377, -10.9850025177, 23.0685062408, + -11.0468349457, 22.0936698914, -11.1057968140, 21.1010150909, + -11.1617650986, 20.0911769867, -11.2146348953, 19.0648784637, + -11.2643251419, 18.0229206085, -11.3107776642, 16.9661674500, + -11.3539581299, 15.8955411911, -11.3938522339, 14.8120079041, + -11.4304676056, 13.7165613174, -11.4638299942, 12.6102132797, + -11.4939804077, 11.4939804077, -11.5209722519, 10.3688755035, + -11.5448684692, 9.2358942032, -11.5657358170, 8.0960149765, + -11.5836448669, 6.9501867294, -11.5986623764, 5.7993311882, + -11.6108531952, 4.6443414688, -11.6202726364, 3.4860816002, + -11.6269655228, 2.3253931999, -11.6309680939, 1.1630967855, + -11.6322994232, -0.0000000000, -11.6309680939, -1.1630967855, + -11.6269655228, -2.3253931999, -11.6202726364, -3.4860816002, + -11.6108531952, -4.6443414688, -11.5986623764, -5.7993311882, + -11.5836448669, -6.9501867294, -11.5657358170, -8.0960149765, + -11.5448684692, -9.2358942032, -11.5209722519, -10.3688755035, + -11.4939804077, -11.4939804077, -11.4638299942, -12.6102132797, + -11.4304676056, -13.7165613174, -11.3938522339, -14.8120079041, + -11.3539581299, -15.8955411911, -11.3107776642, -16.9661674500, + -11.2643251419, -18.0229206085, -11.2146348953, -19.0648784637, + -11.1617650986, -20.0911769867, -11.1057968140, -21.1010150909, + -11.0468349457, -22.0936698914, -10.9850025177, -23.0685062408, + -10.9204444885, -24.0249786377, -10.8533210754, -24.9626388550, + -10.7838068008, -25.8811359406, -10.7120866776, -26.7802181244, + -10.6383552551, -27.6597232819, -10.5628099442, -28.5195865631, + -10.4856500626, -29.3598213196, -10.4070777893, -30.1805267334, + -10.3272886276, -30.9818668365, -9.3175296783, 31.0584316254, + -9.3897972107, 30.2560138702, -9.4609432220, 29.4340457916, + -9.5307846069, 28.5923538208, -9.5991382599, 27.7308425903, + -9.6658191681, 26.8494987488, -9.7306461334, 25.9483890533, + -9.7934408188, 25.0276813507, -9.8540334702, 24.0876388550, + -9.9122667313, 23.1286220551, -9.9679937363, 22.1510982513, + -10.0210866928, 21.1556262970, -10.0714330673, 20.1428661346, + -10.1189422607, 19.1135578156, -10.1635446548, 18.0685253143, + -10.2051935196, 17.0086555481, -10.2438602448, 15.9348936081, + -10.2795400620, 14.8482246399, -10.3122472763, 13.7496623993, + -10.3420104980, 12.6402349472, -10.3688755035, 11.5209722519, + -10.3928966522, 10.3928966522, -10.4141378403, 9.2570114136, + -10.4326667786, 8.1142959595, -10.4485530853, 6.9657020569, + -10.4618625641, 5.8121461868, -10.4726581573, 4.6545147896, + -10.4809942245, 3.4936647415, -10.4869146347, 2.3304255009, + -10.4904632568, 1.1656070948, -10.4916400909, -0.0000000000, + -10.4904632568, -1.1656070948, -10.4869146347, -2.3304255009, + -10.4809942245, -3.4936647415, -10.4726581573, -4.6545147896, + -10.4618625641, -5.8121461868, -10.4485530853, -6.9657020569, + -10.4326667786, -8.1142959595, -10.4141378403, -9.2570114136, + -10.3928966522, -10.3928966522, -10.3688755035, -11.5209722519, + -10.3420104980, -12.6402349472, -10.3122472763, -13.7496623993, + -10.2795400620, -14.8482246399, -10.2438602448, -15.9348936081, + -10.2051935196, -17.0086555481, -10.1635446548, -18.0685253143, + -10.1189422607, -19.1135578156, -10.0714330673, -20.1428661346, + -10.0210866928, -21.1556262970, -9.9679937363, -22.1510982513, + -9.9122667313, -23.1286220551, -9.8540334702, -24.0876388550, + -9.7934408188, -25.0276813507, -9.7306461334, -25.9483890533, + -9.6658191681, -26.8494987488, -9.5991382599, -27.7308425903, + -9.5307846069, -28.5923538208, -9.4609432220, -29.4340457916, + -9.3897972107, -30.2560138702, -9.3175296783, -31.0584316254, + -8.3006286621, 31.1273555756, -8.3652276993, 30.3239517212, + -8.4288072586, 29.5008239746, -8.4912004471, 28.6578006744, + -8.5522403717, 27.7947807312, -8.6117601395, 26.9117507935, + -8.6695947647, 26.0087852478, -8.7255840302, 25.0860557556, + -8.7795763016, 24.1438331604, -8.8314266205, 23.1824951172, + -8.8810071945, 22.2025184631, -8.9282026291, 21.2044811249, + -8.9729146957, 20.1890583038, -9.0150651932, 19.1570129395, + -9.0545949936, 18.1091899872, -9.0914659500, 17.0464992523, + -9.1256580353, 15.9699020386, -9.1571722031, 14.8804044724, + -9.1860256195, 13.7790384293, -9.2122516632, 12.6668453217, + -9.2358942032, 11.5448684692, -9.2570114136, 10.4141378403, + -9.2756633759, 9.2756633759, -9.2919178009, 8.1304273605, + -9.3058395386, 6.9793796539, -9.3174943924, 5.8234338760, + -9.3269481659, 4.6634740829, -9.3342361450, 3.5003383160, + -9.3394098282, 2.3348524570, -9.3425016403, 1.1678127050, + -9.3435297012, -0.0000000000, -9.3425016403, -1.1678127050, + -9.3394098282, -2.3348524570, -9.3342361450, -3.5003383160, + -9.3269481659, -4.6634740829, -9.3174943924, -5.8234338760, + -9.3058395386, -6.9793796539, -9.2919178009, -8.1304273605, + -9.2756633759, -9.2756633759, -9.2570114136, -10.4141378403, + -9.2358942032, -11.5448684692, -9.2122516632, -12.6668453217, + -9.1860256195, -13.7790384293, -9.1571722031, -14.8804044724, + -9.1256580353, -15.9699020386, -9.0914659500, -17.0464992523, + -9.0545949936, -18.1091899872, -9.0150651932, -19.1570129395, + -8.9729146957, -20.1890583038, -8.9282026291, -21.2044811249, + -8.8810071945, -22.2025184631, -8.8314266205, -23.1824951172, + -8.7795763016, -24.1438331604, -8.7255840302, -25.0860557556, + -8.6695947647, -26.0087852478, -8.6117601395, -26.9117507935, + -8.5522403717, -27.7947807312, -8.4912004471, -28.6578006744, + -8.4288072586, -29.5008239746, -8.3652276993, -30.3239517212, + -8.3006286621, -31.1273555756, -7.2773165703, 31.1884994507, + -7.3341183662, 30.3842048645, -7.3900084496, 29.5600337982, + -7.4448385239, 28.7158069611, -7.4984607697, 27.8514251709, + -7.5507259369, 26.9668769836, -7.6014866829, 26.0622406006, + -7.6506013870, 25.1376914978, -7.6979346275, 24.1935100555, + -7.7433609962, 23.2300834656, -7.7867655754, 22.2479019165, + -7.8280491829, 21.2475624084, -7.8671264648, 20.2297534943, + -7.9039311409, 19.1952610016, -7.9384136200, 18.1449451447, + -7.9705429077, 17.0797348022, -8.0003070831, 16.0006141663, + -8.0277090073, 14.9086036682, -8.0527706146, 13.8047494888, + -8.0755243301, 12.6901092529, -8.0960149765, 11.5657358170, + -8.1142959595, 10.4326667786, -8.1304273605, 9.2919178009, + -8.1444711685, 8.1444711685, -8.1564893723, 6.9912767410, + -8.1665477753, 5.8332486153, -8.1746883392, 4.6712508202, + -8.1809673309, 3.5061287880, -8.1854228973, 2.3386921883, + -8.1880846024, 1.1697263718, -8.1889696121, -0.0000000000, + -8.1880846024, -1.1697263718, -8.1854228973, -2.3386921883, + -8.1809673309, -3.5061287880, -8.1746883392, -4.6712508202, + -8.1665477753, -5.8332486153, -8.1564893723, -6.9912767410, + -8.1444711685, -8.1444711685, -8.1304273605, -9.2919178009, + -8.1142959595, -10.4326667786, -8.0960149765, -11.5657358170, + -8.0755243301, -12.6901092529, -8.0527706146, -13.8047494888, + -8.0277090073, -14.9086036682, -8.0003070831, -16.0006141663, + -7.9705429077, -17.0797348022, -7.9384136200, -18.1449451447, + -7.9039311409, -19.1952610016, -7.8671264648, -20.2297534943, + -7.8280491829, -21.2475624084, -7.7867655754, -22.2479019165, + -7.7433609962, -23.2300834656, -7.6979346275, -24.1935100555, + -7.6506013870, -25.1376914978, -7.6014866829, -26.0622406006, + -7.5507259369, -26.9668769836, -7.4984607697, -27.8514251709, + -7.4448385239, -28.7158069611, -7.3900084496, -29.5600337982, + -7.3341183662, -30.3842048645, -7.2773165703, -31.1884994507, + -6.2483472824, 31.2417373657, -6.2972393036, 30.4366550446, + -6.3453345299, 29.6115608215, -6.3925046921, 28.7662715912, + -6.4386205673, 27.9006881714, -6.4835519791, 27.0147991180, + -6.5271716118, 26.1086864471, -6.5693559647, 25.1825313568, + -6.6099877357, 24.2366218567, -6.6489582062, 23.2713546753, + -6.6861701012, 22.2872333527, -6.7215371132, 21.2848682404, + -6.7549881935, 20.2649650574, -6.7864665985, 19.2283229828, + -6.8159332275, 18.1758213043, -6.8433632851, 17.1084079742, + -6.8687496185, 16.0270824432, -6.8920984268, 14.9328804016, + -6.9134311676, 13.8268623352, -6.9327797890, 12.7100963593, + -6.9501867294, 11.5836448669, -6.9657020569, 10.4485530853, + -6.9793796539, 9.3058395386, -6.9912767410, 8.1564893723, + -7.0014543533, 7.0014543533, -7.0099563599, 5.8416299820, + -7.0168380737, 4.6778917313, -7.0221428871, 3.5110714436, + -7.0259060860, 2.3419685364, -7.0281534195, 1.1713588238, + -7.0289006233, -0.0000000000, -7.0281534195, -1.1713588238, + -7.0259060860, -2.3419685364, -7.0221428871, -3.5110714436, + -7.0168380737, -4.6778917313, -7.0099563599, -5.8416299820, + -7.0014543533, -7.0014543533, -6.9912767410, -8.1564893723, + -6.9793796539, -9.3058395386, -6.9657020569, -10.4485530853, + -6.9501867294, -11.5836448669, -6.9327797890, -12.7100963593, + -6.9134311676, -13.8268623352, -6.8920984268, -14.9328804016, + -6.8687496185, -16.0270824432, -6.8433632851, -17.1084079742, + -6.8159332275, -18.1758213043, -6.7864665985, -19.2283229828, + -6.7549881935, -20.2649650574, -6.7215371132, -21.2848682404, + -6.6861701012, -22.2872333527, -6.6489582062, -23.2713546753, + -6.6099877357, -24.2366218567, -6.5693559647, -25.1825313568, + -6.5271716118, -26.1086864471, -6.4835519791, -27.0147991180, + -6.4386205673, -27.9006881714, -6.3925046921, -28.7662715912, + -6.3453345299, -29.6115608215, -6.2972393036, -30.4366550446, + -6.2483472824, -31.2417373657, -5.2144937515, 31.2869625092, + -5.2553801537, 30.4812030792, -5.2955918312, 29.6553134918, + -5.3350205421, 28.8091106415, -5.3735561371, 27.9424934387, + -5.4110903740, 27.0554504395, -5.4475140572, 26.1480674744, + -5.4827241898, 25.2205314636, -5.5166220665, 24.2731380463, + -5.5491170883, 23.3062915802, -5.5801262856, 22.3205051422, + -5.6095800400, 21.3164043427, -5.6374182701, 20.2947063446, + -5.6635961533, 19.2562274933, -5.6880812645, 18.2018604279, + -5.7108559608, 17.1325664520, -5.7319149971, 16.0493621826, + -5.7512674332, 14.9532957077, -5.7689332962, 13.8454399109, + -5.7849416733, 12.7268714905, -5.7993311882, 11.5986623764, + -5.8121461868, 10.4618625641, -5.8234338760, 9.3174943924, + -5.8332486153, 8.1665477753, -5.8416299820, 7.0099563599, + -5.8486313820, 5.8486313820, -5.8542957306, 4.6834368706, + -5.8586602211, 3.5151960850, -5.8617553711, 2.3447020054, + -5.8636031151, 1.1727205515, -5.8642172813, -0.0000000000, + -5.8636031151, -1.1727205515, -5.8617553711, -2.3447020054, + -5.8586602211, -3.5151960850, -5.8542957306, -4.6834368706, + -5.8486313820, -5.8486313820, -5.8416299820, -7.0099563599, + -5.8332486153, -8.1665477753, -5.8234338760, -9.3174943924, + -5.8121461868, -10.4618625641, -5.7993311882, -11.5986623764, + -5.7849416733, -12.7268714905, -5.7689332962, -13.8454399109, + -5.7512674332, -14.9532957077, -5.7319149971, -16.0493621826, + -5.7108559608, -17.1325664520, -5.6880812645, -18.2018604279, + -5.6635961533, -19.2562274933, -5.6374182701, -20.2947063446, + -5.6095800400, -21.3164043427, -5.5801262856, -22.3205051422, + -5.5491170883, -23.3062915802, -5.5166220665, -24.2731380463, + -5.4827241898, -25.2205314636, -5.4475140572, -26.1480674744, + -5.4110903740, -27.0554504395, -5.3735561371, -27.9424934387, + -5.3350205421, -28.8091106415, -5.2955918312, -29.6553134918, + -5.2553801537, -30.4812030792, -5.2144937515, -31.2869625092, + -4.1765446663, 31.3240852356, -4.2093467712, 30.5177650452, + -4.2416019440, 29.6912136078, -4.2732224464, 28.8442516327, + -4.3041195869, 27.9767761230, -4.3342041969, 27.0887775421, + -4.3633904457, 26.1803417206, -4.3915929794, 25.2516613007, + -4.4187340736, 24.3030376434, -4.4447393417, 23.3348808289, + -4.4695439339, 22.3477191925, -4.4930906296, 21.3421802521, + -4.5153336525, 20.3190002441, -4.5362362862, 19.2790031433, + -4.5557746887, 18.2230987549, -4.5739350319, 17.1522560120, + -4.5907158852, 16.0675067902, -4.6061258316, 14.9699077606, + -4.6201815605, 13.8605442047, -4.6329092979, 12.7405004501, + -4.6443414688, 11.6108531952, -4.6545147896, 10.4726581573, + -4.6634740829, 9.3269481659, -4.6712508202, 8.1746883392, + -4.6778917313, 7.0168380737, -4.6834368706, 5.8542957306, + -4.6879205704, 4.6879205704, -4.6913738251, 3.5185303688, + -4.6938219070, 2.3469109535, -4.6952834129, 1.1738208532, + -4.6957693100, -0.0000000000, -4.6952834129, -1.1738208532, + -4.6938219070, -2.3469109535, -4.6913738251, -3.5185303688, + -4.6879205704, -4.6879205704, -4.6834368706, -5.8542957306, + -4.6778917313, -7.0168380737, -4.6712508202, -8.1746883392, + -4.6634740829, -9.3269481659, -4.6545147896, -10.4726581573, + -4.6443414688, -11.6108531952, -4.6329092979, -12.7405004501, + -4.6201815605, -13.8605442047, -4.6061258316, -14.9699077606, + -4.5907158852, -16.0675067902, -4.5739350319, -17.1522560120, + -4.5557746887, -18.2230987549, -4.5362362862, -19.2790031433, + -4.5153336525, -20.3190002441, -4.4930906296, -21.3421802521, + -4.4695439339, -22.3477191925, -4.4447393417, -23.3348808289, + -4.4187340736, -24.3030376434, -4.3915929794, -25.2516613007, + -4.3633904457, -26.1803417206, -4.3342041969, -27.0887775421, + -4.3041195869, -27.9767761230, -4.2732224464, -28.8442516327, + -4.2416019440, -29.6912136078, -4.2093467712, -30.5177650452, + -4.1765446663, -31.3240852356, -3.1353034973, 31.3530349731, + -3.1599588394, 30.5462703705, -3.1842000484, 29.7192001343, + -3.2079601288, 28.8716411591, -3.2311718464, 28.0034904480, + -3.2537686825, 27.1147384644, -3.2756843567, 26.2054748535, + -3.2968556881, 25.2758941650, -3.3172230721, 24.3263034821, + -3.3367314339, 23.3571205139, -3.3553314209, 22.3688755035, + -3.3729808331, 21.3622112274, -3.3896448612, 20.3378696442, + -3.4052970409, 19.2966842651, -3.4199199677, 18.2395725250, + -3.4335041046, 17.1675205231, -3.4460492134, 16.0815639496, + -3.4575622082, 14.9827699661, -3.4680576324, 13.8722305298, + -3.4775555134, 12.7510375977, -3.4860816002, 11.6202726364, + -3.4936647415, 10.4809942245, -3.5003383160, 9.3342361450, + -3.5061287880, 8.1809673309, -3.5110714436, 7.0221428871, + -3.5151960850, 5.8586602211, -3.5185303688, 4.6913738251, + -3.5210976601, 3.5210976601, -3.5229170322, 2.3486113548, + -3.5240030289, 1.1746675968, -3.5243639946, -0.0000000000, + -3.5240030289, -1.1746675968, -3.5229170322, -2.3486113548, + -3.5210976601, -3.5210976601, -3.5185303688, -4.6913738251, + -3.5151960850, -5.8586602211, -3.5110714436, -7.0221428871, + -3.5061287880, -8.1809673309, -3.5003383160, -9.3342361450, + -3.4936647415, -10.4809942245, -3.4860816002, -11.6202726364, + -3.4775555134, -12.7510375977, -3.4680576324, -13.8722305298, + -3.4575622082, -14.9827699661, -3.4460492134, -16.0815639496, + -3.4335041046, -17.1675205231, -3.4199199677, -18.2395725250, + -3.4052970409, -19.2966842651, -3.3896448612, -20.3378696442, + -3.3729808331, -21.3622112274, -3.3553314209, -22.3688755035, + -3.3367314339, -23.3571205139, -3.3172230721, -24.3263034821, + -3.2968556881, -25.2758941650, -3.2756843567, -26.2054748535, + -3.2537686825, -27.1147384644, -3.2311718464, -28.0034904480, + -3.2079601288, -28.8716411591, -3.1842000484, -29.7192001343, + -3.1599588394, -30.5462703705, -3.1353034973, -31.3530349731, + -2.0915834904, 31.3737506866, -2.1080460548, 30.5666675568, + -2.1242303848, 29.7392234802, -2.1400914192, 28.8912334442, + -2.1555843353, 28.0225963593, -2.1706643105, 27.1333026886, + -2.1852869987, 26.2234420776, -2.1994099617, 25.2932147980, + -2.2129933834, 24.3429279327, -2.2260005474, 23.3730049133, + -2.2383983135, 22.3839836121, -2.2501587868, 21.3765087128, + -2.2612588406, 20.3513298035, -2.2716813087, 19.3092918396, + -2.2814145088, 18.2513160706, -2.2904527187, 17.1783962250, + -2.2987961769, 16.0915718079, -2.3064496517, 14.9919233322, + -2.3134238720, 13.8805427551, -2.3197324276, 12.7585287094, + -2.3253931999, 11.6269655228, -2.3304255009, 10.4869146347, + -2.3348524570, 9.3394098282, -2.3386921883, 8.1854228973, + -2.3419685364, 7.0259060860, -2.3447020054, 5.8617553711, + -2.3469109535, 4.6938219070, -2.3486113548, 3.5229170322, + -2.3498163223, 2.3498163223, -2.3505353928, 1.1752676964, + -2.3507742882, -0.0000000000, -2.3505353928, -1.1752676964, + -2.3498163223, -2.3498163223, -2.3486113548, -3.5229170322, + -2.3469109535, -4.6938219070, -2.3447020054, -5.8617553711, + -2.3419685364, -7.0259060860, -2.3386921883, -8.1854228973, + -2.3348524570, -9.3394098282, -2.3304255009, -10.4869146347, + -2.3253931999, -11.6269655228, -2.3197324276, -12.7585287094, + -2.3134238720, -13.8805427551, -2.3064496517, -14.9919233322, + -2.2987961769, -16.0915718079, -2.2904527187, -17.1783962250, + -2.2814145088, -18.2513160706, -2.2716813087, -19.3092918396, + -2.2612588406, -20.3513298035, -2.2501587868, -21.3765087128, + -2.2383983135, -22.3839836121, -2.2260005474, -23.3730049133, + -2.2129933834, -24.3429279327, -2.1994099617, -25.2932147980, + -2.1852869987, -26.2234420776, -2.1706643105, -27.1333026886, + -2.1555843353, -28.0225963593, -2.1400914192, -28.8912334442, + -2.1242303848, -29.7392234802, -2.1080460548, -30.5666675568, + -2.0915834904, -31.3737506866, -1.0462065935, 31.3861980438, + -1.0544456244, 30.5789222717, -1.0625447035, 29.7512512207, + -1.0704815388, 28.9030017853, -1.0782334805, 28.0340709686, + -1.0857779980, 27.1444492340, -1.0930929184, 26.2342300415, + -1.1001570225, 25.3036098480, -1.1069501638, 24.3529033661, + -1.1134541035, 23.3825359344, -1.1196522713, 22.3930454254, + -1.1255306005, 21.3850822449, -1.1310777664, 20.3593997955, + -1.1362851858, 19.3168468475, -1.1411470175, 18.2583522797, + -1.1456606388, 17.1849098206, -1.1498261690, 16.0975666046, + -1.1536463499, 14.9974021912, -1.1571264267, 13.8855171204, + -1.1602735519, 12.7630100250, -1.1630967855, 11.6309680939, + -1.1656070948, 10.4904632568, -1.1678127050, 9.3425016403, + -1.1697263718, 8.1880846024, -1.1713588238, 7.0281534195, + -1.1727205515, 5.8636031151, -1.1738208532, 4.6952834129, + -1.1746675968, 3.5240030289, -1.1752676964, 2.3505353928, + -1.1756256819, 1.1756256819, -1.1757446527, -0.0000000000, + -1.1756256819, -1.1756256819, -1.1752676964, -2.3505353928, + -1.1746675968, -3.5240030289, -1.1738208532, -4.6952834129, + -1.1727205515, -5.8636031151, -1.1713588238, -7.0281534195, + -1.1697263718, -8.1880846024, -1.1678127050, -9.3425016403, + -1.1656070948, -10.4904632568, -1.1630967855, -11.6309680939, + -1.1602735519, -12.7630100250, -1.1571264267, -13.8855171204, + -1.1536463499, -14.9974021912, -1.1498261690, -16.0975666046, + -1.1456606388, -17.1849098206, -1.1411470175, -18.2583522797, + -1.1362851858, -19.3168468475, -1.1310777664, -20.3593997955, + -1.1255306005, -21.3850822449, -1.1196522713, -22.3930454254, + -1.1134541035, -23.3825359344, -1.1069501638, -24.3529033661, + -1.1001570225, -25.3036098480, -1.0930929184, -26.2342300415, + -1.0857779980, -27.1444492340, -1.0782334805, -28.0340709686, + -1.0704815388, -28.9030017853, -1.0625447035, -29.7512512207, + -1.0544456244, -30.5789222717, -1.0462065935, -31.3861980438, + 0.0000000000, 31.3903484344, 0.0000000000, 30.5830078125, + 0.0000000000, 29.7552623749, 0.0000000000, 28.9069271088, + 0.0000000000, 28.0378971100, 0.0000000000, 27.1481666565, + 0.0000000000, 26.2378273010, 0.0000000000, 25.3070774078, + 0.0000000000, 24.3562297821, 0.0000000000, 23.3857116699, + 0.0000000000, 22.3960647583, 0.0000000000, 21.3879394531, + 0.0000000000, 20.3620891571, 0.0000000000, 19.3193645477, + 0.0000000000, 18.2606964111, 0.0000000000, 17.1870784760, + 0.0000000000, 16.0995616913, 0.0000000000, 14.9992265701, + 0.0000000000, 13.8871726990, 0.0000000000, 12.7645006180, + 0.0000000000, 11.6322994232, 0.0000000000, 10.4916400909, + 0.0000000000, 9.3435297012, 0.0000000000, 8.1889696121, + 0.0000000000, 7.0289006233, 0.0000000000, 5.8642172813, + 0.0000000000, 4.6957693100, 0.0000000000, 3.5243639946, + 0.0000000000, 2.3507742882, 0.0000000000, 1.1757446527, + 0.0000000000, -0.0000000000, 0.0000000000, -1.1757446527, + 0.0000000000, -2.3507742882, 0.0000000000, -3.5243639946, + 0.0000000000, -4.6957693100, 0.0000000000, -5.8642172813, + 0.0000000000, -7.0289006233, 0.0000000000, -8.1889696121, + 0.0000000000, -9.3435297012, 0.0000000000, -10.4916400909, + 0.0000000000, -11.6322994232, 0.0000000000, -12.7645006180, + 0.0000000000, -13.8871726990, 0.0000000000, -14.9992265701, + 0.0000000000, -16.0995616913, 0.0000000000, -17.1870784760, + 0.0000000000, -18.2606964111, 0.0000000000, -19.3193645477, + 0.0000000000, -20.3620891571, 0.0000000000, -21.3879394531, + 0.0000000000, -22.3960647583, 0.0000000000, -23.3857116699, + 0.0000000000, -24.3562297821, 0.0000000000, -25.3070774078, + 0.0000000000, -26.2378273010, 0.0000000000, -27.1481666565, + 0.0000000000, -28.0378971100, 0.0000000000, -28.9069271088, + 0.0000000000, -29.7552623749, 0.0000000000, -30.5830078125, + 0.0000000000, -31.3903484344, 1.0462065935, 31.3861980438, + 1.0544456244, 30.5789222717, 1.0625447035, 29.7512512207, + 1.0704815388, 28.9030017853, 1.0782334805, 28.0340709686, + 1.0857779980, 27.1444492340, 1.0930929184, 26.2342300415, + 1.1001570225, 25.3036098480, 1.1069501638, 24.3529033661, + 1.1134541035, 23.3825359344, 1.1196522713, 22.3930454254, + 1.1255306005, 21.3850822449, 1.1310777664, 20.3593997955, + 1.1362851858, 19.3168468475, 1.1411470175, 18.2583522797, + 1.1456606388, 17.1849098206, 1.1498261690, 16.0975666046, + 1.1536463499, 14.9974021912, 1.1571264267, 13.8855171204, + 1.1602735519, 12.7630100250, 1.1630967855, 11.6309680939, + 1.1656070948, 10.4904632568, 1.1678127050, 9.3425016403, + 1.1697263718, 8.1880846024, 1.1713588238, 7.0281534195, + 1.1727205515, 5.8636031151, 1.1738208532, 4.6952834129, + 1.1746675968, 3.5240030289, 1.1752676964, 2.3505353928, + 1.1756256819, 1.1756256819, 1.1757446527, -0.0000000000, + 1.1756256819, -1.1756256819, 1.1752676964, -2.3505353928, + 1.1746675968, -3.5240030289, 1.1738208532, -4.6952834129, + 1.1727205515, -5.8636031151, 1.1713588238, -7.0281534195, + 1.1697263718, -8.1880846024, 1.1678127050, -9.3425016403, + 1.1656070948, -10.4904632568, 1.1630967855, -11.6309680939, + 1.1602735519, -12.7630100250, 1.1571264267, -13.8855171204, + 1.1536463499, -14.9974021912, 1.1498261690, -16.0975666046, + 1.1456606388, -17.1849098206, 1.1411470175, -18.2583522797, + 1.1362851858, -19.3168468475, 1.1310777664, -20.3593997955, + 1.1255306005, -21.3850822449, 1.1196522713, -22.3930454254, + 1.1134541035, -23.3825359344, 1.1069501638, -24.3529033661, + 1.1001570225, -25.3036098480, 1.0930929184, -26.2342300415, + 1.0857779980, -27.1444492340, 1.0782334805, -28.0340709686, + 1.0704815388, -28.9030017853, 1.0625447035, -29.7512512207, + 1.0544456244, -30.5789222717, 1.0462065935, -31.3861980438, + 2.0915834904, 31.3737506866, 2.1080460548, 30.5666675568, + 2.1242303848, 29.7392234802, 2.1400914192, 28.8912334442, + 2.1555843353, 28.0225963593, 2.1706643105, 27.1333026886, + 2.1852869987, 26.2234420776, 2.1994099617, 25.2932147980, + 2.2129933834, 24.3429279327, 2.2260005474, 23.3730049133, + 2.2383983135, 22.3839836121, 2.2501587868, 21.3765087128, + 2.2612588406, 20.3513298035, 2.2716813087, 19.3092918396, + 2.2814145088, 18.2513160706, 2.2904527187, 17.1783962250, + 2.2987961769, 16.0915718079, 2.3064496517, 14.9919233322, + 2.3134238720, 13.8805427551, 2.3197324276, 12.7585287094, + 2.3253931999, 11.6269655228, 2.3304255009, 10.4869146347, + 2.3348524570, 9.3394098282, 2.3386921883, 8.1854228973, + 2.3419685364, 7.0259060860, 2.3447020054, 5.8617553711, + 2.3469109535, 4.6938219070, 2.3486113548, 3.5229170322, + 2.3498163223, 2.3498163223, 2.3505353928, 1.1752676964, + 2.3507742882, -0.0000000000, 2.3505353928, -1.1752676964, + 2.3498163223, -2.3498163223, 2.3486113548, -3.5229170322, + 2.3469109535, -4.6938219070, 2.3447020054, -5.8617553711, + 2.3419685364, -7.0259060860, 2.3386921883, -8.1854228973, + 2.3348524570, -9.3394098282, 2.3304255009, -10.4869146347, + 2.3253931999, -11.6269655228, 2.3197324276, -12.7585287094, + 2.3134238720, -13.8805427551, 2.3064496517, -14.9919233322, + 2.2987961769, -16.0915718079, 2.2904527187, -17.1783962250, + 2.2814145088, -18.2513160706, 2.2716813087, -19.3092918396, + 2.2612588406, -20.3513298035, 2.2501587868, -21.3765087128, + 2.2383983135, -22.3839836121, 2.2260005474, -23.3730049133, + 2.2129933834, -24.3429279327, 2.1994099617, -25.2932147980, + 2.1852869987, -26.2234420776, 2.1706643105, -27.1333026886, + 2.1555843353, -28.0225963593, 2.1400914192, -28.8912334442, + 2.1242303848, -29.7392234802, 2.1080460548, -30.5666675568, + 2.0915834904, -31.3737506866, 3.1353034973, 31.3530349731, + 3.1599588394, 30.5462703705, 3.1842000484, 29.7192001343, + 3.2079601288, 28.8716411591, 3.2311718464, 28.0034904480, + 3.2537686825, 27.1147384644, 3.2756843567, 26.2054748535, + 3.2968556881, 25.2758941650, 3.3172230721, 24.3263034821, + 3.3367314339, 23.3571205139, 3.3553314209, 22.3688755035, + 3.3729808331, 21.3622112274, 3.3896448612, 20.3378696442, + 3.4052970409, 19.2966842651, 3.4199199677, 18.2395725250, + 3.4335041046, 17.1675205231, 3.4460492134, 16.0815639496, + 3.4575622082, 14.9827699661, 3.4680576324, 13.8722305298, + 3.4775555134, 12.7510375977, 3.4860816002, 11.6202726364, + 3.4936647415, 10.4809942245, 3.5003383160, 9.3342361450, + 3.5061287880, 8.1809673309, 3.5110714436, 7.0221428871, + 3.5151960850, 5.8586602211, 3.5185303688, 4.6913738251, + 3.5210976601, 3.5210976601, 3.5229170322, 2.3486113548, + 3.5240030289, 1.1746675968, 3.5243639946, -0.0000000000, + 3.5240030289, -1.1746675968, 3.5229170322, -2.3486113548, + 3.5210976601, -3.5210976601, 3.5185303688, -4.6913738251, + 3.5151960850, -5.8586602211, 3.5110714436, -7.0221428871, + 3.5061287880, -8.1809673309, 3.5003383160, -9.3342361450, + 3.4936647415, -10.4809942245, 3.4860816002, -11.6202726364, + 3.4775555134, -12.7510375977, 3.4680576324, -13.8722305298, + 3.4575622082, -14.9827699661, 3.4460492134, -16.0815639496, + 3.4335041046, -17.1675205231, 3.4199199677, -18.2395725250, + 3.4052970409, -19.2966842651, 3.3896448612, -20.3378696442, + 3.3729808331, -21.3622112274, 3.3553314209, -22.3688755035, + 3.3367314339, -23.3571205139, 3.3172230721, -24.3263034821, + 3.2968556881, -25.2758941650, 3.2756843567, -26.2054748535, + 3.2537686825, -27.1147384644, 3.2311718464, -28.0034904480, + 3.2079601288, -28.8716411591, 3.1842000484, -29.7192001343, + 3.1599588394, -30.5462703705, 3.1353034973, -31.3530349731, + 4.1765446663, 31.3240852356, 4.2093467712, 30.5177650452, + 4.2416019440, 29.6912136078, 4.2732224464, 28.8442516327, + 4.3041195869, 27.9767761230, 4.3342041969, 27.0887775421, + 4.3633904457, 26.1803417206, 4.3915929794, 25.2516613007, + 4.4187340736, 24.3030376434, 4.4447393417, 23.3348808289, + 4.4695439339, 22.3477191925, 4.4930906296, 21.3421802521, + 4.5153336525, 20.3190002441, 4.5362362862, 19.2790031433, + 4.5557746887, 18.2230987549, 4.5739350319, 17.1522560120, + 4.5907158852, 16.0675067902, 4.6061258316, 14.9699077606, + 4.6201815605, 13.8605442047, 4.6329092979, 12.7405004501, + 4.6443414688, 11.6108531952, 4.6545147896, 10.4726581573, + 4.6634740829, 9.3269481659, 4.6712508202, 8.1746883392, + 4.6778917313, 7.0168380737, 4.6834368706, 5.8542957306, + 4.6879205704, 4.6879205704, 4.6913738251, 3.5185303688, + 4.6938219070, 2.3469109535, 4.6952834129, 1.1738208532, + 4.6957693100, -0.0000000000, 4.6952834129, -1.1738208532, + 4.6938219070, -2.3469109535, 4.6913738251, -3.5185303688, + 4.6879205704, -4.6879205704, 4.6834368706, -5.8542957306, + 4.6778917313, -7.0168380737, 4.6712508202, -8.1746883392, + 4.6634740829, -9.3269481659, 4.6545147896, -10.4726581573, + 4.6443414688, -11.6108531952, 4.6329092979, -12.7405004501, + 4.6201815605, -13.8605442047, 4.6061258316, -14.9699077606, + 4.5907158852, -16.0675067902, 4.5739350319, -17.1522560120, + 4.5557746887, -18.2230987549, 4.5362362862, -19.2790031433, + 4.5153336525, -20.3190002441, 4.4930906296, -21.3421802521, + 4.4695439339, -22.3477191925, 4.4447393417, -23.3348808289, + 4.4187340736, -24.3030376434, 4.3915929794, -25.2516613007, + 4.3633904457, -26.1803417206, 4.3342041969, -27.0887775421, + 4.3041195869, -27.9767761230, 4.2732224464, -28.8442516327, + 4.2416019440, -29.6912136078, 4.2093467712, -30.5177650452, + 4.1765446663, -31.3240852356, 5.2144937515, 31.2869625092, + 5.2553801537, 30.4812030792, 5.2955918312, 29.6553134918, + 5.3350205421, 28.8091106415, 5.3735561371, 27.9424934387, + 5.4110903740, 27.0554504395, 5.4475140572, 26.1480674744, + 5.4827241898, 25.2205314636, 5.5166220665, 24.2731380463, + 5.5491170883, 23.3062915802, 5.5801262856, 22.3205051422, + 5.6095800400, 21.3164043427, 5.6374182701, 20.2947063446, + 5.6635961533, 19.2562274933, 5.6880812645, 18.2018604279, + 5.7108559608, 17.1325664520, 5.7319149971, 16.0493621826, + 5.7512674332, 14.9532957077, 5.7689332962, 13.8454399109, + 5.7849416733, 12.7268714905, 5.7993311882, 11.5986623764, + 5.8121461868, 10.4618625641, 5.8234338760, 9.3174943924, + 5.8332486153, 8.1665477753, 5.8416299820, 7.0099563599, + 5.8486313820, 5.8486313820, 5.8542957306, 4.6834368706, + 5.8586602211, 3.5151960850, 5.8617553711, 2.3447020054, + 5.8636031151, 1.1727205515, 5.8642172813, -0.0000000000, + 5.8636031151, -1.1727205515, 5.8617553711, -2.3447020054, + 5.8586602211, -3.5151960850, 5.8542957306, -4.6834368706, + 5.8486313820, -5.8486313820, 5.8416299820, -7.0099563599, + 5.8332486153, -8.1665477753, 5.8234338760, -9.3174943924, + 5.8121461868, -10.4618625641, 5.7993311882, -11.5986623764, + 5.7849416733, -12.7268714905, 5.7689332962, -13.8454399109, + 5.7512674332, -14.9532957077, 5.7319149971, -16.0493621826, + 5.7108559608, -17.1325664520, 5.6880812645, -18.2018604279, + 5.6635961533, -19.2562274933, 5.6374182701, -20.2947063446, + 5.6095800400, -21.3164043427, 5.5801262856, -22.3205051422, + 5.5491170883, -23.3062915802, 5.5166220665, -24.2731380463, + 5.4827241898, -25.2205314636, 5.4475140572, -26.1480674744, + 5.4110903740, -27.0554504395, 5.3735561371, -27.9424934387, + 5.3350205421, -28.8091106415, 5.2955918312, -29.6553134918, + 5.2553801537, -30.4812030792, 5.2144937515, -31.2869625092, + 6.2483472824, 31.2417373657, 6.2972393036, 30.4366550446, + 6.3453345299, 29.6115608215, 6.3925046921, 28.7662715912, + 6.4386205673, 27.9006881714, 6.4835519791, 27.0147991180, + 6.5271716118, 26.1086864471, 6.5693559647, 25.1825313568, + 6.6099877357, 24.2366218567, 6.6489582062, 23.2713546753, + 6.6861701012, 22.2872333527, 6.7215371132, 21.2848682404, + 6.7549881935, 20.2649650574, 6.7864665985, 19.2283229828, + 6.8159332275, 18.1758213043, 6.8433632851, 17.1084079742, + 6.8687496185, 16.0270824432, 6.8920984268, 14.9328804016, + 6.9134311676, 13.8268623352, 6.9327797890, 12.7100963593, + 6.9501867294, 11.5836448669, 6.9657020569, 10.4485530853, + 6.9793796539, 9.3058395386, 6.9912767410, 8.1564893723, + 7.0014543533, 7.0014543533, 7.0099563599, 5.8416299820, + 7.0168380737, 4.6778917313, 7.0221428871, 3.5110714436, + 7.0259060860, 2.3419685364, 7.0281534195, 1.1713588238, + 7.0289006233, -0.0000000000, 7.0281534195, -1.1713588238, + 7.0259060860, -2.3419685364, 7.0221428871, -3.5110714436, + 7.0168380737, -4.6778917313, 7.0099563599, -5.8416299820, + 7.0014543533, -7.0014543533, 6.9912767410, -8.1564893723, + 6.9793796539, -9.3058395386, 6.9657020569, -10.4485530853, + 6.9501867294, -11.5836448669, 6.9327797890, -12.7100963593, + 6.9134311676, -13.8268623352, 6.8920984268, -14.9328804016, + 6.8687496185, -16.0270824432, 6.8433632851, -17.1084079742, + 6.8159332275, -18.1758213043, 6.7864665985, -19.2283229828, + 6.7549881935, -20.2649650574, 6.7215371132, -21.2848682404, + 6.6861701012, -22.2872333527, 6.6489582062, -23.2713546753, + 6.6099877357, -24.2366218567, 6.5693559647, -25.1825313568, + 6.5271716118, -26.1086864471, 6.4835519791, -27.0147991180, + 6.4386205673, -27.9006881714, 6.3925046921, -28.7662715912, + 6.3453345299, -29.6115608215, 6.2972393036, -30.4366550446, + 6.2483472824, -31.2417373657, 7.2773165703, 31.1884994507, + 7.3341183662, 30.3842048645, 7.3900084496, 29.5600337982, + 7.4448385239, 28.7158069611, 7.4984607697, 27.8514251709, + 7.5507259369, 26.9668769836, 7.6014866829, 26.0622406006, + 7.6506013870, 25.1376914978, 7.6979346275, 24.1935100555, + 7.7433609962, 23.2300834656, 7.7867655754, 22.2479019165, + 7.8280491829, 21.2475624084, 7.8671264648, 20.2297534943, + 7.9039311409, 19.1952610016, 7.9384136200, 18.1449451447, + 7.9705429077, 17.0797348022, 8.0003070831, 16.0006141663, + 8.0277090073, 14.9086036682, 8.0527706146, 13.8047494888, + 8.0755243301, 12.6901092529, 8.0960149765, 11.5657358170, + 8.1142959595, 10.4326667786, 8.1304273605, 9.2919178009, + 8.1444711685, 8.1444711685, 8.1564893723, 6.9912767410, + 8.1665477753, 5.8332486153, 8.1746883392, 4.6712508202, + 8.1809673309, 3.5061287880, 8.1854228973, 2.3386921883, + 8.1880846024, 1.1697263718, 8.1889696121, -0.0000000000, + 8.1880846024, -1.1697263718, 8.1854228973, -2.3386921883, + 8.1809673309, -3.5061287880, 8.1746883392, -4.6712508202, + 8.1665477753, -5.8332486153, 8.1564893723, -6.9912767410, + 8.1444711685, -8.1444711685, 8.1304273605, -9.2919178009, + 8.1142959595, -10.4326667786, 8.0960149765, -11.5657358170, + 8.0755243301, -12.6901092529, 8.0527706146, -13.8047494888, + 8.0277090073, -14.9086036682, 8.0003070831, -16.0006141663, + 7.9705429077, -17.0797348022, 7.9384136200, -18.1449451447, + 7.9039311409, -19.1952610016, 7.8671264648, -20.2297534943, + 7.8280491829, -21.2475624084, 7.7867655754, -22.2479019165, + 7.7433609962, -23.2300834656, 7.6979346275, -24.1935100555, + 7.6506013870, -25.1376914978, 7.6014866829, -26.0622406006, + 7.5507259369, -26.9668769836, 7.4984607697, -27.8514251709, + 7.4448385239, -28.7158069611, 7.3900084496, -29.5600337982, + 7.3341183662, -30.3842048645, 7.2773165703, -31.1884994507, + 8.3006286621, 31.1273555756, 8.3652276993, 30.3239517212, + 8.4288072586, 29.5008239746, 8.4912004471, 28.6578006744, + 8.5522403717, 27.7947807312, 8.6117601395, 26.9117507935, + 8.6695947647, 26.0087852478, 8.7255840302, 25.0860557556, + 8.7795763016, 24.1438331604, 8.8314266205, 23.1824951172, + 8.8810071945, 22.2025184631, 8.9282026291, 21.2044811249, + 8.9729146957, 20.1890583038, 9.0150651932, 19.1570129395, + 9.0545949936, 18.1091899872, 9.0914659500, 17.0464992523, + 9.1256580353, 15.9699020386, 9.1571722031, 14.8804044724, + 9.1860256195, 13.7790384293, 9.2122516632, 12.6668453217, + 9.2358942032, 11.5448684692, 9.2570114136, 10.4141378403, + 9.2756633759, 9.2756633759, 9.2919178009, 8.1304273605, + 9.3058395386, 6.9793796539, 9.3174943924, 5.8234338760, + 9.3269481659, 4.6634740829, 9.3342361450, 3.5003383160, + 9.3394098282, 2.3348524570, 9.3425016403, 1.1678127050, + 9.3435297012, -0.0000000000, 9.3425016403, -1.1678127050, + 9.3394098282, -2.3348524570, 9.3342361450, -3.5003383160, + 9.3269481659, -4.6634740829, 9.3174943924, -5.8234338760, + 9.3058395386, -6.9793796539, 9.2919178009, -8.1304273605, + 9.2756633759, -9.2756633759, 9.2570114136, -10.4141378403, + 9.2358942032, -11.5448684692, 9.2122516632, -12.6668453217, + 9.1860256195, -13.7790384293, 9.1571722031, -14.8804044724, + 9.1256580353, -15.9699020386, 9.0914659500, -17.0464992523, + 9.0545949936, -18.1091899872, 9.0150651932, -19.1570129395, + 8.9729146957, -20.1890583038, 8.9282026291, -21.2044811249, + 8.8810071945, -22.2025184631, 8.8314266205, -23.1824951172, + 8.7795763016, -24.1438331604, 8.7255840302, -25.0860557556, + 8.6695947647, -26.0087852478, 8.6117601395, -26.9117507935, + 8.5522403717, -27.7947807312, 8.4912004471, -28.6578006744, + 8.4288072586, -29.5008239746, 8.3652276993, -30.3239517212, + 8.3006286621, -31.1273555756, 9.3175296783, 31.0584316254, + 9.3897972107, 30.2560138702, 9.4609432220, 29.4340457916, + 9.5307846069, 28.5923538208, 9.5991382599, 27.7308425903, + 9.6658191681, 26.8494987488, 9.7306461334, 25.9483890533, + 9.7934408188, 25.0276813507, 9.8540334702, 24.0876388550, + 9.9122667313, 23.1286220551, 9.9679937363, 22.1510982513, + 10.0210866928, 21.1556262970, 10.0714330673, 20.1428661346, + 10.1189422607, 19.1135578156, 10.1635446548, 18.0685253143, + 10.2051935196, 17.0086555481, 10.2438602448, 15.9348936081, + 10.2795400620, 14.8482246399, 10.3122472763, 13.7496623993, + 10.3420104980, 12.6402349472, 10.3688755035, 11.5209722519, + 10.3928966522, 10.3928966522, 10.4141378403, 9.2570114136, + 10.4326667786, 8.1142959595, 10.4485530853, 6.9657020569, + 10.4618625641, 5.8121461868, 10.4726581573, 4.6545147896, + 10.4809942245, 3.4936647415, 10.4869146347, 2.3304255009, + 10.4904632568, 1.1656070948, 10.4916400909, -0.0000000000, + 10.4904632568, -1.1656070948, 10.4869146347, -2.3304255009, + 10.4809942245, -3.4936647415, 10.4726581573, -4.6545147896, + 10.4618625641, -5.8121461868, 10.4485530853, -6.9657020569, + 10.4326667786, -8.1142959595, 10.4141378403, -9.2570114136, + 10.3928966522, -10.3928966522, 10.3688755035, -11.5209722519, + 10.3420104980, -12.6402349472, 10.3122472763, -13.7496623993, + 10.2795400620, -14.8482246399, 10.2438602448, -15.9348936081, + 10.2051935196, -17.0086555481, 10.1635446548, -18.0685253143, + 10.1189422607, -19.1135578156, 10.0714330673, -20.1428661346, + 10.0210866928, -21.1556262970, 9.9679937363, -22.1510982513, + 9.9122667313, -23.1286220551, 9.8540334702, -24.0876388550, + 9.7934408188, -25.0276813507, 9.7306461334, -25.9483890533, + 9.6658191681, -26.8494987488, 9.5991382599, -27.7308425903, + 9.5307846069, -28.5923538208, 9.4609432220, -29.4340457916, + 9.3897972107, -30.2560138702, 9.3175296783, -31.0584316254, + 10.3272886276, 30.9818668365, 10.4070777893, 30.1805267334, + 10.4856500626, 29.3598213196, 10.5628099442, 28.5195865631, + 10.6383552551, 27.6597232819, 10.7120866776, 26.7802181244, + 10.7838068008, 25.8811359406, 10.8533210754, 24.9626388550, + 10.9204444885, 24.0249786377, 10.9850025177, 23.0685062408, + 11.0468349457, 22.0936698914, 11.1057968140, 21.1010150909, + 11.1617650986, 20.0911769867, 11.2146348953, 19.0648784637, + 11.2643251419, 18.0229206085, 11.3107776642, 16.9661674500, + 11.3539581299, 15.8955411911, 11.3938522339, 14.8120079041, + 11.4304676056, 13.7165613174, 11.4638299942, 12.6102132797, + 11.4939804077, 11.4939804077, 11.5209722519, 10.3688755035, + 11.5448684692, 9.2358942032, 11.5657358170, 8.0960149765, + 11.5836448669, 6.9501867294, 11.5986623764, 5.7993311882, + 11.6108531952, 4.6443414688, 11.6202726364, 3.4860816002, + 11.6269655228, 2.3253931999, 11.6309680939, 1.1630967855, + 11.6322994232, -0.0000000000, 11.6309680939, -1.1630967855, + 11.6269655228, -2.3253931999, 11.6202726364, -3.4860816002, + 11.6108531952, -4.6443414688, 11.5986623764, -5.7993311882, + 11.5836448669, -6.9501867294, 11.5657358170, -8.0960149765, + 11.5448684692, -9.2358942032, 11.5209722519, -10.3688755035, + 11.4939804077, -11.4939804077, 11.4638299942, -12.6102132797, + 11.4304676056, -13.7165613174, 11.3938522339, -14.8120079041, + 11.3539581299, -15.8955411911, 11.3107776642, -16.9661674500, + 11.2643251419, -18.0229206085, 11.2146348953, -19.0648784637, + 11.1617650986, -20.0911769867, 11.1057968140, -21.1010150909, + 11.0468349457, -22.0936698914, 10.9850025177, -23.0685062408, + 10.9204444885, -24.0249786377, 10.8533210754, -24.9626388550, + 10.7838068008, -25.8811359406, 10.7120866776, -26.7802181244, + 10.6383552551, -27.6597232819, 10.5628099442, -28.5195865631, + 10.4856500626, -29.3598213196, 10.4070777893, -30.1805267334, + 10.3272886276, -30.9818668365, 11.3292007446, 30.8978214264, + 11.4163465500, 30.0976409912, 11.5021886826, 29.2782974243, + 11.5865163803, 28.4396305084, 11.6691150665, 27.5815448761, + 11.7497692108, 26.7040214539, 11.8282670975, 25.8071269989, + 11.9043979645, 24.8910140991, 11.9779634476, 23.9559268951, + 12.0487728119, 23.0022029877, 12.1166515350, 22.0302753448, + 12.1814413071, 21.0406703949, 12.2430028915, 20.0340042114, + 12.3012199402, 19.0109767914, 12.3559989929, 17.9723625183, + 12.4072723389, 16.9190063477, 12.4549913406, 15.8518075943, + 12.4991369247, 14.7717065811, 12.5397062302, 13.6796798706, + 12.5767202377, 12.5767202377, 12.6102132797, 11.4638299942, + 12.6402349472, 10.3420104980, 12.6668453217, 9.2122516632, + 12.6901092529, 8.0755243301, 12.7100963593, 6.9327797890, + 12.7268714905, 5.7849416733, 12.7405004501, 4.6329092979, + 12.7510375977, 3.4775555134, 12.7585287094, 2.3197324276, + 12.7630100250, 1.1602735519, 12.7645006180, -0.0000000000, + 12.7630100250, -1.1602735519, 12.7585287094, -2.3197324276, + 12.7510375977, -3.4775555134, 12.7405004501, -4.6329092979, + 12.7268714905, -5.7849416733, 12.7100963593, -6.9327797890, + 12.6901092529, -8.0755243301, 12.6668453217, -9.2122516632, + 12.6402349472, -10.3420104980, 12.6102132797, -11.4638299942, + 12.5767202377, -12.5767202377, 12.5397062302, -13.6796798706, + 12.4991369247, -14.7717065811, 12.4549913406, -15.8518075943, + 12.4072723389, -16.9190063477, 12.3559989929, -17.9723625183, + 12.3012199402, -19.0109767914, 12.2430028915, -20.0340042114, + 12.1814413071, -21.0406703949, 12.1166515350, -22.0302753448, + 12.0487728119, -23.0022029877, 11.9779634476, -23.9559268951, + 11.9043979645, -24.8910140991, 11.8282670975, -25.8071269989, + 11.7497692108, -26.7040214539, 11.6691150665, -27.5815448761, + 11.5865163803, -28.4396305084, 11.5021886826, -29.2782974243, + 11.4163465500, -30.0976409912, 11.3292007446, -30.8978214264, + 12.3225889206, 30.8064727783, 12.4169082642, 30.0075283051, + 12.5098447800, 29.1896381378, 12.6011743546, 28.3526420593, + 12.6906690598, 27.4964504242, 12.7781000137, 26.6210422516, + 12.8632402420, 25.7264804840, 12.9458684921, 24.8129138947, + 13.0257682800, 23.8805751801, 13.1027374268, 22.9297904968, + 13.1765851974, 21.9609756470, 13.2471399307, 20.9746379852, + 13.3142499924, 19.9713764191, 13.3777856827, 18.9518623352, + 13.4376392365, 17.9168510437, 13.4937295914, 16.8671627045, + 13.5460004807, 15.8036670685, 13.5944185257, 14.7272872925, + 13.6389751434, 13.6389751434, 13.6796798706, 12.5397062302, + 13.7165613174, 11.4304676056, 13.7496623993, 10.3122472763, + 13.7790384293, 9.1860256195, 13.8047494888, 8.0527706146, + 13.8268623352, 6.9134311676, 13.8454399109, 5.7689332962, + 13.8605442047, 4.6201815605, 13.8722305298, 3.4680576324, + 13.8805427551, 2.3134238720, 13.8855171204, 1.1571264267, + 13.8871726990, -0.0000000000, 13.8855171204, -1.1571264267, + 13.8805427551, -2.3134238720, 13.8722305298, -3.4680576324, + 13.8605442047, -4.6201815605, 13.8454399109, -5.7689332962, + 13.8268623352, -6.9134311676, 13.8047494888, -8.0527706146, + 13.7790384293, -9.1860256195, 13.7496623993, -10.3122472763, + 13.7165613174, -11.4304676056, 13.6796798706, -12.5397062302, + 13.6389751434, -13.6389751434, 13.5944185257, -14.7272872925, + 13.5460004807, -15.8036670685, 13.4937295914, -16.8671627045, + 13.4376392365, -17.9168510437, 13.3777856827, -18.9518623352, + 13.3142499924, -19.9713764191, 13.2471399307, -20.9746379852, + 13.1765851974, -21.9609756470, 13.1027374268, -22.9297904968, + 13.0257682800, -23.8805751801, 12.9458684921, -24.8129138947, + 12.8632402420, -25.7264804840, 12.7781000137, -26.6210422516, + 12.6906690598, -27.4964504242, 12.6011743546, -28.3526420593, + 12.5098447800, -29.1896381378, 12.4169082642, -30.0075283051, + 12.3225889206, -30.8064727783, 13.3068008423, 30.7080020905, + 13.4081001282, 29.9103755951, 13.5079383850, 29.0940208435, + 13.6060838699, 28.2587909698, 13.7022991180, 27.4045982361, + 13.7963418961, 26.5314273834, 13.8879728317, 25.6393356323, + 13.9769563675, 24.7284622192, 14.0630645752, 23.7990322113, + 14.1460809708, 22.8513603210, 14.2258014679, 21.8858470917, + 14.3020410538, 20.9029827118, 14.3746337891, 19.9033393860, + 14.4434366226, 18.8875694275, 14.5083284378, 17.8564052582, + 14.5692167282, 16.8106346130, 14.6260318756, 15.7511110306, + 14.6787290573, 14.6787290573, 14.7272872925, 13.5944185257, + 14.7717065811, 12.4991369247, 14.8120079041, 11.3938522339, + 14.8482246399, 10.2795400620, 14.8804044724, 9.1571722031, + 14.9086036682, 8.0277090073, 14.9328804016, 6.8920984268, + 14.9532957077, 5.7512674332, 14.9699077606, 4.6061258316, + 14.9827699661, 3.4575622082, 14.9919233322, 2.3064496517, + 14.9974021912, 1.1536463499, 14.9992265701, -0.0000000000, + 14.9974021912, -1.1536463499, 14.9919233322, -2.3064496517, + 14.9827699661, -3.4575622082, 14.9699077606, -4.6061258316, + 14.9532957077, -5.7512674332, 14.9328804016, -6.8920984268, + 14.9086036682, -8.0277090073, 14.8804044724, -9.1571722031, + 14.8482246399, -10.2795400620, 14.8120079041, -11.3938522339, + 14.7717065811, -12.4991369247, 14.7272872925, -13.5944185257, + 14.6787290573, -14.6787290573, 14.6260318756, -15.7511110306, + 14.5692167282, -16.8106346130, 14.5083284378, -17.8564052582, + 14.4434366226, -18.8875694275, 14.3746337891, -19.9033393860, + 14.3020410538, -20.9029827118, 14.2258014679, -21.8858470917, + 14.1460809708, -22.8513603210, 14.0630645752, -23.7990322113, + 13.9769563675, -24.7284622192, 13.8879728317, -25.6393356323, + 13.7963418961, -26.5314273834, 13.7022991180, -27.4045982361, + 13.6060838699, -28.2587909698, 13.5079383850, -29.0940208435, + 13.4081001282, -29.9103755951, 13.3068008423, -30.7080020905, + 14.2812309265, 30.6026382446, 14.3892917633, 29.8063888550, + 14.4958209991, 28.9916419983, 14.6005821228, 28.1582660675, + 14.7033243179, 27.3061733246, 14.8037958145, 26.4353485107, + 14.9017429352, 25.5458450317, 14.9969215393, 24.6378002167, + 15.0890903473, 23.7114276886, 15.1780204773, 22.7670307159, + 15.2634954453, 21.8049926758, 15.3453159332, 20.8257865906, + 15.4233045578, 19.8299636841, 15.4973020554, 18.8181533813, + 15.5671777725, 17.7910594940, 15.6328210831, 16.7494506836, + 15.6941518784, 15.6941518784, 15.7511110306, 14.6260318756, + 15.8036670685, 13.5460004807, 15.8518075943, 12.4549913406, + 15.8955411911, 11.3539581299, 15.9348936081, 10.2438602448, + 15.9699020386, 9.1256580353, 16.0006141663, 8.0003070831, + 16.0270824432, 6.8687496185, 16.0493621826, 5.7319149971, + 16.0675067902, 4.5907158852, 16.0815639496, 3.4460492134, + 16.0915718079, 2.2987961769, 16.0975666046, 1.1498261690, + 16.0995616913, -0.0000000000, 16.0975666046, -1.1498261690, + 16.0915718079, -2.2987961769, 16.0815639496, -3.4460492134, + 16.0675067902, -4.5907158852, 16.0493621826, -5.7319149971, + 16.0270824432, -6.8687496185, 16.0006141663, -8.0003070831, + 15.9699020386, -9.1256580353, 15.9348936081, -10.2438602448, + 15.8955411911, -11.3539581299, 15.8518075943, -12.4549913406, + 15.8036670685, -13.5460004807, 15.7511110306, -14.6260318756, + 15.6941518784, -15.6941518784, 15.6328210831, -16.7494506836, + 15.5671777725, -17.7910594940, 15.4973020554, -18.8181533813, + 15.4233045578, -19.8299636841, 15.3453159332, -20.8257865906, + 15.2634954453, -21.8049926758, 15.1780204773, -22.7670307159, + 15.0890903473, -23.7114276886, 14.9969215393, -24.6378002167, + 14.9017429352, -25.5458450317, 14.8037958145, -26.4353485107, + 14.7033243179, -27.3061733246, 14.6005821228, -28.1582660675, + 14.4958209991, -28.9916419983, 14.3892917633, -29.8063888550, + 14.2812309265, -30.6026382446, 15.2453002930, 30.4906005859, + 15.3598909378, 29.6957893372, 15.4728860855, 28.8827209473, + 15.5840415955, 28.0512752533, 15.6930990219, 27.2013721466, + 15.7997951508, 26.3329906464, 15.9038677216, 25.4461898804, + 16.0050601959, 24.5410938263, 16.1031227112, 23.6179122925, + 16.1978111267, 22.6769371033, 16.2889003754, 21.7185325623, + 16.3761768341, 20.7431564331, 16.4594497681, 19.7513389587, + 16.5385475159, 18.7436866760, 16.6133232117, 17.7208786011, + 16.6836566925, 16.6836566925, 16.7494506836, 15.6328210831, + 16.8106346130, 14.5692167282, 16.8671627045, 13.4937295914, + 16.9190063477, 12.4072723389, 16.9661674500, 11.3107776642, + 17.0086555481, 10.2051935196, 17.0464992523, 9.0914659500, + 17.0797348022, 7.9705429077, 17.1084079742, 6.8433632851, + 17.1325664520, 5.7108559608, 17.1522560120, 4.5739350319, + 17.1675205231, 3.4335041046, 17.1783962250, 2.2904527187, + 17.1849098206, 1.1456606388, 17.1870784760, -0.0000000000, + 17.1849098206, -1.1456606388, 17.1783962250, -2.2904527187, + 17.1675205231, -3.4335041046, 17.1522560120, -4.5739350319, + 17.1325664520, -5.7108559608, 17.1084079742, -6.8433632851, + 17.0797348022, -7.9705429077, 17.0464992523, -9.0914659500, + 17.0086555481, -10.2051935196, 16.9661674500, -11.3107776642, + 16.9190063477, -12.4072723389, 16.8671627045, -13.4937295914, + 16.8106346130, -14.5692167282, 16.7494506836, -15.6328210831, + 16.6836566925, -16.6836566925, 16.6133232117, -17.7208786011, + 16.5385475159, -18.7436866760, 16.4594497681, -19.7513389587, + 16.3761768341, -20.7431564331, 16.2889003754, -21.7185325623, + 16.1978111267, -22.6769371033, 16.1031227112, -23.6179122925, + 16.0050601959, -24.5410938263, 15.9038677216, -25.4461898804, + 15.7997951508, -26.3329906464, 15.6930990219, -27.2013721466, + 15.5840415955, -28.0512752533, 15.4728860855, -28.8827209473, + 15.3598909378, -29.6957893372, 15.2453002930, -30.4906005859, + 16.1984691620, 30.3721294403, 16.3193359375, 29.5787963867, + 16.4385623932, 28.7674865723, 16.5558776855, 27.9380435944, + 16.6710205078, 27.0904083252, 16.7837219238, 26.2245635986, + 16.8937091827, 25.3405628204, 17.0007152557, 24.4385280609, + 17.1044807434, 23.5186595917, 17.2047519684, 22.5812358856, + 17.3012905121, 21.6266136169, 17.3938732147, 20.6552238464, + 17.4822959900, 19.6675815582, 17.5663719177, 18.6642704010, + 17.6459465027, 17.6459465027, 17.7208786011, 16.6133232117, + 17.7910594940, 15.5671777725, 17.8564052582, 14.5083284378, + 17.9168510437, 13.4376392365, 17.9723625183, 12.3559989929, + 18.0229206085, 11.2643251419, 18.0685253143, 10.1635446548, + 18.1091899872, 9.0545949936, 18.1449451447, 7.9384136200, + 18.1758213043, 6.8159332275, 18.2018604279, 5.6880812645, + 18.2230987549, 4.5557746887, 18.2395725250, 3.4199199677, + 18.2513160706, 2.2814145088, 18.2583522797, 1.1411470175, + 18.2606964111, -0.0000000000, 18.2583522797, -1.1411470175, + 18.2513160706, -2.2814145088, 18.2395725250, -3.4199199677, + 18.2230987549, -4.5557746887, 18.2018604279, -5.6880812645, + 18.1758213043, -6.8159332275, 18.1449451447, -7.9384136200, + 18.1091899872, -9.0545949936, 18.0685253143, -10.1635446548, + 18.0229206085, -11.2643251419, 17.9723625183, -12.3559989929, + 17.9168510437, -13.4376392365, 17.8564052582, -14.5083284378, + 17.7910594940, -15.5671777725, 17.7208786011, -16.6133232117, + 17.6459465027, -17.6459465027, 17.5663719177, -18.6642704010, + 17.4822959900, -19.6675815582, 17.3938732147, -20.6552238464, + 17.3012905121, -21.6266136169, 17.2047519684, -22.5812358856, + 17.1044807434, -23.5186595917, 17.0007152557, -24.4385280609, + 16.8937091827, -25.3405628204, 16.7837219238, -26.2245635986, + 16.6710205078, -27.0904083252, 16.5558776855, -27.9380435944, + 16.4385623932, -28.7674865723, 16.3193359375, -29.5787963867, + 16.1984691620, -30.3721294403, 17.1402397156, 30.2474803925, + 17.2671279907, 29.4556884766, 17.3923187256, 28.6461734772, + 17.5155487061, 27.8188114166, 17.6365318298, 26.9735183716, + 17.7549991608, 26.1102924347, 17.8706703186, 25.2291812897, + 17.9832706451, 24.3303070068, 18.0925292969, 23.4138622284, + 18.1981868744, 22.4801120758, 18.2999897003, 21.5293998718, + 18.3977069855, 20.5621433258, 18.4911193848, 19.5788326263, + 18.5800323486, 18.5800323486, 18.6642704010, 17.5663719177, + 18.7436866760, 16.5385475159, 18.8181533813, 15.4973020554, + 18.8875694275, 14.4434366226, 18.9518623352, 13.3777856827, + 19.0109767914, 12.3012199402, 19.0648784637, 11.2146348953, + 19.1135578156, 10.1189422607, 19.1570129395, 9.0150651932, + 19.1952610016, 7.9039311409, 19.2283229828, 6.7864665985, + 19.2562274933, 5.6635961533, 19.2790031433, 4.5362362862, + 19.2966842651, 3.4052970409, 19.3092918396, 2.2716813087, + 19.3168468475, 1.1362851858, 19.3193645477, -0.0000000000, + 19.3168468475, -1.1362851858, 19.3092918396, -2.2716813087, + 19.2966842651, -3.4052970409, 19.2790031433, -4.5362362862, + 19.2562274933, -5.6635961533, 19.2283229828, -6.7864665985, + 19.1952610016, -7.9039311409, 19.1570129395, -9.0150651932, + 19.1135578156, -10.1189422607, 19.0648784637, -11.2146348953, + 19.0109767914, -12.3012199402, 18.9518623352, -13.3777856827, + 18.8875694275, -14.4434366226, 18.8181533813, -15.4973020554, + 18.7436866760, -16.5385475159, 18.6642704010, -17.5663719177, + 18.5800323486, -18.5800323486, 18.4911193848, -19.5788326263, + 18.3977069855, -20.5621433258, 18.2999897003, -21.5293998718, + 18.1981868744, -22.4801120758, 18.0925292969, -23.4138622284, + 17.9832706451, -24.3303070068, 17.8706703186, -25.2291812897, + 17.7549991608, -26.1102924347, 17.6365318298, -26.9735183716, + 17.5155487061, -27.8188114166, 17.3923187256, -28.6461734772, + 17.2671279907, -29.4556884766, 17.1402397156, -30.2474803925, + 18.0701522827, 30.1169204712, 18.2027912140, 29.3267192841, + 18.3336811066, 28.5190601349, 18.4625568390, 27.6938343048, + 18.5891189575, 26.8509502411, 18.7131004333, 25.9904155731, + 18.8342094421, 25.1122798920, 18.9521656036, 24.2166576385, + 19.0666923523, 23.3037357330, 19.1775150299, 22.3737659454, + 19.2843761444, 21.4270839691, 19.3870315552, 20.4640884399, + 19.4852523804, 19.4852523804, 19.5788326263, 18.4911193848, + 19.6675815582, 17.4822959900, 19.7513389587, 16.4594497681, + 19.8299636841, 15.4233045578, 19.9033393860, 14.3746337891, + 19.9713764191, 13.3142499924, 20.0340042114, 12.2430028915, + 20.0911769867, 11.1617650986, 20.1428661346, 10.0714330673, + 20.1890583038, 8.9729146957, 20.2297534943, 7.8671264648, + 20.2649650574, 6.7549881935, 20.2947063446, 5.6374182701, + 20.3190002441, 4.5153336525, 20.3378696442, 3.3896448612, + 20.3513298035, 2.2612588406, 20.3593997955, 1.1310777664, + 20.3620891571, -0.0000000000, 20.3593997955, -1.1310777664, + 20.3513298035, -2.2612588406, 20.3378696442, -3.3896448612, + 20.3190002441, -4.5153336525, 20.2947063446, -5.6374182701, + 20.2649650574, -6.7549881935, 20.2297534943, -7.8671264648, + 20.1890583038, -8.9729146957, 20.1428661346, -10.0714330673, + 20.0911769867, -11.1617650986, 20.0340042114, -12.2430028915, + 19.9713764191, -13.3142499924, 19.9033393860, -14.3746337891, + 19.8299636841, -15.4233045578, 19.7513389587, -16.4594497681, + 19.6675815582, -17.4822959900, 19.5788326263, -18.4911193848, + 19.4852523804, -19.4852523804, 19.3870315552, -20.4640884399, + 19.2843761444, -21.4270839691, 19.1775150299, -22.3737659454, + 19.0666923523, -23.3037357330, 18.9521656036, -24.2166576385, + 18.8342094421, -25.1122798920, 18.7131004333, -25.9904155731, + 18.5891189575, -26.8509502411, 18.4625568390, -27.6938343048, + 18.3336811066, -28.5190601349, 18.2027912140, -29.3267192841, + 18.0701522827, -30.1169204712, 18.9877948761, 29.9807300568, + 19.1259002686, 29.1921634674, 19.2622089386, 28.3864135742, + 19.3964443207, 27.5633678436, 19.5283241272, 26.7229709625, + 19.6575489044, 25.8651943207, 19.7838344574, 24.9901065826, + 19.9068927765, 24.0978183746, 20.0264377594, 23.1885070801, + 20.1421909332, 22.2624206543, 20.2538833618, 21.3198776245, + 20.3612632751, 20.3612632751, 20.4640884399, 19.3870315552, + 20.5621433258, 18.3977069855, 20.6552238464, 17.3938732147, + 20.7431564331, 16.3761768341, 20.8257865906, 15.3453159332, + 20.9029827118, 14.3020410538, 20.9746379852, 13.2471399307, + 21.0406703949, 12.1814413071, 21.1010150909, 11.1057968140, + 21.1556262970, 10.0210866928, 21.2044811249, 8.9282026291, + 21.2475624084, 7.8280491829, 21.2848682404, 6.7215371132, + 21.3164043427, 5.6095800400, 21.3421802521, 4.4930906296, + 21.3622112274, 3.3729808331, 21.3765087128, 2.2501587868, + 21.3850822449, 1.1255306005, 21.3879394531, -0.0000000000, + 21.3850822449, -1.1255306005, 21.3765087128, -2.2501587868, + 21.3622112274, -3.3729808331, 21.3421802521, -4.4930906296, + 21.3164043427, -5.6095800400, 21.2848682404, -6.7215371132, + 21.2475624084, -7.8280491829, 21.2044811249, -8.9282026291, + 21.1556262970, -10.0210866928, 21.1010150909, -11.1057968140, + 21.0406703949, -12.1814413071, 20.9746379852, -13.2471399307, + 20.9029827118, -14.3020410538, 20.8257865906, -15.3453159332, + 20.7431564331, -16.3761768341, 20.6552238464, -17.3938732147, + 20.5621433258, -18.3977069855, 20.4640884399, -19.3870315552, + 20.3612632751, -20.3612632751, 20.2538833618, -21.3198776245, + 20.1421909332, -22.2624206543, 20.0264377594, -23.1885070801, + 19.9068927765, -24.0978183746, 19.7838344574, -24.9901065826, + 19.6575489044, -25.8651943207, 19.5283241272, -26.7229709625, + 19.3964443207, -27.5633678436, 19.2622089386, -28.3864135742, + 19.1259002686, -29.1921634674, 18.9877948761, -29.9807300568, + 19.8927974701, 29.8391952515, 20.0360755920, 29.0523090363, + 20.1775131226, 28.2485179901, 20.3168258667, 27.4277153015, + 20.4537258148, 26.5898437500, 20.5879211426, 25.7349014282, + 20.7191085815, 24.8629302979, 20.8469982147, 23.9740486145, + 20.9713001251, 23.0684318542, 21.0917301178, 22.1463165283, + 21.2080097198, 21.2080097198, 21.3198776245, 20.2538833618, + 21.4270839691, 19.2843761444, 21.5293998718, 18.2999897003, + 21.6266136169, 17.3012905121, 21.7185325623, 16.2889003754, + 21.8049926758, 15.2634954453, 21.8858470917, 14.2258014679, + 21.9609756470, 13.1765851974, 22.0302753448, 12.1166515350, + 22.0936698914, 11.0468349457, 22.1510982513, 9.9679937363, + 22.2025184631, 8.8810071945, 22.2479019165, 7.7867655754, + 22.2872333527, 6.6861701012, 22.3205051422, 5.5801262856, + 22.3477191925, 4.4695439339, 22.3688755035, 3.3553314209, + 22.3839836121, 2.2383983135, 22.3930454254, 1.1196522713, + 22.3960647583, -0.0000000000, 22.3930454254, -1.1196522713, + 22.3839836121, -2.2383983135, 22.3688755035, -3.3553314209, + 22.3477191925, -4.4695439339, 22.3205051422, -5.5801262856, + 22.2872333527, -6.6861701012, 22.2479019165, -7.7867655754, + 22.2025184631, -8.8810071945, 22.1510982513, -9.9679937363, + 22.0936698914, -11.0468349457, 22.0302753448, -12.1166515350, + 21.9609756470, -13.1765851974, 21.8858470917, -14.2258014679, + 21.8049926758, -15.2634954453, 21.7185325623, -16.2889003754, + 21.6266136169, -17.3012905121, 21.5293998718, -18.2999897003, + 21.4270839691, -19.2843761444, 21.3198776245, -20.2538833618, + 21.2080097198, -21.2080097198, 21.0917301178, -22.1463165283, + 20.9713001251, -23.0684318542, 20.8469982147, -23.9740486145, + 20.7191085815, -24.8629302979, 20.5879211426, -25.7349014282, + 20.4537258148, -26.5898437500, 20.3168258667, -27.4277153015, + 20.1775131226, -28.2485179901, 20.0360755920, -29.0523090363, + 19.8927974701, -29.8391952515, 20.7848300934, 29.6926136017, + 20.9329833984, 28.9074535370, 21.0792503357, 28.1056671143, + 21.2233448029, 27.2871570587, 21.3649730682, 26.4518718719, + 21.5038471222, 25.5998191833, 21.6396484375, 24.7310256958, + 21.7720890045, 23.8456211090, 21.9008731842, 22.9437732697, + 22.0257110596, 22.0257110596, 22.1463165283, 21.0917301178, + 22.2624206543, 20.1421909332, 22.3737659454, 19.1775150299, + 22.4801120758, 18.1981868744, 22.5812358856, 17.2047519684, + 22.6769371033, 16.1978111267, 22.7670307159, 15.1780204773, + 22.8513603210, 14.1460809708, 22.9297904968, 13.1027374268, + 23.0022029877, 12.0487728119, 23.0685062408, 10.9850025177, + 23.1286220551, 9.9122667313, 23.1824951172, 8.8314266205, + 23.2300834656, 7.7433609962, 23.2713546753, 6.6489582062, + 23.3062915802, 5.5491170883, 23.3348808289, 4.4447393417, + 23.3571205139, 3.3367314339, 23.3730049133, 2.2260005474, + 23.3825359344, 1.1134541035, 23.3857116699, -0.0000000000, + 23.3825359344, -1.1134541035, 23.3730049133, -2.2260005474, + 23.3571205139, -3.3367314339, 23.3348808289, -4.4447393417, + 23.3062915802, -5.5491170883, 23.2713546753, -6.6489582062, + 23.2300834656, -7.7433609962, 23.1824951172, -8.8314266205, + 23.1286220551, -9.9122667313, 23.0685062408, -10.9850025177, + 23.0022029877, -12.0487728119, 22.9297904968, -13.1027374268, + 22.8513603210, -14.1460809708, 22.7670307159, -15.1780204773, + 22.6769371033, -16.1978111267, 22.5812358856, -17.2047519684, + 22.4801120758, -18.1981868744, 22.3737659454, -19.1775150299, + 22.2624206543, -20.1421909332, 22.1463165283, -21.0917301178, + 22.0257110596, -22.0257110596, 21.9008731842, -22.9437732697, + 21.7720890045, -23.8456211090, 21.6396484375, -24.7310256958, + 21.5038471222, -25.5998191833, 21.3649730682, -26.4518718719, + 21.2233448029, -27.2871570587, 21.0792503357, -28.1056671143, + 20.9329833984, -28.9074535370, 20.7848300934, -29.6926136017, + 21.6636123657, 29.5412883759, 21.8163356781, 28.7578964233, + 21.9671268463, 27.9581623077, 22.1156997681, 27.1419944763, + 22.2617568970, 26.3093490601, 22.4049987793, 25.4602241516, + 22.5451297760, 24.5946865082, 22.6818294525, 23.7128200531, + 22.8148078918, 22.8148078918, 22.9437732697, 21.9008731842, + 23.0684318542, 20.9713001251, 23.1885070801, 20.0264377594, + 23.3037357330, 19.0666923523, 23.4138622284, 18.0925292969, + 23.5186595917, 17.1044807434, 23.6179122925, 16.1031227112, + 23.7114276886, 15.0890903473, 23.7990322113, 14.0630645752, + 23.8805751801, 13.0257682800, 23.9559268951, 11.9779634476, + 24.0249786377, 10.9204444885, 24.0876388550, 9.8540334702, + 24.1438331604, 8.7795763016, 24.1935100555, 7.6979346275, + 24.2366218567, 6.6099877357, 24.2731380463, 5.5166220665, + 24.3030376434, 4.4187340736, 24.3263034821, 3.3172230721, + 24.3429279327, 2.2129933834, 24.3529033661, 1.1069501638, + 24.3562297821, -0.0000000000, 24.3529033661, -1.1069501638, + 24.3429279327, -2.2129933834, 24.3263034821, -3.3172230721, + 24.3030376434, -4.4187340736, 24.2731380463, -5.5166220665, + 24.2366218567, -6.6099877357, 24.1935100555, -7.6979346275, + 24.1438331604, -8.7795763016, 24.0876388550, -9.8540334702, + 24.0249786377, -10.9204444885, 23.9559268951, -11.9779634476, + 23.8805751801, -13.0257682800, 23.7990322113, -14.0630645752, + 23.7114276886, -15.0890903473, 23.6179122925, -16.1031227112, + 23.5186595917, -17.1044807434, 23.4138622284, -18.0925292969, + 23.3037357330, -19.0666923523, 23.1885070801, -20.0264377594, + 23.0684318542, -20.9713001251, 22.9437732697, -21.9008731842, + 22.8148078918, -22.8148078918, 22.6818294525, -23.7128200531, + 22.5451297760, -24.5946865082, 22.4049987793, -25.4602241516, + 22.2617568970, -26.3093490601, 22.1156997681, -27.1419944763, + 21.9671268463, -27.9581623077, 21.8163356781, -28.7578964233, + 21.6636123657, -29.5412883759, 22.5289039612, 29.3855266571, + 22.6858882904, 28.6039466858, 22.8408966064, 27.8063087463, + 22.9936389923, 26.9925327301, 23.1438159943, 26.1625747681, + 23.2911281586, 25.3164443970, 23.4352703094, 24.4541950226, + 23.5759410858, 23.5759410858, 23.7128200531, 22.6818294525, + 23.8456211090, 21.7720890045, 23.9740486145, 20.8469982147, + 24.0978183746, 19.9068927765, 24.2166576385, 18.9521656036, + 24.3303070068, 17.9832706451, 24.4385280609, 17.0007152557, + 24.5410938263, 16.0050601959, 24.6378002167, 14.9969215393, + 24.7284622192, 13.9769563675, 24.8129138947, 12.9458684921, + 24.8910140991, 11.9043979645, 24.9626388550, 10.8533210754, + 25.0276813507, 9.7934408188, 25.0860557556, 8.7255840302, + 25.1376914978, 7.6506013870, 25.1825313568, 6.5693559647, + 25.2205314636, 5.4827241898, 25.2516613007, 4.3915929794, + 25.2758941650, 3.2968556881, 25.2932147980, 2.1994099617, + 25.3036098480, 1.1001570225, 25.3070774078, -0.0000000000, + 25.3036098480, -1.1001570225, 25.2932147980, -2.1994099617, + 25.2758941650, -3.2968556881, 25.2516613007, -4.3915929794, + 25.2205314636, -5.4827241898, 25.1825313568, -6.5693559647, + 25.1376914978, -7.6506013870, 25.0860557556, -8.7255840302, + 25.0276813507, -9.7934408188, 24.9626388550, -10.8533210754, + 24.8910140991, -11.9043979645, 24.8129138947, -12.9458684921, + 24.7284622192, -13.9769563675, 24.6378002167, -14.9969215393, + 24.5410938263, -16.0050601959, 24.4385280609, -17.0007152557, + 24.3303070068, -17.9832706451, 24.2166576385, -18.9521656036, + 24.0978183746, -19.9068927765, 23.9740486145, -20.8469982147, + 23.8456211090, -21.7720890045, 23.7128200531, -22.6818294525, + 23.5759410858, -23.5759410858, 23.4352703094, -24.4541950226, + 23.2911281586, -25.3164443970, 23.1438159943, -26.1625747681, + 22.9936389923, -26.9925327301, 22.8408966064, -27.8063087463, + 22.6858882904, -28.6039466858, 22.5289039612, -29.3855266571, + 23.3805084229, 29.2256336212, 23.5414447784, 28.4459114075, + 23.7003574371, 27.6504173279, 23.8569583893, 26.8390789032, + 24.0109481812, 26.0118598938, 24.1620216370, 25.1687717438, + 24.3098735809, 24.3098735809, 24.4541950226, 23.4352703094, + 24.5946865082, 22.5451297760, 24.7310256958, 21.6396484375, + 24.8629302979, 20.7191085815, 24.9901065826, 19.7838344574, + 25.1122798920, 18.8342094421, 25.2291812897, 17.8706703186, + 25.3405628204, 16.8937091827, 25.4461898804, 15.9038677216, + 25.5458450317, 14.9017429352, 25.6393356323, 13.8879728317, + 25.7264804840, 12.8632402420, 25.8071269989, 11.8282670975, + 25.8811359406, 10.7838068008, 25.9483890533, 9.7306461334, + 26.0087852478, 8.6695947647, 26.0622406006, 7.6014866829, + 26.1086864471, 6.5271716118, 26.1480674744, 5.4475140572, + 26.1803417206, 4.3633904457, 26.2054748535, 3.2756843567, + 26.2234420776, 2.1852869987, 26.2342300415, 1.0930929184, + 26.2378273010, -0.0000000000, 26.2342300415, -1.0930929184, + 26.2234420776, -2.1852869987, 26.2054748535, -3.2756843567, + 26.1803417206, -4.3633904457, 26.1480674744, -5.4475140572, + 26.1086864471, -6.5271716118, 26.0622406006, -7.6014866829, + 26.0087852478, -8.6695947647, 25.9483890533, -9.7306461334, + 25.8811359406, -10.7838068008, 25.8071269989, -11.8282670975, + 25.7264804840, -12.8632402420, 25.6393356323, -13.8879728317, + 25.5458450317, -14.9017429352, 25.4461898804, -15.9038677216, + 25.3405628204, -16.8937091827, 25.2291812897, -17.8706703186, + 25.1122798920, -18.8342094421, 24.9901065826, -19.7838344574, + 24.8629302979, -20.7191085815, 24.7310256958, -21.6396484375, + 24.5946865082, -22.5451297760, 24.4541950226, -23.4352703094, + 24.3098735809, -24.3098735809, 24.1620216370, -25.1687717438, + 24.0109481812, -26.0118598938, 23.8569583893, -26.8390789032, + 23.7003574371, -27.6504173279, 23.5414447784, -28.4459114075, + 23.3805084229, -29.2256336212, 24.2182693481, 29.0619239807, + 24.3828468323, 28.2841033936, 24.5453567505, 27.4907989502, + 24.7055053711, 26.6819458008, 24.8629932404, 25.8575134277, + 25.0175189972, 25.0175189972, 25.1687717438, 24.1620216370, + 25.3164443970, 23.2911281586, 25.4602241516, 22.4049987793, + 25.5998191833, 21.5038471222, 25.7349014282, 20.5879211426, + 25.8651943207, 19.6575489044, 25.9904155731, 18.7131004333, + 26.1102924347, 17.7549991608, 26.2245635986, 16.7837219238, + 26.3329906464, 15.7997951508, 26.4353485107, 14.8037958145, + 26.5314273834, 13.7963418961, 26.6210422516, 12.7781000137, + 26.7040214539, 11.7497692108, 26.7802181244, 10.7120866776, + 26.8494987488, 9.6658191681, 26.9117507935, 8.6117601395, + 26.9668769836, 7.5507259369, 27.0147991180, 6.4835519791, + 27.0554504395, 5.4110903740, 27.0887775421, 4.3342041969, + 27.1147384644, 3.2537686825, 27.1333026886, 2.1706643105, + 27.1444492340, 1.0857779980, 27.1481666565, -0.0000000000, + 27.1444492340, -1.0857779980, 27.1333026886, -2.1706643105, + 27.1147384644, -3.2537686825, 27.0887775421, -4.3342041969, + 27.0554504395, -5.4110903740, 27.0147991180, -6.4835519791, + 26.9668769836, -7.5507259369, 26.9117507935, -8.6117601395, + 26.8494987488, -9.6658191681, 26.7802181244, -10.7120866776, + 26.7040214539, -11.7497692108, 26.6210422516, -12.7781000137, + 26.5314273834, -13.7963418961, 26.4353485107, -14.8037958145, + 26.3329906464, -15.7997951508, 26.2245635986, -16.7837219238, + 26.1102924347, -17.7549991608, 25.9904155731, -18.7131004333, + 25.8651943207, -19.6575489044, 25.7349014282, -20.5879211426, + 25.5998191833, -21.5038471222, 25.4602241516, -22.4049987793, + 25.3164443970, -23.2911281586, 25.1687717438, -24.1620216370, + 25.0175189972, -25.0175189972, 24.8629932404, -25.8575134277, + 24.7055053711, -26.6819458008, 24.5453567505, -27.4907989502, + 24.3828468323, -28.2841033936, 24.2182693481, -29.0619239807, + 25.0420761108, 28.8947029114, 25.2099876404, 28.1188316345, + 25.3757820129, 27.3277645111, 25.5391674042, 26.5214443207, + 25.6998462677, 25.6998462677, 25.8575134277, 24.8629932404, + 26.0118598938, 24.0109481812, 26.1625747681, 23.1438159943, + 26.3093490601, 22.2617568970, 26.4518718719, 21.3649730682, + 26.5898437500, 20.4537258148, 26.7229709625, 19.5283241272, + 26.8509502411, 18.5891189575, 26.9735183716, 17.6365318298, + 27.0904083252, 16.6710205078, 27.2013721466, 15.6930990219, + 27.3061733246, 14.7033243179, 27.4045982361, 13.7022991180, + 27.4964504242, 12.6906690598, 27.5815448761, 11.6691150665, + 27.6597232819, 10.6383552551, 27.7308425903, 9.5991382599, + 27.7947807312, 8.5522403717, 27.8514251709, 7.4984607697, + 27.9006881714, 6.4386205673, 27.9424934387, 5.3735561371, + 27.9767761230, 4.3041195869, 28.0034904480, 3.2311718464, + 28.0225963593, 2.1555843353, 28.0340709686, 1.0782334805, + 28.0378971100, -0.0000000000, 28.0340709686, -1.0782334805, + 28.0225963593, -2.1555843353, 28.0034904480, -3.2311718464, + 27.9767761230, -4.3041195869, 27.9424934387, -5.3735561371, + 27.9006881714, -6.4386205673, 27.8514251709, -7.4984607697, + 27.7947807312, -8.5522403717, 27.7308425903, -9.5991382599, + 27.6597232819, -10.6383552551, 27.5815448761, -11.6691150665, + 27.4964504242, -12.6906690598, 27.4045982361, -13.7022991180, + 27.3061733246, -14.7033243179, 27.2013721466, -15.6930990219, + 27.0904083252, -16.6710205078, 26.9735183716, -17.6365318298, + 26.8509502411, -18.5891189575, 26.7229709625, -19.5283241272, + 26.5898437500, -20.4537258148, 26.4518718719, -21.3649730682, + 26.3093490601, -22.2617568970, 26.1625747681, -23.1438159943, + 26.0118598938, -24.0109481812, 25.8575134277, -24.8629932404, + 25.6998462677, -25.6998462677, 25.5391674042, -26.5214443207, + 25.3757820129, -27.3277645111, 25.2099876404, -28.1188316345, + 25.0420761108, -28.8947029114, 25.8518505096, 28.7242774963, + 26.0227890015, 27.9504032135, 26.1915645599, 27.1616210938, + 26.3578815460, 26.3578815460, 26.5214443207, 25.5391674042, + 26.6819458008, 24.7055053711, 26.8390789032, 23.8569583893, + 26.9925327301, 22.9936389923, 27.1419944763, 22.1156997681, + 27.2871570587, 21.2233448029, 27.4277153015, 20.3168258667, + 27.5633678436, 19.3964443207, 27.6938343048, 18.4625568390, + 27.8188114166, 17.5155487061, 27.9380435944, 16.5558776855, + 28.0512752533, 15.5840415955, 28.1582660675, 14.6005821228, + 28.2587909698, 13.6060838699, 28.3526420593, 12.6011743546, + 28.4396305084, 11.5865163803, 28.5195865631, 10.5628099442, + 28.5923538208, 9.5307846069, 28.6578006744, 8.4912004471, + 28.7158069611, 7.4448385239, 28.7662715912, 6.3925046921, + 28.8091106415, 5.3350205421, 28.8442516327, 4.2732224464, + 28.8716411591, 3.2079601288, 28.8912334442, 2.1400914192, + 28.9030017853, 1.0704815388, 28.9069271088, -0.0000000000, + 28.9030017853, -1.0704815388, 28.8912334442, -2.1400914192, + 28.8716411591, -3.2079601288, 28.8442516327, -4.2732224464, + 28.8091106415, -5.3350205421, 28.7662715912, -6.3925046921, + 28.7158069611, -7.4448385239, 28.6578006744, -8.4912004471, + 28.5923538208, -9.5307846069, 28.5195865631, -10.5628099442, + 28.4396305084, -11.5865163803, 28.3526420593, -12.6011743546, + 28.2587909698, -13.6060838699, 28.1582660675, -14.6005821228, + 28.0512752533, -15.5840415955, 27.9380435944, -16.5558776855, + 27.8188114166, -17.5155487061, 27.6938343048, -18.4625568390, + 27.5633678436, -19.3964443207, 27.4277153015, -20.3168258667, + 27.2871570587, -21.2233448029, 27.1419944763, -22.1156997681, + 26.9925327301, -22.9936389923, 26.8390789032, -23.8569583893, + 26.6819458008, -24.7055053711, 26.5214443207, -25.5391674042, + 26.3578815460, -26.3578815460, 26.1915645599, -27.1616210938, + 26.0227890015, -27.9504032135, 25.8518505096, -28.7242774963, + 26.6475524902, 28.5509490967, 26.8212203979, 27.7791213989, + 26.9926738739, 26.9926738739, 27.1616210938, 26.1915645599, + 27.3277645111, 25.3757820129, 27.4907989502, 24.5453567505, + 27.6504173279, 23.7003574371, 27.8063087463, 22.8408966064, + 27.9581623077, 21.9671268463, 28.1056671143, 21.0792503357, + 28.2485179901, 20.1775131226, 28.3864135742, 19.2622089386, + 28.5190601349, 18.3336811066, 28.6461734772, 17.3923187256, + 28.7674865723, 16.4385623932, 28.8827209473, 15.4728860855, + 28.9916419983, 14.4958209991, 29.0940208435, 13.5079383850, + 29.1896381378, 12.5098447800, 29.2782974243, 11.5021886826, + 29.3598213196, 10.4856500626, 29.4340457916, 9.4609432220, + 29.5008239746, 8.4288072586, 29.5600337982, 7.3900084496, + 29.6115608215, 6.3453345299, 29.6553134918, 5.2955918312, + 29.6912136078, 4.2416019440, 29.7192001343, 3.1842000484, + 29.7392234802, 2.1242303848, 29.7512512207, 1.0625447035, + 29.7552623749, -0.0000000000, 29.7512512207, -1.0625447035, + 29.7392234802, -2.1242303848, 29.7192001343, -3.1842000484, + 29.6912136078, -4.2416019440, 29.6553134918, -5.2955918312, + 29.6115608215, -6.3453345299, 29.5600337982, -7.3900084496, + 29.5008239746, -8.4288072586, 29.4340457916, -9.4609432220, + 29.3598213196, -10.4856500626, 29.2782974243, -11.5021886826, + 29.1896381378, -12.5098447800, 29.0940208435, -13.5079383850, + 28.9916419983, -14.4958209991, 28.8827209473, -15.4728860855, + 28.7674865723, -16.4385623932, 28.6461734772, -17.3923187256, + 28.5190601349, -18.3336811066, 28.3864135742, -19.2622089386, + 28.2485179901, -20.1775131226, 28.1056671143, -21.0792503357, + 27.9581623077, -21.9671268463, 27.8063087463, -22.8408966064, + 27.6504173279, -23.7003574371, 27.4907989502, -24.5453567505, + 27.3277645111, -25.3757820129, 27.1616210938, -26.1915645599, + 26.9926738739, -26.9926738739, 26.8212203979, -27.7791213989, + 26.6475524902, -28.5509490967, 27.4291725159, 28.3750057220, + 27.6052837372, 27.6052837372, 27.7791213989, 26.8212203979, + 27.9504032135, 26.0227890015, 28.1188316345, 25.2099876404, + 28.2841033936, 24.3828468323, 28.4459114075, 23.5414447784, + 28.6039466858, 22.6858882904, 28.7578964233, 21.8163356781, + 28.9074535370, 20.9329833984, 29.0523090363, 20.0360755920, + 29.1921634674, 19.1259002686, 29.3267192841, 18.2027912140, + 29.4556884766, 17.2671279907, 29.5787963867, 16.3193359375, + 29.6957893372, 15.3598909378, 29.8063888550, 14.3892917633, + 29.9103755951, 13.4081001282, 30.0075283051, 12.4169082642, + 30.0976409912, 11.4163465500, 30.1805267334, 10.4070777893, + 30.2560138702, 9.3897972107, 30.3239517212, 8.3652276993, + 30.3842048645, 7.3341183662, 30.4366550446, 6.2972393036, + 30.4812030792, 5.2553801537, 30.5177650452, 4.2093467712, + 30.5462703705, 3.1599588394, 30.5666675568, 2.1080460548, + 30.5789222717, 1.0544456244, 30.5830078125, -0.0000000000, + 30.5789222717, -1.0544456244, 30.5666675568, -2.1080460548, + 30.5462703705, -3.1599588394, 30.5177650452, -4.2093467712, + 30.4812030792, -5.2553801537, 30.4366550446, -6.2972393036, + 30.3842048645, -7.3341183662, 30.3239517212, -8.3652276993, + 30.2560138702, -9.3897972107, 30.1805267334, -10.4070777893, + 30.0976409912, -11.4163465500, 30.0075283051, -12.4169082642, + 29.9103755951, -13.4081001282, 29.8063888550, -14.3892917633, + 29.6957893372, -15.3598909378, 29.5787963867, -16.3193359375, + 29.4556884766, -17.2671279907, 29.3267192841, -18.2027912140, + 29.1921634674, -19.1259002686, 29.0523090363, -20.0360755920, + 28.9074535370, -20.9329833984, 28.7578964233, -21.8163356781, + 28.6039466858, -22.6858882904, 28.4459114075, -23.5414447784, + 28.2841033936, -24.3828468323, 28.1188316345, -25.2099876404, + 27.9504032135, -26.0227890015, 27.7791213989, -26.8212203979, + 27.6052837372, -27.6052837372, 27.4291725159, -28.3750057220, + 28.1967468262, 28.1967468262, 28.3750057220, 27.4291725159, + 28.5509490967, 26.6475524902, 28.7242774963, 25.8518505096, + 28.8947029114, 25.0420761108, 29.0619239807, 24.2182693481, + 29.2256336212, 23.3805084229, 29.3855266571, 22.5289039612, + 29.5412883759, 21.6636123657, 29.6926136017, 20.7848300934, + 29.8391952515, 19.8927974701, 29.9807300568, 18.9877948761, + 30.1169204712, 18.0701522827, 30.2474803925, 17.1402397156, + 30.3721294403, 16.1984691620, 30.4906005859, 15.2453002930, + 30.6026382446, 14.2812309265, 30.7080020905, 13.3068008423, + 30.8064727783, 12.3225889206, 30.8978214264, 11.3292007446, + 30.9818668365, 10.3272886276, 31.0584316254, 9.3175296783, + 31.1273555756, 8.3006286621, 31.1884994507, 7.2773165703, + 31.2417373657, 6.2483472824, 31.2869625092, 5.2144937515, + 31.3240852356, 4.1765446663, 31.3530349731, 3.1353034973, + 31.3737506866, 2.0915834904, 31.3861980438, 1.0462065935, + 31.3903484344, -0.0000000000, 31.3861980438, -1.0462065935, + 31.3737506866, -2.0915834904, 31.3530349731, -3.1353034973, + 31.3240852356, -4.1765446663, 31.2869625092, -5.2144937515, + 31.2417373657, -6.2483472824, 31.1884994507, -7.2773165703, + 31.1273555756, -8.3006286621, 31.0584316254, -9.3175296783, + 30.9818668365, -10.3272886276, 30.8978214264, -11.3292007446, + 30.8064727783, -12.3225889206, 30.7080020905, -13.3068008423, + 30.6026382446, -14.2812309265, 30.4906005859, -15.2453002930, + 30.3721294403, -16.1984691620, 30.2474803925, -17.1402397156, + 30.1169204712, -18.0701522827, 29.9807300568, -18.9877948761, + 29.8391952515, -19.8927974701, 29.6926136017, -20.7848300934, + 29.5412883759, -21.6636123657, 29.3855266571, -22.5289039612, + 29.2256336212, -23.3805084229, 29.0619239807, -24.2182693481, + 28.8947029114, -25.0420761108, 28.7242774963, -25.8518505096, + 28.5509490967, -26.6475524902, 28.3750057220, -27.4291725159, + 28.1967468262, -28.1967468262, +}; + +} /* namespace Pdraw */ + +#endif /* USE_GLES2 */ diff --git a/libpdraw/src/pdraw_gles2_hmd_shaders.cpp b/libpdraw/src/pdraw_gles2_hmd_shaders.cpp index 7b0aa24..85b7f06 100644 --- a/libpdraw/src/pdraw_gles2_hmd_shaders.cpp +++ b/libpdraw/src/pdraw_gles2_hmd_shaders.cpp @@ -1,199 +1,199 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * OpenGL ES 2.0 HMD distortion correction - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * Extracted from FPVToolbox by Frédéric Bertolus - * https://github.com/niavok/fpvtoolbox - * and translated from Java to C++ - */ - -#ifdef USE_GLES2 - -# include "pdraw_gles2_common.hpp" - -namespace Pdraw { - -const GLchar *pdraw_gles2HmdVertexShader = -# if defined(GL_ES_VERSION_2_0) && \ - (defined(ANDROID) || defined(__APPLE__) || defined(MESON)) - "precision highp float;\n" -# endif - "uniform vec2 EyeToSourceUVScale;\n" - "uniform vec2 EyeToSourceUVOffset;\n" - "uniform vec2 EyeToSourceScale;\n" - "uniform vec2 EyeToSourceOffset;\n" - "uniform int ChromaticAberrationCorrection;\n" - "uniform int Rotation;\n" - "\n" - "attribute vec2 Position;\n" - "attribute vec4 Color;\n" - "\n" - "attribute vec2 TexCoord0;\n" - "attribute vec2 TexCoord1;\n" - "attribute vec2 TexCoord2;\n" - "\n" - "varying vec2 oTexCoord0;\n" - "varying vec2 oTexCoord1;\n" - "varying vec2 oTexCoord2;\n" - "\n" - "varying vec4 oColor;\n" - "\n" - "void main()\n" - "{\n" - " gl_Position.x = Position.x * EyeToSourceScale.x " - "+ EyeToSourceOffset.x;\n" - " gl_Position.y = Position.y * EyeToSourceScale.y + " - "EyeToSourceOffset.y;\n" - " gl_Position.z = 0.5;\n" - " gl_Position.w = 1.0;\n" - "\n" - " float red_color_distortion = 0.0;\n" - " float green_color_distortion = 0.0;\n" - " float blue_color_distortion = 0.0;\n" - "\n" - " if (ChromaticAberrationCorrection == 1)\n" - " {\n" - " red_color_distortion = -0.006;\n" - " blue_color_distortion = 0.009;\n" - " }\n" - "\n" - " /* Vertex inputs are in TanEyeAngle space for the R,G,B channels\n" - " * (i.e. after chromatic aberration and distortion).\n" - " * Scale them into the correct [0-1],[0-1] UV lookup space\n" - " * (depending on eye) */\n" - " vec2 RotatedTexCoord0 = TexCoord0;\n" - " vec2 RotatedTexCoord1 = TexCoord1;\n" - " vec2 RotatedTexCoord2 = TexCoord2;\n" - "\n" - " if (Rotation == 90)\n" - " {\n" - " RotatedTexCoord0 = vec2(TexCoord0.y, 1.0 - TexCoord0.x);\n" - " RotatedTexCoord1 = vec2(TexCoord1.y, 1.0 - TexCoord1.x);\n" - " RotatedTexCoord2 = vec2(TexCoord2.y, 1.0 - TexCoord2.x);\n" - " }\n" - " else if (Rotation == 180)\n" - " {\n" - " RotatedTexCoord0 = vec2(1.0 - TexCoord0.x, " - "1.0 - TexCoord0.y);\n" - " RotatedTexCoord1 = vec2(1.0 - TexCoord1.x, " - "1.0 - TexCoord1.y);\n" - " RotatedTexCoord2 = vec2(1.0 - TexCoord2.x, " - "1.0 - TexCoord2.y);\n" - " }\n" - " else if (Rotation == 270)\n" - " {\n" - " RotatedTexCoord0 = vec2(1.0 - TexCoord0.y, TexCoord0.x);\n" - " RotatedTexCoord1 = vec2(1.0 - TexCoord1.y, TexCoord1.x);\n" - " RotatedTexCoord2 = vec2(1.0 - TexCoord2.y, TexCoord2.x);\n" - " }\n" - "\n" - " oTexCoord0 = ((RotatedTexCoord0 - vec2(0.5,0.5)) * " - "EyeToSourceUVScale * (1.0 + red_color_distortion)) + " - "vec2(0.5,0.5) + EyeToSourceUVOffset;\n" - " oTexCoord1 = ((RotatedTexCoord1 - vec2(0.5,0.5)) * " - "EyeToSourceUVScale * (1.0 + green_color_distortion)) + " - "vec2(0.5,0.5) + EyeToSourceUVOffset;\n" - " oTexCoord2 = ((RotatedTexCoord2 - vec2(0.5,0.5)) * " - "EyeToSourceUVScale * (1.0 + blue_color_distortion)) + " - "vec2(0.5,0.5) + EyeToSourceUVOffset;\n" - "\n" - " oColor = Color; // Used for vignette fade.\n" - "}\n"; - - -const GLchar *pdraw_gles2HmdFragmentShader = -# if defined(GL_ES_VERSION_2_0) && \ - (defined(ANDROID) || defined(__APPLE__) || defined(MESON)) - "precision highp float;\n" - "uniform sampler2D Texture0;\n" -# elif 0 && defined(GL_ES_VERSION_2_0) && defined(ANDROID) - "/* important to include in order to use rendered\n" - " * Android View to gl texture */\n" - "#extension GL_OES_EGL_image_external : require\n" - "precision highp float;\n" - "/* make sure to use samplerExternalOES instead of sampler2D */\n" - "uniform samplerExternalOES Texture0; /* the input texture */\n" -# else - "uniform sampler2D Texture0;\n" -# endif - "\n" - "uniform int LensLimits;\n" - "\n" - "varying vec4 oColor;\n" - "varying vec2 oTexCoord0;\n" - "varying vec2 oTexCoord1;\n" - "varying vec2 oTexCoord2;\n" - "\n" - "void main()\n" - "{\n" - " float ResultA = 0.0;\n" - "\n" - " float ResultR;\n" - "\n" - " if (oTexCoord0.x > 1.0 || oTexCoord0.y > 1.0 || " - "oTexCoord0.x < 0.0 || oTexCoord0.y < 0.0)\n" - " {\n" - " ResultR = (LensLimits == 1) ? 0.1 : 0.0;\n" - " }\n" - " else\n" - " {\n" - " ResultR = texture2D(Texture0, oTexCoord0).r;\n" - " ResultA = texture2D(Texture0, oTexCoord1).a;\n" - " }\n" - "\n" - " float ResultG;\n" - " if (oTexCoord1.x > 1.0 || oTexCoord1.y > 1.0 || " - "oTexCoord1.x < 0.0 || oTexCoord1.y < 0.0)\n" - " {\n" - " ResultG = (LensLimits == 1) ? 0.2 : 0.0;\n" - " }\n" - " else\n" - " {\n" - " ResultG = texture2D(Texture0, oTexCoord1).g;\n" - " }\n" - "\n" - " float ResultB;\n" - " if (oTexCoord2.x > 1.0 || oTexCoord2.y > 1.0 || " - "oTexCoord2.x < 0.0 || oTexCoord2.y < 0.0)\n" - " {\n" - " ResultB = (LensLimits == 1) ? 0.3 : 0.0;\n" - " }\n" - " else\n" - " {\n" - " ResultB = texture2D(Texture0, oTexCoord2).b;\n" - " }\n" - "\n" - " gl_FragColor = vec4(ResultR * oColor.r, ResultG * oColor.g, " - "ResultB * oColor.b, ResultA);\n" - "}\n"; - -} /* namespace Pdraw */ - -#endif /* USE_GLES2 */ +/** + * Parrot Drones Awesome Video Viewer Library + * OpenGL ES 2.0 HMD distortion correction + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Extracted from FPVToolbox by Frédéric Bertolus + * https://github.com/niavok/fpvtoolbox + * and translated from Java to C++ + */ + +#ifdef USE_GLES2 + +# include "pdraw_gles2_common.hpp" + +namespace Pdraw { + +const GLchar *pdraw_gles2HmdVertexShader = +# if defined(GL_ES_VERSION_2_0) && \ + (defined(ANDROID) || defined(__APPLE__) || defined(MESON)) + "precision highp float;\n" +# endif + "uniform vec2 EyeToSourceUVScale;\n" + "uniform vec2 EyeToSourceUVOffset;\n" + "uniform vec2 EyeToSourceScale;\n" + "uniform vec2 EyeToSourceOffset;\n" + "uniform int ChromaticAberrationCorrection;\n" + "uniform int Rotation;\n" + "\n" + "attribute vec2 Position;\n" + "attribute vec4 Color;\n" + "\n" + "attribute vec2 TexCoord0;\n" + "attribute vec2 TexCoord1;\n" + "attribute vec2 TexCoord2;\n" + "\n" + "varying vec2 oTexCoord0;\n" + "varying vec2 oTexCoord1;\n" + "varying vec2 oTexCoord2;\n" + "\n" + "varying vec4 oColor;\n" + "\n" + "void main()\n" + "{\n" + " gl_Position.x = Position.x * EyeToSourceScale.x " + "+ EyeToSourceOffset.x;\n" + " gl_Position.y = Position.y * EyeToSourceScale.y + " + "EyeToSourceOffset.y;\n" + " gl_Position.z = 0.5;\n" + " gl_Position.w = 1.0;\n" + "\n" + " float red_color_distortion = 0.0;\n" + " float green_color_distortion = 0.0;\n" + " float blue_color_distortion = 0.0;\n" + "\n" + " if (ChromaticAberrationCorrection == 1)\n" + " {\n" + " red_color_distortion = -0.006;\n" + " blue_color_distortion = 0.009;\n" + " }\n" + "\n" + " /* Vertex inputs are in TanEyeAngle space for the R,G,B channels\n" + " * (i.e. after chromatic aberration and distortion).\n" + " * Scale them into the correct [0-1],[0-1] UV lookup space\n" + " * (depending on eye) */\n" + " vec2 RotatedTexCoord0 = TexCoord0;\n" + " vec2 RotatedTexCoord1 = TexCoord1;\n" + " vec2 RotatedTexCoord2 = TexCoord2;\n" + "\n" + " if (Rotation == 90)\n" + " {\n" + " RotatedTexCoord0 = vec2(TexCoord0.y, 1.0 - TexCoord0.x);\n" + " RotatedTexCoord1 = vec2(TexCoord1.y, 1.0 - TexCoord1.x);\n" + " RotatedTexCoord2 = vec2(TexCoord2.y, 1.0 - TexCoord2.x);\n" + " }\n" + " else if (Rotation == 180)\n" + " {\n" + " RotatedTexCoord0 = vec2(1.0 - TexCoord0.x, " + "1.0 - TexCoord0.y);\n" + " RotatedTexCoord1 = vec2(1.0 - TexCoord1.x, " + "1.0 - TexCoord1.y);\n" + " RotatedTexCoord2 = vec2(1.0 - TexCoord2.x, " + "1.0 - TexCoord2.y);\n" + " }\n" + " else if (Rotation == 270)\n" + " {\n" + " RotatedTexCoord0 = vec2(1.0 - TexCoord0.y, TexCoord0.x);\n" + " RotatedTexCoord1 = vec2(1.0 - TexCoord1.y, TexCoord1.x);\n" + " RotatedTexCoord2 = vec2(1.0 - TexCoord2.y, TexCoord2.x);\n" + " }\n" + "\n" + " oTexCoord0 = ((RotatedTexCoord0 - vec2(0.5,0.5)) * " + "EyeToSourceUVScale * (1.0 + red_color_distortion)) + " + "vec2(0.5,0.5) + EyeToSourceUVOffset;\n" + " oTexCoord1 = ((RotatedTexCoord1 - vec2(0.5,0.5)) * " + "EyeToSourceUVScale * (1.0 + green_color_distortion)) + " + "vec2(0.5,0.5) + EyeToSourceUVOffset;\n" + " oTexCoord2 = ((RotatedTexCoord2 - vec2(0.5,0.5)) * " + "EyeToSourceUVScale * (1.0 + blue_color_distortion)) + " + "vec2(0.5,0.5) + EyeToSourceUVOffset;\n" + "\n" + " oColor = Color; // Used for vignette fade.\n" + "}\n"; + + +const GLchar *pdraw_gles2HmdFragmentShader = +# if defined(GL_ES_VERSION_2_0) && \ + (defined(ANDROID) || defined(__APPLE__) || defined(MESON)) + "precision highp float;\n" + "uniform sampler2D Texture0;\n" +# elif 0 && defined(GL_ES_VERSION_2_0) && defined(ANDROID) + "/* important to include in order to use rendered\n" + " * Android View to gl texture */\n" + "#extension GL_OES_EGL_image_external : require\n" + "precision highp float;\n" + "/* make sure to use samplerExternalOES instead of sampler2D */\n" + "uniform samplerExternalOES Texture0; /* the input texture */\n" +# else + "uniform sampler2D Texture0;\n" +# endif + "\n" + "uniform int LensLimits;\n" + "\n" + "varying vec4 oColor;\n" + "varying vec2 oTexCoord0;\n" + "varying vec2 oTexCoord1;\n" + "varying vec2 oTexCoord2;\n" + "\n" + "void main()\n" + "{\n" + " float ResultA = 0.0;\n" + "\n" + " float ResultR;\n" + "\n" + " if (oTexCoord0.x > 1.0 || oTexCoord0.y > 1.0 || " + "oTexCoord0.x < 0.0 || oTexCoord0.y < 0.0)\n" + " {\n" + " ResultR = (LensLimits == 1) ? 0.1 : 0.0;\n" + " }\n" + " else\n" + " {\n" + " ResultR = texture2D(Texture0, oTexCoord0).r;\n" + " ResultA = texture2D(Texture0, oTexCoord1).a;\n" + " }\n" + "\n" + " float ResultG;\n" + " if (oTexCoord1.x > 1.0 || oTexCoord1.y > 1.0 || " + "oTexCoord1.x < 0.0 || oTexCoord1.y < 0.0)\n" + " {\n" + " ResultG = (LensLimits == 1) ? 0.2 : 0.0;\n" + " }\n" + " else\n" + " {\n" + " ResultG = texture2D(Texture0, oTexCoord1).g;\n" + " }\n" + "\n" + " float ResultB;\n" + " if (oTexCoord2.x > 1.0 || oTexCoord2.y > 1.0 || " + "oTexCoord2.x < 0.0 || oTexCoord2.y < 0.0)\n" + " {\n" + " ResultB = (LensLimits == 1) ? 0.3 : 0.0;\n" + " }\n" + " else\n" + " {\n" + " ResultB = texture2D(Texture0, oTexCoord2).b;\n" + " }\n" + "\n" + " gl_FragColor = vec4(ResultR * oColor.r, ResultG * oColor.g, " + "ResultB * oColor.b, ResultA);\n" + "}\n"; + +} /* namespace Pdraw */ + +#endif /* USE_GLES2 */ diff --git a/libpdraw/src/pdraw_gles2_hmd_texcoords.cpp b/libpdraw/src/pdraw_gles2_hmd_texcoords.cpp index cea6340..0622f2f 100644 --- a/libpdraw/src/pdraw_gles2_hmd_texcoords.cpp +++ b/libpdraw/src/pdraw_gles2_hmd_texcoords.cpp @@ -1,1907 +1,1907 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * OpenGL ES 2.0 HMD distortion correction - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * Extracted from FPVToolbox by Frédéric Bertolus - * https://github.com/niavok/fpvtoolbox - * and translated from Java to C++ - */ - -#ifdef USE_GLES2 - -namespace Pdraw { - -extern const float pdraw_gles2HmdTexCoords[7442] = { - 0.000000000000, 0.000000000000, 0.000000000000, 0.016666667536, - 0.000000000000, 0.033333335072, 0.000000000000, 0.050000000745, - 0.000000000000, 0.066666670144, 0.000000000000, 0.083333335817, - 0.000000000000, 0.100000001490, 0.000000000000, 0.116666667163, - 0.000000000000, 0.133333340287, 0.000000000000, 0.150000005960, - 0.000000000000, 0.166666671634, 0.000000000000, 0.183333337307, - 0.000000000000, 0.200000002980, 0.000000000000, 0.216666668653, - 0.000000000000, 0.233333334327, 0.000000000000, 0.250000000000, - 0.000000000000, 0.266666680574, 0.000000000000, 0.283333331347, - 0.000000000000, 0.300000011921, 0.000000000000, 0.316666662693, - 0.000000000000, 0.333333343267, 0.000000000000, 0.349999994040, - 0.000000000000, 0.366666674614, 0.000000000000, 0.383333325386, - 0.000000000000, 0.400000005960, 0.000000000000, 0.416666656733, - 0.000000000000, 0.433333337307, 0.000000000000, 0.449999988079, - 0.000000000000, 0.466666668653, 0.000000000000, 0.483333319426, - 0.000000000000, 0.500000000000, 0.000000000000, 0.516666650772, - 0.000000000000, 0.533333361149, 0.000000000000, 0.550000011921, - 0.000000000000, 0.566666662693, 0.000000000000, 0.583333313465, - 0.000000000000, 0.600000023842, 0.000000000000, 0.616666674614, - 0.000000000000, 0.633333325386, 0.000000000000, 0.649999976158, - 0.000000000000, 0.666666686535, 0.000000000000, 0.683333337307, - 0.000000000000, 0.699999988079, 0.000000000000, 0.716666638851, - 0.000000000000, 0.733333349228, 0.000000000000, 0.750000000000, - 0.000000000000, 0.766666650772, 0.000000000000, 0.783333361149, - 0.000000000000, 0.800000011921, 0.000000000000, 0.816666662693, - 0.000000000000, 0.833333313465, 0.000000000000, 0.850000023842, - 0.000000000000, 0.866666674614, 0.000000000000, 0.883333325386, - 0.000000000000, 0.899999976158, 0.000000000000, 0.916666686535, - 0.000000000000, 0.933333337307, 0.000000000000, 0.949999988079, - 0.000000000000, 0.966666638851, 0.000000000000, 0.983333349228, - 0.000000000000, 1.000000000000, 0.016666667536, 0.000000000000, - 0.016666667536, 0.016666667536, 0.016666667536, 0.033333335072, - 0.016666667536, 0.050000000745, 0.016666667536, 0.066666670144, - 0.016666667536, 0.083333335817, 0.016666667536, 0.100000001490, - 0.016666667536, 0.116666667163, 0.016666667536, 0.133333340287, - 0.016666667536, 0.150000005960, 0.016666667536, 0.166666671634, - 0.016666667536, 0.183333337307, 0.016666667536, 0.200000002980, - 0.016666667536, 0.216666668653, 0.016666667536, 0.233333334327, - 0.016666667536, 0.250000000000, 0.016666667536, 0.266666680574, - 0.016666667536, 0.283333331347, 0.016666667536, 0.300000011921, - 0.016666667536, 0.316666662693, 0.016666667536, 0.333333343267, - 0.016666667536, 0.349999994040, 0.016666667536, 0.366666674614, - 0.016666667536, 0.383333325386, 0.016666667536, 0.400000005960, - 0.016666667536, 0.416666656733, 0.016666667536, 0.433333337307, - 0.016666667536, 0.449999988079, 0.016666667536, 0.466666668653, - 0.016666667536, 0.483333319426, 0.016666667536, 0.500000000000, - 0.016666667536, 0.516666650772, 0.016666667536, 0.533333361149, - 0.016666667536, 0.550000011921, 0.016666667536, 0.566666662693, - 0.016666667536, 0.583333313465, 0.016666667536, 0.600000023842, - 0.016666667536, 0.616666674614, 0.016666667536, 0.633333325386, - 0.016666667536, 0.649999976158, 0.016666667536, 0.666666686535, - 0.016666667536, 0.683333337307, 0.016666667536, 0.699999988079, - 0.016666667536, 0.716666638851, 0.016666667536, 0.733333349228, - 0.016666667536, 0.750000000000, 0.016666667536, 0.766666650772, - 0.016666667536, 0.783333361149, 0.016666667536, 0.800000011921, - 0.016666667536, 0.816666662693, 0.016666667536, 0.833333313465, - 0.016666667536, 0.850000023842, 0.016666667536, 0.866666674614, - 0.016666667536, 0.883333325386, 0.016666667536, 0.899999976158, - 0.016666667536, 0.916666686535, 0.016666667536, 0.933333337307, - 0.016666667536, 0.949999988079, 0.016666667536, 0.966666638851, - 0.016666667536, 0.983333349228, 0.016666667536, 1.000000000000, - 0.033333335072, 0.000000000000, 0.033333335072, 0.016666667536, - 0.033333335072, 0.033333335072, 0.033333335072, 0.050000000745, - 0.033333335072, 0.066666670144, 0.033333335072, 0.083333335817, - 0.033333335072, 0.100000001490, 0.033333335072, 0.116666667163, - 0.033333335072, 0.133333340287, 0.033333335072, 0.150000005960, - 0.033333335072, 0.166666671634, 0.033333335072, 0.183333337307, - 0.033333335072, 0.200000002980, 0.033333335072, 0.216666668653, - 0.033333335072, 0.233333334327, 0.033333335072, 0.250000000000, - 0.033333335072, 0.266666680574, 0.033333335072, 0.283333331347, - 0.033333335072, 0.300000011921, 0.033333335072, 0.316666662693, - 0.033333335072, 0.333333343267, 0.033333335072, 0.349999994040, - 0.033333335072, 0.366666674614, 0.033333335072, 0.383333325386, - 0.033333335072, 0.400000005960, 0.033333335072, 0.416666656733, - 0.033333335072, 0.433333337307, 0.033333335072, 0.449999988079, - 0.033333335072, 0.466666668653, 0.033333335072, 0.483333319426, - 0.033333335072, 0.500000000000, 0.033333335072, 0.516666650772, - 0.033333335072, 0.533333361149, 0.033333335072, 0.550000011921, - 0.033333335072, 0.566666662693, 0.033333335072, 0.583333313465, - 0.033333335072, 0.600000023842, 0.033333335072, 0.616666674614, - 0.033333335072, 0.633333325386, 0.033333335072, 0.649999976158, - 0.033333335072, 0.666666686535, 0.033333335072, 0.683333337307, - 0.033333335072, 0.699999988079, 0.033333335072, 0.716666638851, - 0.033333335072, 0.733333349228, 0.033333335072, 0.750000000000, - 0.033333335072, 0.766666650772, 0.033333335072, 0.783333361149, - 0.033333335072, 0.800000011921, 0.033333335072, 0.816666662693, - 0.033333335072, 0.833333313465, 0.033333335072, 0.850000023842, - 0.033333335072, 0.866666674614, 0.033333335072, 0.883333325386, - 0.033333335072, 0.899999976158, 0.033333335072, 0.916666686535, - 0.033333335072, 0.933333337307, 0.033333335072, 0.949999988079, - 0.033333335072, 0.966666638851, 0.033333335072, 0.983333349228, - 0.033333335072, 1.000000000000, 0.050000000745, 0.000000000000, - 0.050000000745, 0.016666667536, 0.050000000745, 0.033333335072, - 0.050000000745, 0.050000000745, 0.050000000745, 0.066666670144, - 0.050000000745, 0.083333335817, 0.050000000745, 0.100000001490, - 0.050000000745, 0.116666667163, 0.050000000745, 0.133333340287, - 0.050000000745, 0.150000005960, 0.050000000745, 0.166666671634, - 0.050000000745, 0.183333337307, 0.050000000745, 0.200000002980, - 0.050000000745, 0.216666668653, 0.050000000745, 0.233333334327, - 0.050000000745, 0.250000000000, 0.050000000745, 0.266666680574, - 0.050000000745, 0.283333331347, 0.050000000745, 0.300000011921, - 0.050000000745, 0.316666662693, 0.050000000745, 0.333333343267, - 0.050000000745, 0.349999994040, 0.050000000745, 0.366666674614, - 0.050000000745, 0.383333325386, 0.050000000745, 0.400000005960, - 0.050000000745, 0.416666656733, 0.050000000745, 0.433333337307, - 0.050000000745, 0.449999988079, 0.050000000745, 0.466666668653, - 0.050000000745, 0.483333319426, 0.050000000745, 0.500000000000, - 0.050000000745, 0.516666650772, 0.050000000745, 0.533333361149, - 0.050000000745, 0.550000011921, 0.050000000745, 0.566666662693, - 0.050000000745, 0.583333313465, 0.050000000745, 0.600000023842, - 0.050000000745, 0.616666674614, 0.050000000745, 0.633333325386, - 0.050000000745, 0.649999976158, 0.050000000745, 0.666666686535, - 0.050000000745, 0.683333337307, 0.050000000745, 0.699999988079, - 0.050000000745, 0.716666638851, 0.050000000745, 0.733333349228, - 0.050000000745, 0.750000000000, 0.050000000745, 0.766666650772, - 0.050000000745, 0.783333361149, 0.050000000745, 0.800000011921, - 0.050000000745, 0.816666662693, 0.050000000745, 0.833333313465, - 0.050000000745, 0.850000023842, 0.050000000745, 0.866666674614, - 0.050000000745, 0.883333325386, 0.050000000745, 0.899999976158, - 0.050000000745, 0.916666686535, 0.050000000745, 0.933333337307, - 0.050000000745, 0.949999988079, 0.050000000745, 0.966666638851, - 0.050000000745, 0.983333349228, 0.050000000745, 1.000000000000, - 0.066666670144, 0.000000000000, 0.066666670144, 0.016666667536, - 0.066666670144, 0.033333335072, 0.066666670144, 0.050000000745, - 0.066666670144, 0.066666670144, 0.066666670144, 0.083333335817, - 0.066666670144, 0.100000001490, 0.066666670144, 0.116666667163, - 0.066666670144, 0.133333340287, 0.066666670144, 0.150000005960, - 0.066666670144, 0.166666671634, 0.066666670144, 0.183333337307, - 0.066666670144, 0.200000002980, 0.066666670144, 0.216666668653, - 0.066666670144, 0.233333334327, 0.066666670144, 0.250000000000, - 0.066666670144, 0.266666680574, 0.066666670144, 0.283333331347, - 0.066666670144, 0.300000011921, 0.066666670144, 0.316666662693, - 0.066666670144, 0.333333343267, 0.066666670144, 0.349999994040, - 0.066666670144, 0.366666674614, 0.066666670144, 0.383333325386, - 0.066666670144, 0.400000005960, 0.066666670144, 0.416666656733, - 0.066666670144, 0.433333337307, 0.066666670144, 0.449999988079, - 0.066666670144, 0.466666668653, 0.066666670144, 0.483333319426, - 0.066666670144, 0.500000000000, 0.066666670144, 0.516666650772, - 0.066666670144, 0.533333361149, 0.066666670144, 0.550000011921, - 0.066666670144, 0.566666662693, 0.066666670144, 0.583333313465, - 0.066666670144, 0.600000023842, 0.066666670144, 0.616666674614, - 0.066666670144, 0.633333325386, 0.066666670144, 0.649999976158, - 0.066666670144, 0.666666686535, 0.066666670144, 0.683333337307, - 0.066666670144, 0.699999988079, 0.066666670144, 0.716666638851, - 0.066666670144, 0.733333349228, 0.066666670144, 0.750000000000, - 0.066666670144, 0.766666650772, 0.066666670144, 0.783333361149, - 0.066666670144, 0.800000011921, 0.066666670144, 0.816666662693, - 0.066666670144, 0.833333313465, 0.066666670144, 0.850000023842, - 0.066666670144, 0.866666674614, 0.066666670144, 0.883333325386, - 0.066666670144, 0.899999976158, 0.066666670144, 0.916666686535, - 0.066666670144, 0.933333337307, 0.066666670144, 0.949999988079, - 0.066666670144, 0.966666638851, 0.066666670144, 0.983333349228, - 0.066666670144, 1.000000000000, 0.083333335817, 0.000000000000, - 0.083333335817, 0.016666667536, 0.083333335817, 0.033333335072, - 0.083333335817, 0.050000000745, 0.083333335817, 0.066666670144, - 0.083333335817, 0.083333335817, 0.083333335817, 0.100000001490, - 0.083333335817, 0.116666667163, 0.083333335817, 0.133333340287, - 0.083333335817, 0.150000005960, 0.083333335817, 0.166666671634, - 0.083333335817, 0.183333337307, 0.083333335817, 0.200000002980, - 0.083333335817, 0.216666668653, 0.083333335817, 0.233333334327, - 0.083333335817, 0.250000000000, 0.083333335817, 0.266666680574, - 0.083333335817, 0.283333331347, 0.083333335817, 0.300000011921, - 0.083333335817, 0.316666662693, 0.083333335817, 0.333333343267, - 0.083333335817, 0.349999994040, 0.083333335817, 0.366666674614, - 0.083333335817, 0.383333325386, 0.083333335817, 0.400000005960, - 0.083333335817, 0.416666656733, 0.083333335817, 0.433333337307, - 0.083333335817, 0.449999988079, 0.083333335817, 0.466666668653, - 0.083333335817, 0.483333319426, 0.083333335817, 0.500000000000, - 0.083333335817, 0.516666650772, 0.083333335817, 0.533333361149, - 0.083333335817, 0.550000011921, 0.083333335817, 0.566666662693, - 0.083333335817, 0.583333313465, 0.083333335817, 0.600000023842, - 0.083333335817, 0.616666674614, 0.083333335817, 0.633333325386, - 0.083333335817, 0.649999976158, 0.083333335817, 0.666666686535, - 0.083333335817, 0.683333337307, 0.083333335817, 0.699999988079, - 0.083333335817, 0.716666638851, 0.083333335817, 0.733333349228, - 0.083333335817, 0.750000000000, 0.083333335817, 0.766666650772, - 0.083333335817, 0.783333361149, 0.083333335817, 0.800000011921, - 0.083333335817, 0.816666662693, 0.083333335817, 0.833333313465, - 0.083333335817, 0.850000023842, 0.083333335817, 0.866666674614, - 0.083333335817, 0.883333325386, 0.083333335817, 0.899999976158, - 0.083333335817, 0.916666686535, 0.083333335817, 0.933333337307, - 0.083333335817, 0.949999988079, 0.083333335817, 0.966666638851, - 0.083333335817, 0.983333349228, 0.083333335817, 1.000000000000, - 0.100000001490, 0.000000000000, 0.100000001490, 0.016666667536, - 0.100000001490, 0.033333335072, 0.100000001490, 0.050000000745, - 0.100000001490, 0.066666670144, 0.100000001490, 0.083333335817, - 0.100000001490, 0.100000001490, 0.100000001490, 0.116666667163, - 0.100000001490, 0.133333340287, 0.100000001490, 0.150000005960, - 0.100000001490, 0.166666671634, 0.100000001490, 0.183333337307, - 0.100000001490, 0.200000002980, 0.100000001490, 0.216666668653, - 0.100000001490, 0.233333334327, 0.100000001490, 0.250000000000, - 0.100000001490, 0.266666680574, 0.100000001490, 0.283333331347, - 0.100000001490, 0.300000011921, 0.100000001490, 0.316666662693, - 0.100000001490, 0.333333343267, 0.100000001490, 0.349999994040, - 0.100000001490, 0.366666674614, 0.100000001490, 0.383333325386, - 0.100000001490, 0.400000005960, 0.100000001490, 0.416666656733, - 0.100000001490, 0.433333337307, 0.100000001490, 0.449999988079, - 0.100000001490, 0.466666668653, 0.100000001490, 0.483333319426, - 0.100000001490, 0.500000000000, 0.100000001490, 0.516666650772, - 0.100000001490, 0.533333361149, 0.100000001490, 0.550000011921, - 0.100000001490, 0.566666662693, 0.100000001490, 0.583333313465, - 0.100000001490, 0.600000023842, 0.100000001490, 0.616666674614, - 0.100000001490, 0.633333325386, 0.100000001490, 0.649999976158, - 0.100000001490, 0.666666686535, 0.100000001490, 0.683333337307, - 0.100000001490, 0.699999988079, 0.100000001490, 0.716666638851, - 0.100000001490, 0.733333349228, 0.100000001490, 0.750000000000, - 0.100000001490, 0.766666650772, 0.100000001490, 0.783333361149, - 0.100000001490, 0.800000011921, 0.100000001490, 0.816666662693, - 0.100000001490, 0.833333313465, 0.100000001490, 0.850000023842, - 0.100000001490, 0.866666674614, 0.100000001490, 0.883333325386, - 0.100000001490, 0.899999976158, 0.100000001490, 0.916666686535, - 0.100000001490, 0.933333337307, 0.100000001490, 0.949999988079, - 0.100000001490, 0.966666638851, 0.100000001490, 0.983333349228, - 0.100000001490, 1.000000000000, 0.116666667163, 0.000000000000, - 0.116666667163, 0.016666667536, 0.116666667163, 0.033333335072, - 0.116666667163, 0.050000000745, 0.116666667163, 0.066666670144, - 0.116666667163, 0.083333335817, 0.116666667163, 0.100000001490, - 0.116666667163, 0.116666667163, 0.116666667163, 0.133333340287, - 0.116666667163, 0.150000005960, 0.116666667163, 0.166666671634, - 0.116666667163, 0.183333337307, 0.116666667163, 0.200000002980, - 0.116666667163, 0.216666668653, 0.116666667163, 0.233333334327, - 0.116666667163, 0.250000000000, 0.116666667163, 0.266666680574, - 0.116666667163, 0.283333331347, 0.116666667163, 0.300000011921, - 0.116666667163, 0.316666662693, 0.116666667163, 0.333333343267, - 0.116666667163, 0.349999994040, 0.116666667163, 0.366666674614, - 0.116666667163, 0.383333325386, 0.116666667163, 0.400000005960, - 0.116666667163, 0.416666656733, 0.116666667163, 0.433333337307, - 0.116666667163, 0.449999988079, 0.116666667163, 0.466666668653, - 0.116666667163, 0.483333319426, 0.116666667163, 0.500000000000, - 0.116666667163, 0.516666650772, 0.116666667163, 0.533333361149, - 0.116666667163, 0.550000011921, 0.116666667163, 0.566666662693, - 0.116666667163, 0.583333313465, 0.116666667163, 0.600000023842, - 0.116666667163, 0.616666674614, 0.116666667163, 0.633333325386, - 0.116666667163, 0.649999976158, 0.116666667163, 0.666666686535, - 0.116666667163, 0.683333337307, 0.116666667163, 0.699999988079, - 0.116666667163, 0.716666638851, 0.116666667163, 0.733333349228, - 0.116666667163, 0.750000000000, 0.116666667163, 0.766666650772, - 0.116666667163, 0.783333361149, 0.116666667163, 0.800000011921, - 0.116666667163, 0.816666662693, 0.116666667163, 0.833333313465, - 0.116666667163, 0.850000023842, 0.116666667163, 0.866666674614, - 0.116666667163, 0.883333325386, 0.116666667163, 0.899999976158, - 0.116666667163, 0.916666686535, 0.116666667163, 0.933333337307, - 0.116666667163, 0.949999988079, 0.116666667163, 0.966666638851, - 0.116666667163, 0.983333349228, 0.116666667163, 1.000000000000, - 0.133333340287, 0.000000000000, 0.133333340287, 0.016666667536, - 0.133333340287, 0.033333335072, 0.133333340287, 0.050000000745, - 0.133333340287, 0.066666670144, 0.133333340287, 0.083333335817, - 0.133333340287, 0.100000001490, 0.133333340287, 0.116666667163, - 0.133333340287, 0.133333340287, 0.133333340287, 0.150000005960, - 0.133333340287, 0.166666671634, 0.133333340287, 0.183333337307, - 0.133333340287, 0.200000002980, 0.133333340287, 0.216666668653, - 0.133333340287, 0.233333334327, 0.133333340287, 0.250000000000, - 0.133333340287, 0.266666680574, 0.133333340287, 0.283333331347, - 0.133333340287, 0.300000011921, 0.133333340287, 0.316666662693, - 0.133333340287, 0.333333343267, 0.133333340287, 0.349999994040, - 0.133333340287, 0.366666674614, 0.133333340287, 0.383333325386, - 0.133333340287, 0.400000005960, 0.133333340287, 0.416666656733, - 0.133333340287, 0.433333337307, 0.133333340287, 0.449999988079, - 0.133333340287, 0.466666668653, 0.133333340287, 0.483333319426, - 0.133333340287, 0.500000000000, 0.133333340287, 0.516666650772, - 0.133333340287, 0.533333361149, 0.133333340287, 0.550000011921, - 0.133333340287, 0.566666662693, 0.133333340287, 0.583333313465, - 0.133333340287, 0.600000023842, 0.133333340287, 0.616666674614, - 0.133333340287, 0.633333325386, 0.133333340287, 0.649999976158, - 0.133333340287, 0.666666686535, 0.133333340287, 0.683333337307, - 0.133333340287, 0.699999988079, 0.133333340287, 0.716666638851, - 0.133333340287, 0.733333349228, 0.133333340287, 0.750000000000, - 0.133333340287, 0.766666650772, 0.133333340287, 0.783333361149, - 0.133333340287, 0.800000011921, 0.133333340287, 0.816666662693, - 0.133333340287, 0.833333313465, 0.133333340287, 0.850000023842, - 0.133333340287, 0.866666674614, 0.133333340287, 0.883333325386, - 0.133333340287, 0.899999976158, 0.133333340287, 0.916666686535, - 0.133333340287, 0.933333337307, 0.133333340287, 0.949999988079, - 0.133333340287, 0.966666638851, 0.133333340287, 0.983333349228, - 0.133333340287, 1.000000000000, 0.150000005960, 0.000000000000, - 0.150000005960, 0.016666667536, 0.150000005960, 0.033333335072, - 0.150000005960, 0.050000000745, 0.150000005960, 0.066666670144, - 0.150000005960, 0.083333335817, 0.150000005960, 0.100000001490, - 0.150000005960, 0.116666667163, 0.150000005960, 0.133333340287, - 0.150000005960, 0.150000005960, 0.150000005960, 0.166666671634, - 0.150000005960, 0.183333337307, 0.150000005960, 0.200000002980, - 0.150000005960, 0.216666668653, 0.150000005960, 0.233333334327, - 0.150000005960, 0.250000000000, 0.150000005960, 0.266666680574, - 0.150000005960, 0.283333331347, 0.150000005960, 0.300000011921, - 0.150000005960, 0.316666662693, 0.150000005960, 0.333333343267, - 0.150000005960, 0.349999994040, 0.150000005960, 0.366666674614, - 0.150000005960, 0.383333325386, 0.150000005960, 0.400000005960, - 0.150000005960, 0.416666656733, 0.150000005960, 0.433333337307, - 0.150000005960, 0.449999988079, 0.150000005960, 0.466666668653, - 0.150000005960, 0.483333319426, 0.150000005960, 0.500000000000, - 0.150000005960, 0.516666650772, 0.150000005960, 0.533333361149, - 0.150000005960, 0.550000011921, 0.150000005960, 0.566666662693, - 0.150000005960, 0.583333313465, 0.150000005960, 0.600000023842, - 0.150000005960, 0.616666674614, 0.150000005960, 0.633333325386, - 0.150000005960, 0.649999976158, 0.150000005960, 0.666666686535, - 0.150000005960, 0.683333337307, 0.150000005960, 0.699999988079, - 0.150000005960, 0.716666638851, 0.150000005960, 0.733333349228, - 0.150000005960, 0.750000000000, 0.150000005960, 0.766666650772, - 0.150000005960, 0.783333361149, 0.150000005960, 0.800000011921, - 0.150000005960, 0.816666662693, 0.150000005960, 0.833333313465, - 0.150000005960, 0.850000023842, 0.150000005960, 0.866666674614, - 0.150000005960, 0.883333325386, 0.150000005960, 0.899999976158, - 0.150000005960, 0.916666686535, 0.150000005960, 0.933333337307, - 0.150000005960, 0.949999988079, 0.150000005960, 0.966666638851, - 0.150000005960, 0.983333349228, 0.150000005960, 1.000000000000, - 0.166666671634, 0.000000000000, 0.166666671634, 0.016666667536, - 0.166666671634, 0.033333335072, 0.166666671634, 0.050000000745, - 0.166666671634, 0.066666670144, 0.166666671634, 0.083333335817, - 0.166666671634, 0.100000001490, 0.166666671634, 0.116666667163, - 0.166666671634, 0.133333340287, 0.166666671634, 0.150000005960, - 0.166666671634, 0.166666671634, 0.166666671634, 0.183333337307, - 0.166666671634, 0.200000002980, 0.166666671634, 0.216666668653, - 0.166666671634, 0.233333334327, 0.166666671634, 0.250000000000, - 0.166666671634, 0.266666680574, 0.166666671634, 0.283333331347, - 0.166666671634, 0.300000011921, 0.166666671634, 0.316666662693, - 0.166666671634, 0.333333343267, 0.166666671634, 0.349999994040, - 0.166666671634, 0.366666674614, 0.166666671634, 0.383333325386, - 0.166666671634, 0.400000005960, 0.166666671634, 0.416666656733, - 0.166666671634, 0.433333337307, 0.166666671634, 0.449999988079, - 0.166666671634, 0.466666668653, 0.166666671634, 0.483333319426, - 0.166666671634, 0.500000000000, 0.166666671634, 0.516666650772, - 0.166666671634, 0.533333361149, 0.166666671634, 0.550000011921, - 0.166666671634, 0.566666662693, 0.166666671634, 0.583333313465, - 0.166666671634, 0.600000023842, 0.166666671634, 0.616666674614, - 0.166666671634, 0.633333325386, 0.166666671634, 0.649999976158, - 0.166666671634, 0.666666686535, 0.166666671634, 0.683333337307, - 0.166666671634, 0.699999988079, 0.166666671634, 0.716666638851, - 0.166666671634, 0.733333349228, 0.166666671634, 0.750000000000, - 0.166666671634, 0.766666650772, 0.166666671634, 0.783333361149, - 0.166666671634, 0.800000011921, 0.166666671634, 0.816666662693, - 0.166666671634, 0.833333313465, 0.166666671634, 0.850000023842, - 0.166666671634, 0.866666674614, 0.166666671634, 0.883333325386, - 0.166666671634, 0.899999976158, 0.166666671634, 0.916666686535, - 0.166666671634, 0.933333337307, 0.166666671634, 0.949999988079, - 0.166666671634, 0.966666638851, 0.166666671634, 0.983333349228, - 0.166666671634, 1.000000000000, 0.183333337307, 0.000000000000, - 0.183333337307, 0.016666667536, 0.183333337307, 0.033333335072, - 0.183333337307, 0.050000000745, 0.183333337307, 0.066666670144, - 0.183333337307, 0.083333335817, 0.183333337307, 0.100000001490, - 0.183333337307, 0.116666667163, 0.183333337307, 0.133333340287, - 0.183333337307, 0.150000005960, 0.183333337307, 0.166666671634, - 0.183333337307, 0.183333337307, 0.183333337307, 0.200000002980, - 0.183333337307, 0.216666668653, 0.183333337307, 0.233333334327, - 0.183333337307, 0.250000000000, 0.183333337307, 0.266666680574, - 0.183333337307, 0.283333331347, 0.183333337307, 0.300000011921, - 0.183333337307, 0.316666662693, 0.183333337307, 0.333333343267, - 0.183333337307, 0.349999994040, 0.183333337307, 0.366666674614, - 0.183333337307, 0.383333325386, 0.183333337307, 0.400000005960, - 0.183333337307, 0.416666656733, 0.183333337307, 0.433333337307, - 0.183333337307, 0.449999988079, 0.183333337307, 0.466666668653, - 0.183333337307, 0.483333319426, 0.183333337307, 0.500000000000, - 0.183333337307, 0.516666650772, 0.183333337307, 0.533333361149, - 0.183333337307, 0.550000011921, 0.183333337307, 0.566666662693, - 0.183333337307, 0.583333313465, 0.183333337307, 0.600000023842, - 0.183333337307, 0.616666674614, 0.183333337307, 0.633333325386, - 0.183333337307, 0.649999976158, 0.183333337307, 0.666666686535, - 0.183333337307, 0.683333337307, 0.183333337307, 0.699999988079, - 0.183333337307, 0.716666638851, 0.183333337307, 0.733333349228, - 0.183333337307, 0.750000000000, 0.183333337307, 0.766666650772, - 0.183333337307, 0.783333361149, 0.183333337307, 0.800000011921, - 0.183333337307, 0.816666662693, 0.183333337307, 0.833333313465, - 0.183333337307, 0.850000023842, 0.183333337307, 0.866666674614, - 0.183333337307, 0.883333325386, 0.183333337307, 0.899999976158, - 0.183333337307, 0.916666686535, 0.183333337307, 0.933333337307, - 0.183333337307, 0.949999988079, 0.183333337307, 0.966666638851, - 0.183333337307, 0.983333349228, 0.183333337307, 1.000000000000, - 0.200000002980, 0.000000000000, 0.200000002980, 0.016666667536, - 0.200000002980, 0.033333335072, 0.200000002980, 0.050000000745, - 0.200000002980, 0.066666670144, 0.200000002980, 0.083333335817, - 0.200000002980, 0.100000001490, 0.200000002980, 0.116666667163, - 0.200000002980, 0.133333340287, 0.200000002980, 0.150000005960, - 0.200000002980, 0.166666671634, 0.200000002980, 0.183333337307, - 0.200000002980, 0.200000002980, 0.200000002980, 0.216666668653, - 0.200000002980, 0.233333334327, 0.200000002980, 0.250000000000, - 0.200000002980, 0.266666680574, 0.200000002980, 0.283333331347, - 0.200000002980, 0.300000011921, 0.200000002980, 0.316666662693, - 0.200000002980, 0.333333343267, 0.200000002980, 0.349999994040, - 0.200000002980, 0.366666674614, 0.200000002980, 0.383333325386, - 0.200000002980, 0.400000005960, 0.200000002980, 0.416666656733, - 0.200000002980, 0.433333337307, 0.200000002980, 0.449999988079, - 0.200000002980, 0.466666668653, 0.200000002980, 0.483333319426, - 0.200000002980, 0.500000000000, 0.200000002980, 0.516666650772, - 0.200000002980, 0.533333361149, 0.200000002980, 0.550000011921, - 0.200000002980, 0.566666662693, 0.200000002980, 0.583333313465, - 0.200000002980, 0.600000023842, 0.200000002980, 0.616666674614, - 0.200000002980, 0.633333325386, 0.200000002980, 0.649999976158, - 0.200000002980, 0.666666686535, 0.200000002980, 0.683333337307, - 0.200000002980, 0.699999988079, 0.200000002980, 0.716666638851, - 0.200000002980, 0.733333349228, 0.200000002980, 0.750000000000, - 0.200000002980, 0.766666650772, 0.200000002980, 0.783333361149, - 0.200000002980, 0.800000011921, 0.200000002980, 0.816666662693, - 0.200000002980, 0.833333313465, 0.200000002980, 0.850000023842, - 0.200000002980, 0.866666674614, 0.200000002980, 0.883333325386, - 0.200000002980, 0.899999976158, 0.200000002980, 0.916666686535, - 0.200000002980, 0.933333337307, 0.200000002980, 0.949999988079, - 0.200000002980, 0.966666638851, 0.200000002980, 0.983333349228, - 0.200000002980, 1.000000000000, 0.216666668653, 0.000000000000, - 0.216666668653, 0.016666667536, 0.216666668653, 0.033333335072, - 0.216666668653, 0.050000000745, 0.216666668653, 0.066666670144, - 0.216666668653, 0.083333335817, 0.216666668653, 0.100000001490, - 0.216666668653, 0.116666667163, 0.216666668653, 0.133333340287, - 0.216666668653, 0.150000005960, 0.216666668653, 0.166666671634, - 0.216666668653, 0.183333337307, 0.216666668653, 0.200000002980, - 0.216666668653, 0.216666668653, 0.216666668653, 0.233333334327, - 0.216666668653, 0.250000000000, 0.216666668653, 0.266666680574, - 0.216666668653, 0.283333331347, 0.216666668653, 0.300000011921, - 0.216666668653, 0.316666662693, 0.216666668653, 0.333333343267, - 0.216666668653, 0.349999994040, 0.216666668653, 0.366666674614, - 0.216666668653, 0.383333325386, 0.216666668653, 0.400000005960, - 0.216666668653, 0.416666656733, 0.216666668653, 0.433333337307, - 0.216666668653, 0.449999988079, 0.216666668653, 0.466666668653, - 0.216666668653, 0.483333319426, 0.216666668653, 0.500000000000, - 0.216666668653, 0.516666650772, 0.216666668653, 0.533333361149, - 0.216666668653, 0.550000011921, 0.216666668653, 0.566666662693, - 0.216666668653, 0.583333313465, 0.216666668653, 0.600000023842, - 0.216666668653, 0.616666674614, 0.216666668653, 0.633333325386, - 0.216666668653, 0.649999976158, 0.216666668653, 0.666666686535, - 0.216666668653, 0.683333337307, 0.216666668653, 0.699999988079, - 0.216666668653, 0.716666638851, 0.216666668653, 0.733333349228, - 0.216666668653, 0.750000000000, 0.216666668653, 0.766666650772, - 0.216666668653, 0.783333361149, 0.216666668653, 0.800000011921, - 0.216666668653, 0.816666662693, 0.216666668653, 0.833333313465, - 0.216666668653, 0.850000023842, 0.216666668653, 0.866666674614, - 0.216666668653, 0.883333325386, 0.216666668653, 0.899999976158, - 0.216666668653, 0.916666686535, 0.216666668653, 0.933333337307, - 0.216666668653, 0.949999988079, 0.216666668653, 0.966666638851, - 0.216666668653, 0.983333349228, 0.216666668653, 1.000000000000, - 0.233333334327, 0.000000000000, 0.233333334327, 0.016666667536, - 0.233333334327, 0.033333335072, 0.233333334327, 0.050000000745, - 0.233333334327, 0.066666670144, 0.233333334327, 0.083333335817, - 0.233333334327, 0.100000001490, 0.233333334327, 0.116666667163, - 0.233333334327, 0.133333340287, 0.233333334327, 0.150000005960, - 0.233333334327, 0.166666671634, 0.233333334327, 0.183333337307, - 0.233333334327, 0.200000002980, 0.233333334327, 0.216666668653, - 0.233333334327, 0.233333334327, 0.233333334327, 0.250000000000, - 0.233333334327, 0.266666680574, 0.233333334327, 0.283333331347, - 0.233333334327, 0.300000011921, 0.233333334327, 0.316666662693, - 0.233333334327, 0.333333343267, 0.233333334327, 0.349999994040, - 0.233333334327, 0.366666674614, 0.233333334327, 0.383333325386, - 0.233333334327, 0.400000005960, 0.233333334327, 0.416666656733, - 0.233333334327, 0.433333337307, 0.233333334327, 0.449999988079, - 0.233333334327, 0.466666668653, 0.233333334327, 0.483333319426, - 0.233333334327, 0.500000000000, 0.233333334327, 0.516666650772, - 0.233333334327, 0.533333361149, 0.233333334327, 0.550000011921, - 0.233333334327, 0.566666662693, 0.233333334327, 0.583333313465, - 0.233333334327, 0.600000023842, 0.233333334327, 0.616666674614, - 0.233333334327, 0.633333325386, 0.233333334327, 0.649999976158, - 0.233333334327, 0.666666686535, 0.233333334327, 0.683333337307, - 0.233333334327, 0.699999988079, 0.233333334327, 0.716666638851, - 0.233333334327, 0.733333349228, 0.233333334327, 0.750000000000, - 0.233333334327, 0.766666650772, 0.233333334327, 0.783333361149, - 0.233333334327, 0.800000011921, 0.233333334327, 0.816666662693, - 0.233333334327, 0.833333313465, 0.233333334327, 0.850000023842, - 0.233333334327, 0.866666674614, 0.233333334327, 0.883333325386, - 0.233333334327, 0.899999976158, 0.233333334327, 0.916666686535, - 0.233333334327, 0.933333337307, 0.233333334327, 0.949999988079, - 0.233333334327, 0.966666638851, 0.233333334327, 0.983333349228, - 0.233333334327, 1.000000000000, 0.250000000000, 0.000000000000, - 0.250000000000, 0.016666667536, 0.250000000000, 0.033333335072, - 0.250000000000, 0.050000000745, 0.250000000000, 0.066666670144, - 0.250000000000, 0.083333335817, 0.250000000000, 0.100000001490, - 0.250000000000, 0.116666667163, 0.250000000000, 0.133333340287, - 0.250000000000, 0.150000005960, 0.250000000000, 0.166666671634, - 0.250000000000, 0.183333337307, 0.250000000000, 0.200000002980, - 0.250000000000, 0.216666668653, 0.250000000000, 0.233333334327, - 0.250000000000, 0.250000000000, 0.250000000000, 0.266666680574, - 0.250000000000, 0.283333331347, 0.250000000000, 0.300000011921, - 0.250000000000, 0.316666662693, 0.250000000000, 0.333333343267, - 0.250000000000, 0.349999994040, 0.250000000000, 0.366666674614, - 0.250000000000, 0.383333325386, 0.250000000000, 0.400000005960, - 0.250000000000, 0.416666656733, 0.250000000000, 0.433333337307, - 0.250000000000, 0.449999988079, 0.250000000000, 0.466666668653, - 0.250000000000, 0.483333319426, 0.250000000000, 0.500000000000, - 0.250000000000, 0.516666650772, 0.250000000000, 0.533333361149, - 0.250000000000, 0.550000011921, 0.250000000000, 0.566666662693, - 0.250000000000, 0.583333313465, 0.250000000000, 0.600000023842, - 0.250000000000, 0.616666674614, 0.250000000000, 0.633333325386, - 0.250000000000, 0.649999976158, 0.250000000000, 0.666666686535, - 0.250000000000, 0.683333337307, 0.250000000000, 0.699999988079, - 0.250000000000, 0.716666638851, 0.250000000000, 0.733333349228, - 0.250000000000, 0.750000000000, 0.250000000000, 0.766666650772, - 0.250000000000, 0.783333361149, 0.250000000000, 0.800000011921, - 0.250000000000, 0.816666662693, 0.250000000000, 0.833333313465, - 0.250000000000, 0.850000023842, 0.250000000000, 0.866666674614, - 0.250000000000, 0.883333325386, 0.250000000000, 0.899999976158, - 0.250000000000, 0.916666686535, 0.250000000000, 0.933333337307, - 0.250000000000, 0.949999988079, 0.250000000000, 0.966666638851, - 0.250000000000, 0.983333349228, 0.250000000000, 1.000000000000, - 0.266666680574, 0.000000000000, 0.266666680574, 0.016666667536, - 0.266666680574, 0.033333335072, 0.266666680574, 0.050000000745, - 0.266666680574, 0.066666670144, 0.266666680574, 0.083333335817, - 0.266666680574, 0.100000001490, 0.266666680574, 0.116666667163, - 0.266666680574, 0.133333340287, 0.266666680574, 0.150000005960, - 0.266666680574, 0.166666671634, 0.266666680574, 0.183333337307, - 0.266666680574, 0.200000002980, 0.266666680574, 0.216666668653, - 0.266666680574, 0.233333334327, 0.266666680574, 0.250000000000, - 0.266666680574, 0.266666680574, 0.266666680574, 0.283333331347, - 0.266666680574, 0.300000011921, 0.266666680574, 0.316666662693, - 0.266666680574, 0.333333343267, 0.266666680574, 0.349999994040, - 0.266666680574, 0.366666674614, 0.266666680574, 0.383333325386, - 0.266666680574, 0.400000005960, 0.266666680574, 0.416666656733, - 0.266666680574, 0.433333337307, 0.266666680574, 0.449999988079, - 0.266666680574, 0.466666668653, 0.266666680574, 0.483333319426, - 0.266666680574, 0.500000000000, 0.266666680574, 0.516666650772, - 0.266666680574, 0.533333361149, 0.266666680574, 0.550000011921, - 0.266666680574, 0.566666662693, 0.266666680574, 0.583333313465, - 0.266666680574, 0.600000023842, 0.266666680574, 0.616666674614, - 0.266666680574, 0.633333325386, 0.266666680574, 0.649999976158, - 0.266666680574, 0.666666686535, 0.266666680574, 0.683333337307, - 0.266666680574, 0.699999988079, 0.266666680574, 0.716666638851, - 0.266666680574, 0.733333349228, 0.266666680574, 0.750000000000, - 0.266666680574, 0.766666650772, 0.266666680574, 0.783333361149, - 0.266666680574, 0.800000011921, 0.266666680574, 0.816666662693, - 0.266666680574, 0.833333313465, 0.266666680574, 0.850000023842, - 0.266666680574, 0.866666674614, 0.266666680574, 0.883333325386, - 0.266666680574, 0.899999976158, 0.266666680574, 0.916666686535, - 0.266666680574, 0.933333337307, 0.266666680574, 0.949999988079, - 0.266666680574, 0.966666638851, 0.266666680574, 0.983333349228, - 0.266666680574, 1.000000000000, 0.283333331347, 0.000000000000, - 0.283333331347, 0.016666667536, 0.283333331347, 0.033333335072, - 0.283333331347, 0.050000000745, 0.283333331347, 0.066666670144, - 0.283333331347, 0.083333335817, 0.283333331347, 0.100000001490, - 0.283333331347, 0.116666667163, 0.283333331347, 0.133333340287, - 0.283333331347, 0.150000005960, 0.283333331347, 0.166666671634, - 0.283333331347, 0.183333337307, 0.283333331347, 0.200000002980, - 0.283333331347, 0.216666668653, 0.283333331347, 0.233333334327, - 0.283333331347, 0.250000000000, 0.283333331347, 0.266666680574, - 0.283333331347, 0.283333331347, 0.283333331347, 0.300000011921, - 0.283333331347, 0.316666662693, 0.283333331347, 0.333333343267, - 0.283333331347, 0.349999994040, 0.283333331347, 0.366666674614, - 0.283333331347, 0.383333325386, 0.283333331347, 0.400000005960, - 0.283333331347, 0.416666656733, 0.283333331347, 0.433333337307, - 0.283333331347, 0.449999988079, 0.283333331347, 0.466666668653, - 0.283333331347, 0.483333319426, 0.283333331347, 0.500000000000, - 0.283333331347, 0.516666650772, 0.283333331347, 0.533333361149, - 0.283333331347, 0.550000011921, 0.283333331347, 0.566666662693, - 0.283333331347, 0.583333313465, 0.283333331347, 0.600000023842, - 0.283333331347, 0.616666674614, 0.283333331347, 0.633333325386, - 0.283333331347, 0.649999976158, 0.283333331347, 0.666666686535, - 0.283333331347, 0.683333337307, 0.283333331347, 0.699999988079, - 0.283333331347, 0.716666638851, 0.283333331347, 0.733333349228, - 0.283333331347, 0.750000000000, 0.283333331347, 0.766666650772, - 0.283333331347, 0.783333361149, 0.283333331347, 0.800000011921, - 0.283333331347, 0.816666662693, 0.283333331347, 0.833333313465, - 0.283333331347, 0.850000023842, 0.283333331347, 0.866666674614, - 0.283333331347, 0.883333325386, 0.283333331347, 0.899999976158, - 0.283333331347, 0.916666686535, 0.283333331347, 0.933333337307, - 0.283333331347, 0.949999988079, 0.283333331347, 0.966666638851, - 0.283333331347, 0.983333349228, 0.283333331347, 1.000000000000, - 0.300000011921, 0.000000000000, 0.300000011921, 0.016666667536, - 0.300000011921, 0.033333335072, 0.300000011921, 0.050000000745, - 0.300000011921, 0.066666670144, 0.300000011921, 0.083333335817, - 0.300000011921, 0.100000001490, 0.300000011921, 0.116666667163, - 0.300000011921, 0.133333340287, 0.300000011921, 0.150000005960, - 0.300000011921, 0.166666671634, 0.300000011921, 0.183333337307, - 0.300000011921, 0.200000002980, 0.300000011921, 0.216666668653, - 0.300000011921, 0.233333334327, 0.300000011921, 0.250000000000, - 0.300000011921, 0.266666680574, 0.300000011921, 0.283333331347, - 0.300000011921, 0.300000011921, 0.300000011921, 0.316666662693, - 0.300000011921, 0.333333343267, 0.300000011921, 0.349999994040, - 0.300000011921, 0.366666674614, 0.300000011921, 0.383333325386, - 0.300000011921, 0.400000005960, 0.300000011921, 0.416666656733, - 0.300000011921, 0.433333337307, 0.300000011921, 0.449999988079, - 0.300000011921, 0.466666668653, 0.300000011921, 0.483333319426, - 0.300000011921, 0.500000000000, 0.300000011921, 0.516666650772, - 0.300000011921, 0.533333361149, 0.300000011921, 0.550000011921, - 0.300000011921, 0.566666662693, 0.300000011921, 0.583333313465, - 0.300000011921, 0.600000023842, 0.300000011921, 0.616666674614, - 0.300000011921, 0.633333325386, 0.300000011921, 0.649999976158, - 0.300000011921, 0.666666686535, 0.300000011921, 0.683333337307, - 0.300000011921, 0.699999988079, 0.300000011921, 0.716666638851, - 0.300000011921, 0.733333349228, 0.300000011921, 0.750000000000, - 0.300000011921, 0.766666650772, 0.300000011921, 0.783333361149, - 0.300000011921, 0.800000011921, 0.300000011921, 0.816666662693, - 0.300000011921, 0.833333313465, 0.300000011921, 0.850000023842, - 0.300000011921, 0.866666674614, 0.300000011921, 0.883333325386, - 0.300000011921, 0.899999976158, 0.300000011921, 0.916666686535, - 0.300000011921, 0.933333337307, 0.300000011921, 0.949999988079, - 0.300000011921, 0.966666638851, 0.300000011921, 0.983333349228, - 0.300000011921, 1.000000000000, 0.316666662693, 0.000000000000, - 0.316666662693, 0.016666667536, 0.316666662693, 0.033333335072, - 0.316666662693, 0.050000000745, 0.316666662693, 0.066666670144, - 0.316666662693, 0.083333335817, 0.316666662693, 0.100000001490, - 0.316666662693, 0.116666667163, 0.316666662693, 0.133333340287, - 0.316666662693, 0.150000005960, 0.316666662693, 0.166666671634, - 0.316666662693, 0.183333337307, 0.316666662693, 0.200000002980, - 0.316666662693, 0.216666668653, 0.316666662693, 0.233333334327, - 0.316666662693, 0.250000000000, 0.316666662693, 0.266666680574, - 0.316666662693, 0.283333331347, 0.316666662693, 0.300000011921, - 0.316666662693, 0.316666662693, 0.316666662693, 0.333333343267, - 0.316666662693, 0.349999994040, 0.316666662693, 0.366666674614, - 0.316666662693, 0.383333325386, 0.316666662693, 0.400000005960, - 0.316666662693, 0.416666656733, 0.316666662693, 0.433333337307, - 0.316666662693, 0.449999988079, 0.316666662693, 0.466666668653, - 0.316666662693, 0.483333319426, 0.316666662693, 0.500000000000, - 0.316666662693, 0.516666650772, 0.316666662693, 0.533333361149, - 0.316666662693, 0.550000011921, 0.316666662693, 0.566666662693, - 0.316666662693, 0.583333313465, 0.316666662693, 0.600000023842, - 0.316666662693, 0.616666674614, 0.316666662693, 0.633333325386, - 0.316666662693, 0.649999976158, 0.316666662693, 0.666666686535, - 0.316666662693, 0.683333337307, 0.316666662693, 0.699999988079, - 0.316666662693, 0.716666638851, 0.316666662693, 0.733333349228, - 0.316666662693, 0.750000000000, 0.316666662693, 0.766666650772, - 0.316666662693, 0.783333361149, 0.316666662693, 0.800000011921, - 0.316666662693, 0.816666662693, 0.316666662693, 0.833333313465, - 0.316666662693, 0.850000023842, 0.316666662693, 0.866666674614, - 0.316666662693, 0.883333325386, 0.316666662693, 0.899999976158, - 0.316666662693, 0.916666686535, 0.316666662693, 0.933333337307, - 0.316666662693, 0.949999988079, 0.316666662693, 0.966666638851, - 0.316666662693, 0.983333349228, 0.316666662693, 1.000000000000, - 0.333333343267, 0.000000000000, 0.333333343267, 0.016666667536, - 0.333333343267, 0.033333335072, 0.333333343267, 0.050000000745, - 0.333333343267, 0.066666670144, 0.333333343267, 0.083333335817, - 0.333333343267, 0.100000001490, 0.333333343267, 0.116666667163, - 0.333333343267, 0.133333340287, 0.333333343267, 0.150000005960, - 0.333333343267, 0.166666671634, 0.333333343267, 0.183333337307, - 0.333333343267, 0.200000002980, 0.333333343267, 0.216666668653, - 0.333333343267, 0.233333334327, 0.333333343267, 0.250000000000, - 0.333333343267, 0.266666680574, 0.333333343267, 0.283333331347, - 0.333333343267, 0.300000011921, 0.333333343267, 0.316666662693, - 0.333333343267, 0.333333343267, 0.333333343267, 0.349999994040, - 0.333333343267, 0.366666674614, 0.333333343267, 0.383333325386, - 0.333333343267, 0.400000005960, 0.333333343267, 0.416666656733, - 0.333333343267, 0.433333337307, 0.333333343267, 0.449999988079, - 0.333333343267, 0.466666668653, 0.333333343267, 0.483333319426, - 0.333333343267, 0.500000000000, 0.333333343267, 0.516666650772, - 0.333333343267, 0.533333361149, 0.333333343267, 0.550000011921, - 0.333333343267, 0.566666662693, 0.333333343267, 0.583333313465, - 0.333333343267, 0.600000023842, 0.333333343267, 0.616666674614, - 0.333333343267, 0.633333325386, 0.333333343267, 0.649999976158, - 0.333333343267, 0.666666686535, 0.333333343267, 0.683333337307, - 0.333333343267, 0.699999988079, 0.333333343267, 0.716666638851, - 0.333333343267, 0.733333349228, 0.333333343267, 0.750000000000, - 0.333333343267, 0.766666650772, 0.333333343267, 0.783333361149, - 0.333333343267, 0.800000011921, 0.333333343267, 0.816666662693, - 0.333333343267, 0.833333313465, 0.333333343267, 0.850000023842, - 0.333333343267, 0.866666674614, 0.333333343267, 0.883333325386, - 0.333333343267, 0.899999976158, 0.333333343267, 0.916666686535, - 0.333333343267, 0.933333337307, 0.333333343267, 0.949999988079, - 0.333333343267, 0.966666638851, 0.333333343267, 0.983333349228, - 0.333333343267, 1.000000000000, 0.349999994040, 0.000000000000, - 0.349999994040, 0.016666667536, 0.349999994040, 0.033333335072, - 0.349999994040, 0.050000000745, 0.349999994040, 0.066666670144, - 0.349999994040, 0.083333335817, 0.349999994040, 0.100000001490, - 0.349999994040, 0.116666667163, 0.349999994040, 0.133333340287, - 0.349999994040, 0.150000005960, 0.349999994040, 0.166666671634, - 0.349999994040, 0.183333337307, 0.349999994040, 0.200000002980, - 0.349999994040, 0.216666668653, 0.349999994040, 0.233333334327, - 0.349999994040, 0.250000000000, 0.349999994040, 0.266666680574, - 0.349999994040, 0.283333331347, 0.349999994040, 0.300000011921, - 0.349999994040, 0.316666662693, 0.349999994040, 0.333333343267, - 0.349999994040, 0.349999994040, 0.349999994040, 0.366666674614, - 0.349999994040, 0.383333325386, 0.349999994040, 0.400000005960, - 0.349999994040, 0.416666656733, 0.349999994040, 0.433333337307, - 0.349999994040, 0.449999988079, 0.349999994040, 0.466666668653, - 0.349999994040, 0.483333319426, 0.349999994040, 0.500000000000, - 0.349999994040, 0.516666650772, 0.349999994040, 0.533333361149, - 0.349999994040, 0.550000011921, 0.349999994040, 0.566666662693, - 0.349999994040, 0.583333313465, 0.349999994040, 0.600000023842, - 0.349999994040, 0.616666674614, 0.349999994040, 0.633333325386, - 0.349999994040, 0.649999976158, 0.349999994040, 0.666666686535, - 0.349999994040, 0.683333337307, 0.349999994040, 0.699999988079, - 0.349999994040, 0.716666638851, 0.349999994040, 0.733333349228, - 0.349999994040, 0.750000000000, 0.349999994040, 0.766666650772, - 0.349999994040, 0.783333361149, 0.349999994040, 0.800000011921, - 0.349999994040, 0.816666662693, 0.349999994040, 0.833333313465, - 0.349999994040, 0.850000023842, 0.349999994040, 0.866666674614, - 0.349999994040, 0.883333325386, 0.349999994040, 0.899999976158, - 0.349999994040, 0.916666686535, 0.349999994040, 0.933333337307, - 0.349999994040, 0.949999988079, 0.349999994040, 0.966666638851, - 0.349999994040, 0.983333349228, 0.349999994040, 1.000000000000, - 0.366666674614, 0.000000000000, 0.366666674614, 0.016666667536, - 0.366666674614, 0.033333335072, 0.366666674614, 0.050000000745, - 0.366666674614, 0.066666670144, 0.366666674614, 0.083333335817, - 0.366666674614, 0.100000001490, 0.366666674614, 0.116666667163, - 0.366666674614, 0.133333340287, 0.366666674614, 0.150000005960, - 0.366666674614, 0.166666671634, 0.366666674614, 0.183333337307, - 0.366666674614, 0.200000002980, 0.366666674614, 0.216666668653, - 0.366666674614, 0.233333334327, 0.366666674614, 0.250000000000, - 0.366666674614, 0.266666680574, 0.366666674614, 0.283333331347, - 0.366666674614, 0.300000011921, 0.366666674614, 0.316666662693, - 0.366666674614, 0.333333343267, 0.366666674614, 0.349999994040, - 0.366666674614, 0.366666674614, 0.366666674614, 0.383333325386, - 0.366666674614, 0.400000005960, 0.366666674614, 0.416666656733, - 0.366666674614, 0.433333337307, 0.366666674614, 0.449999988079, - 0.366666674614, 0.466666668653, 0.366666674614, 0.483333319426, - 0.366666674614, 0.500000000000, 0.366666674614, 0.516666650772, - 0.366666674614, 0.533333361149, 0.366666674614, 0.550000011921, - 0.366666674614, 0.566666662693, 0.366666674614, 0.583333313465, - 0.366666674614, 0.600000023842, 0.366666674614, 0.616666674614, - 0.366666674614, 0.633333325386, 0.366666674614, 0.649999976158, - 0.366666674614, 0.666666686535, 0.366666674614, 0.683333337307, - 0.366666674614, 0.699999988079, 0.366666674614, 0.716666638851, - 0.366666674614, 0.733333349228, 0.366666674614, 0.750000000000, - 0.366666674614, 0.766666650772, 0.366666674614, 0.783333361149, - 0.366666674614, 0.800000011921, 0.366666674614, 0.816666662693, - 0.366666674614, 0.833333313465, 0.366666674614, 0.850000023842, - 0.366666674614, 0.866666674614, 0.366666674614, 0.883333325386, - 0.366666674614, 0.899999976158, 0.366666674614, 0.916666686535, - 0.366666674614, 0.933333337307, 0.366666674614, 0.949999988079, - 0.366666674614, 0.966666638851, 0.366666674614, 0.983333349228, - 0.366666674614, 1.000000000000, 0.383333325386, 0.000000000000, - 0.383333325386, 0.016666667536, 0.383333325386, 0.033333335072, - 0.383333325386, 0.050000000745, 0.383333325386, 0.066666670144, - 0.383333325386, 0.083333335817, 0.383333325386, 0.100000001490, - 0.383333325386, 0.116666667163, 0.383333325386, 0.133333340287, - 0.383333325386, 0.150000005960, 0.383333325386, 0.166666671634, - 0.383333325386, 0.183333337307, 0.383333325386, 0.200000002980, - 0.383333325386, 0.216666668653, 0.383333325386, 0.233333334327, - 0.383333325386, 0.250000000000, 0.383333325386, 0.266666680574, - 0.383333325386, 0.283333331347, 0.383333325386, 0.300000011921, - 0.383333325386, 0.316666662693, 0.383333325386, 0.333333343267, - 0.383333325386, 0.349999994040, 0.383333325386, 0.366666674614, - 0.383333325386, 0.383333325386, 0.383333325386, 0.400000005960, - 0.383333325386, 0.416666656733, 0.383333325386, 0.433333337307, - 0.383333325386, 0.449999988079, 0.383333325386, 0.466666668653, - 0.383333325386, 0.483333319426, 0.383333325386, 0.500000000000, - 0.383333325386, 0.516666650772, 0.383333325386, 0.533333361149, - 0.383333325386, 0.550000011921, 0.383333325386, 0.566666662693, - 0.383333325386, 0.583333313465, 0.383333325386, 0.600000023842, - 0.383333325386, 0.616666674614, 0.383333325386, 0.633333325386, - 0.383333325386, 0.649999976158, 0.383333325386, 0.666666686535, - 0.383333325386, 0.683333337307, 0.383333325386, 0.699999988079, - 0.383333325386, 0.716666638851, 0.383333325386, 0.733333349228, - 0.383333325386, 0.750000000000, 0.383333325386, 0.766666650772, - 0.383333325386, 0.783333361149, 0.383333325386, 0.800000011921, - 0.383333325386, 0.816666662693, 0.383333325386, 0.833333313465, - 0.383333325386, 0.850000023842, 0.383333325386, 0.866666674614, - 0.383333325386, 0.883333325386, 0.383333325386, 0.899999976158, - 0.383333325386, 0.916666686535, 0.383333325386, 0.933333337307, - 0.383333325386, 0.949999988079, 0.383333325386, 0.966666638851, - 0.383333325386, 0.983333349228, 0.383333325386, 1.000000000000, - 0.400000005960, 0.000000000000, 0.400000005960, 0.016666667536, - 0.400000005960, 0.033333335072, 0.400000005960, 0.050000000745, - 0.400000005960, 0.066666670144, 0.400000005960, 0.083333335817, - 0.400000005960, 0.100000001490, 0.400000005960, 0.116666667163, - 0.400000005960, 0.133333340287, 0.400000005960, 0.150000005960, - 0.400000005960, 0.166666671634, 0.400000005960, 0.183333337307, - 0.400000005960, 0.200000002980, 0.400000005960, 0.216666668653, - 0.400000005960, 0.233333334327, 0.400000005960, 0.250000000000, - 0.400000005960, 0.266666680574, 0.400000005960, 0.283333331347, - 0.400000005960, 0.300000011921, 0.400000005960, 0.316666662693, - 0.400000005960, 0.333333343267, 0.400000005960, 0.349999994040, - 0.400000005960, 0.366666674614, 0.400000005960, 0.383333325386, - 0.400000005960, 0.400000005960, 0.400000005960, 0.416666656733, - 0.400000005960, 0.433333337307, 0.400000005960, 0.449999988079, - 0.400000005960, 0.466666668653, 0.400000005960, 0.483333319426, - 0.400000005960, 0.500000000000, 0.400000005960, 0.516666650772, - 0.400000005960, 0.533333361149, 0.400000005960, 0.550000011921, - 0.400000005960, 0.566666662693, 0.400000005960, 0.583333313465, - 0.400000005960, 0.600000023842, 0.400000005960, 0.616666674614, - 0.400000005960, 0.633333325386, 0.400000005960, 0.649999976158, - 0.400000005960, 0.666666686535, 0.400000005960, 0.683333337307, - 0.400000005960, 0.699999988079, 0.400000005960, 0.716666638851, - 0.400000005960, 0.733333349228, 0.400000005960, 0.750000000000, - 0.400000005960, 0.766666650772, 0.400000005960, 0.783333361149, - 0.400000005960, 0.800000011921, 0.400000005960, 0.816666662693, - 0.400000005960, 0.833333313465, 0.400000005960, 0.850000023842, - 0.400000005960, 0.866666674614, 0.400000005960, 0.883333325386, - 0.400000005960, 0.899999976158, 0.400000005960, 0.916666686535, - 0.400000005960, 0.933333337307, 0.400000005960, 0.949999988079, - 0.400000005960, 0.966666638851, 0.400000005960, 0.983333349228, - 0.400000005960, 1.000000000000, 0.416666656733, 0.000000000000, - 0.416666656733, 0.016666667536, 0.416666656733, 0.033333335072, - 0.416666656733, 0.050000000745, 0.416666656733, 0.066666670144, - 0.416666656733, 0.083333335817, 0.416666656733, 0.100000001490, - 0.416666656733, 0.116666667163, 0.416666656733, 0.133333340287, - 0.416666656733, 0.150000005960, 0.416666656733, 0.166666671634, - 0.416666656733, 0.183333337307, 0.416666656733, 0.200000002980, - 0.416666656733, 0.216666668653, 0.416666656733, 0.233333334327, - 0.416666656733, 0.250000000000, 0.416666656733, 0.266666680574, - 0.416666656733, 0.283333331347, 0.416666656733, 0.300000011921, - 0.416666656733, 0.316666662693, 0.416666656733, 0.333333343267, - 0.416666656733, 0.349999994040, 0.416666656733, 0.366666674614, - 0.416666656733, 0.383333325386, 0.416666656733, 0.400000005960, - 0.416666656733, 0.416666656733, 0.416666656733, 0.433333337307, - 0.416666656733, 0.449999988079, 0.416666656733, 0.466666668653, - 0.416666656733, 0.483333319426, 0.416666656733, 0.500000000000, - 0.416666656733, 0.516666650772, 0.416666656733, 0.533333361149, - 0.416666656733, 0.550000011921, 0.416666656733, 0.566666662693, - 0.416666656733, 0.583333313465, 0.416666656733, 0.600000023842, - 0.416666656733, 0.616666674614, 0.416666656733, 0.633333325386, - 0.416666656733, 0.649999976158, 0.416666656733, 0.666666686535, - 0.416666656733, 0.683333337307, 0.416666656733, 0.699999988079, - 0.416666656733, 0.716666638851, 0.416666656733, 0.733333349228, - 0.416666656733, 0.750000000000, 0.416666656733, 0.766666650772, - 0.416666656733, 0.783333361149, 0.416666656733, 0.800000011921, - 0.416666656733, 0.816666662693, 0.416666656733, 0.833333313465, - 0.416666656733, 0.850000023842, 0.416666656733, 0.866666674614, - 0.416666656733, 0.883333325386, 0.416666656733, 0.899999976158, - 0.416666656733, 0.916666686535, 0.416666656733, 0.933333337307, - 0.416666656733, 0.949999988079, 0.416666656733, 0.966666638851, - 0.416666656733, 0.983333349228, 0.416666656733, 1.000000000000, - 0.433333337307, 0.000000000000, 0.433333337307, 0.016666667536, - 0.433333337307, 0.033333335072, 0.433333337307, 0.050000000745, - 0.433333337307, 0.066666670144, 0.433333337307, 0.083333335817, - 0.433333337307, 0.100000001490, 0.433333337307, 0.116666667163, - 0.433333337307, 0.133333340287, 0.433333337307, 0.150000005960, - 0.433333337307, 0.166666671634, 0.433333337307, 0.183333337307, - 0.433333337307, 0.200000002980, 0.433333337307, 0.216666668653, - 0.433333337307, 0.233333334327, 0.433333337307, 0.250000000000, - 0.433333337307, 0.266666680574, 0.433333337307, 0.283333331347, - 0.433333337307, 0.300000011921, 0.433333337307, 0.316666662693, - 0.433333337307, 0.333333343267, 0.433333337307, 0.349999994040, - 0.433333337307, 0.366666674614, 0.433333337307, 0.383333325386, - 0.433333337307, 0.400000005960, 0.433333337307, 0.416666656733, - 0.433333337307, 0.433333337307, 0.433333337307, 0.449999988079, - 0.433333337307, 0.466666668653, 0.433333337307, 0.483333319426, - 0.433333337307, 0.500000000000, 0.433333337307, 0.516666650772, - 0.433333337307, 0.533333361149, 0.433333337307, 0.550000011921, - 0.433333337307, 0.566666662693, 0.433333337307, 0.583333313465, - 0.433333337307, 0.600000023842, 0.433333337307, 0.616666674614, - 0.433333337307, 0.633333325386, 0.433333337307, 0.649999976158, - 0.433333337307, 0.666666686535, 0.433333337307, 0.683333337307, - 0.433333337307, 0.699999988079, 0.433333337307, 0.716666638851, - 0.433333337307, 0.733333349228, 0.433333337307, 0.750000000000, - 0.433333337307, 0.766666650772, 0.433333337307, 0.783333361149, - 0.433333337307, 0.800000011921, 0.433333337307, 0.816666662693, - 0.433333337307, 0.833333313465, 0.433333337307, 0.850000023842, - 0.433333337307, 0.866666674614, 0.433333337307, 0.883333325386, - 0.433333337307, 0.899999976158, 0.433333337307, 0.916666686535, - 0.433333337307, 0.933333337307, 0.433333337307, 0.949999988079, - 0.433333337307, 0.966666638851, 0.433333337307, 0.983333349228, - 0.433333337307, 1.000000000000, 0.449999988079, 0.000000000000, - 0.449999988079, 0.016666667536, 0.449999988079, 0.033333335072, - 0.449999988079, 0.050000000745, 0.449999988079, 0.066666670144, - 0.449999988079, 0.083333335817, 0.449999988079, 0.100000001490, - 0.449999988079, 0.116666667163, 0.449999988079, 0.133333340287, - 0.449999988079, 0.150000005960, 0.449999988079, 0.166666671634, - 0.449999988079, 0.183333337307, 0.449999988079, 0.200000002980, - 0.449999988079, 0.216666668653, 0.449999988079, 0.233333334327, - 0.449999988079, 0.250000000000, 0.449999988079, 0.266666680574, - 0.449999988079, 0.283333331347, 0.449999988079, 0.300000011921, - 0.449999988079, 0.316666662693, 0.449999988079, 0.333333343267, - 0.449999988079, 0.349999994040, 0.449999988079, 0.366666674614, - 0.449999988079, 0.383333325386, 0.449999988079, 0.400000005960, - 0.449999988079, 0.416666656733, 0.449999988079, 0.433333337307, - 0.449999988079, 0.449999988079, 0.449999988079, 0.466666668653, - 0.449999988079, 0.483333319426, 0.449999988079, 0.500000000000, - 0.449999988079, 0.516666650772, 0.449999988079, 0.533333361149, - 0.449999988079, 0.550000011921, 0.449999988079, 0.566666662693, - 0.449999988079, 0.583333313465, 0.449999988079, 0.600000023842, - 0.449999988079, 0.616666674614, 0.449999988079, 0.633333325386, - 0.449999988079, 0.649999976158, 0.449999988079, 0.666666686535, - 0.449999988079, 0.683333337307, 0.449999988079, 0.699999988079, - 0.449999988079, 0.716666638851, 0.449999988079, 0.733333349228, - 0.449999988079, 0.750000000000, 0.449999988079, 0.766666650772, - 0.449999988079, 0.783333361149, 0.449999988079, 0.800000011921, - 0.449999988079, 0.816666662693, 0.449999988079, 0.833333313465, - 0.449999988079, 0.850000023842, 0.449999988079, 0.866666674614, - 0.449999988079, 0.883333325386, 0.449999988079, 0.899999976158, - 0.449999988079, 0.916666686535, 0.449999988079, 0.933333337307, - 0.449999988079, 0.949999988079, 0.449999988079, 0.966666638851, - 0.449999988079, 0.983333349228, 0.449999988079, 1.000000000000, - 0.466666668653, 0.000000000000, 0.466666668653, 0.016666667536, - 0.466666668653, 0.033333335072, 0.466666668653, 0.050000000745, - 0.466666668653, 0.066666670144, 0.466666668653, 0.083333335817, - 0.466666668653, 0.100000001490, 0.466666668653, 0.116666667163, - 0.466666668653, 0.133333340287, 0.466666668653, 0.150000005960, - 0.466666668653, 0.166666671634, 0.466666668653, 0.183333337307, - 0.466666668653, 0.200000002980, 0.466666668653, 0.216666668653, - 0.466666668653, 0.233333334327, 0.466666668653, 0.250000000000, - 0.466666668653, 0.266666680574, 0.466666668653, 0.283333331347, - 0.466666668653, 0.300000011921, 0.466666668653, 0.316666662693, - 0.466666668653, 0.333333343267, 0.466666668653, 0.349999994040, - 0.466666668653, 0.366666674614, 0.466666668653, 0.383333325386, - 0.466666668653, 0.400000005960, 0.466666668653, 0.416666656733, - 0.466666668653, 0.433333337307, 0.466666668653, 0.449999988079, - 0.466666668653, 0.466666668653, 0.466666668653, 0.483333319426, - 0.466666668653, 0.500000000000, 0.466666668653, 0.516666650772, - 0.466666668653, 0.533333361149, 0.466666668653, 0.550000011921, - 0.466666668653, 0.566666662693, 0.466666668653, 0.583333313465, - 0.466666668653, 0.600000023842, 0.466666668653, 0.616666674614, - 0.466666668653, 0.633333325386, 0.466666668653, 0.649999976158, - 0.466666668653, 0.666666686535, 0.466666668653, 0.683333337307, - 0.466666668653, 0.699999988079, 0.466666668653, 0.716666638851, - 0.466666668653, 0.733333349228, 0.466666668653, 0.750000000000, - 0.466666668653, 0.766666650772, 0.466666668653, 0.783333361149, - 0.466666668653, 0.800000011921, 0.466666668653, 0.816666662693, - 0.466666668653, 0.833333313465, 0.466666668653, 0.850000023842, - 0.466666668653, 0.866666674614, 0.466666668653, 0.883333325386, - 0.466666668653, 0.899999976158, 0.466666668653, 0.916666686535, - 0.466666668653, 0.933333337307, 0.466666668653, 0.949999988079, - 0.466666668653, 0.966666638851, 0.466666668653, 0.983333349228, - 0.466666668653, 1.000000000000, 0.483333319426, 0.000000000000, - 0.483333319426, 0.016666667536, 0.483333319426, 0.033333335072, - 0.483333319426, 0.050000000745, 0.483333319426, 0.066666670144, - 0.483333319426, 0.083333335817, 0.483333319426, 0.100000001490, - 0.483333319426, 0.116666667163, 0.483333319426, 0.133333340287, - 0.483333319426, 0.150000005960, 0.483333319426, 0.166666671634, - 0.483333319426, 0.183333337307, 0.483333319426, 0.200000002980, - 0.483333319426, 0.216666668653, 0.483333319426, 0.233333334327, - 0.483333319426, 0.250000000000, 0.483333319426, 0.266666680574, - 0.483333319426, 0.283333331347, 0.483333319426, 0.300000011921, - 0.483333319426, 0.316666662693, 0.483333319426, 0.333333343267, - 0.483333319426, 0.349999994040, 0.483333319426, 0.366666674614, - 0.483333319426, 0.383333325386, 0.483333319426, 0.400000005960, - 0.483333319426, 0.416666656733, 0.483333319426, 0.433333337307, - 0.483333319426, 0.449999988079, 0.483333319426, 0.466666668653, - 0.483333319426, 0.483333319426, 0.483333319426, 0.500000000000, - 0.483333319426, 0.516666650772, 0.483333319426, 0.533333361149, - 0.483333319426, 0.550000011921, 0.483333319426, 0.566666662693, - 0.483333319426, 0.583333313465, 0.483333319426, 0.600000023842, - 0.483333319426, 0.616666674614, 0.483333319426, 0.633333325386, - 0.483333319426, 0.649999976158, 0.483333319426, 0.666666686535, - 0.483333319426, 0.683333337307, 0.483333319426, 0.699999988079, - 0.483333319426, 0.716666638851, 0.483333319426, 0.733333349228, - 0.483333319426, 0.750000000000, 0.483333319426, 0.766666650772, - 0.483333319426, 0.783333361149, 0.483333319426, 0.800000011921, - 0.483333319426, 0.816666662693, 0.483333319426, 0.833333313465, - 0.483333319426, 0.850000023842, 0.483333319426, 0.866666674614, - 0.483333319426, 0.883333325386, 0.483333319426, 0.899999976158, - 0.483333319426, 0.916666686535, 0.483333319426, 0.933333337307, - 0.483333319426, 0.949999988079, 0.483333319426, 0.966666638851, - 0.483333319426, 0.983333349228, 0.483333319426, 1.000000000000, - 0.500000000000, 0.000000000000, 0.500000000000, 0.016666667536, - 0.500000000000, 0.033333335072, 0.500000000000, 0.050000000745, - 0.500000000000, 0.066666670144, 0.500000000000, 0.083333335817, - 0.500000000000, 0.100000001490, 0.500000000000, 0.116666667163, - 0.500000000000, 0.133333340287, 0.500000000000, 0.150000005960, - 0.500000000000, 0.166666671634, 0.500000000000, 0.183333337307, - 0.500000000000, 0.200000002980, 0.500000000000, 0.216666668653, - 0.500000000000, 0.233333334327, 0.500000000000, 0.250000000000, - 0.500000000000, 0.266666680574, 0.500000000000, 0.283333331347, - 0.500000000000, 0.300000011921, 0.500000000000, 0.316666662693, - 0.500000000000, 0.333333343267, 0.500000000000, 0.349999994040, - 0.500000000000, 0.366666674614, 0.500000000000, 0.383333325386, - 0.500000000000, 0.400000005960, 0.500000000000, 0.416666656733, - 0.500000000000, 0.433333337307, 0.500000000000, 0.449999988079, - 0.500000000000, 0.466666668653, 0.500000000000, 0.483333319426, - 0.500000000000, 0.500000000000, 0.500000000000, 0.516666650772, - 0.500000000000, 0.533333361149, 0.500000000000, 0.550000011921, - 0.500000000000, 0.566666662693, 0.500000000000, 0.583333313465, - 0.500000000000, 0.600000023842, 0.500000000000, 0.616666674614, - 0.500000000000, 0.633333325386, 0.500000000000, 0.649999976158, - 0.500000000000, 0.666666686535, 0.500000000000, 0.683333337307, - 0.500000000000, 0.699999988079, 0.500000000000, 0.716666638851, - 0.500000000000, 0.733333349228, 0.500000000000, 0.750000000000, - 0.500000000000, 0.766666650772, 0.500000000000, 0.783333361149, - 0.500000000000, 0.800000011921, 0.500000000000, 0.816666662693, - 0.500000000000, 0.833333313465, 0.500000000000, 0.850000023842, - 0.500000000000, 0.866666674614, 0.500000000000, 0.883333325386, - 0.500000000000, 0.899999976158, 0.500000000000, 0.916666686535, - 0.500000000000, 0.933333337307, 0.500000000000, 0.949999988079, - 0.500000000000, 0.966666638851, 0.500000000000, 0.983333349228, - 0.500000000000, 1.000000000000, 0.516666650772, 0.000000000000, - 0.516666650772, 0.016666667536, 0.516666650772, 0.033333335072, - 0.516666650772, 0.050000000745, 0.516666650772, 0.066666670144, - 0.516666650772, 0.083333335817, 0.516666650772, 0.100000001490, - 0.516666650772, 0.116666667163, 0.516666650772, 0.133333340287, - 0.516666650772, 0.150000005960, 0.516666650772, 0.166666671634, - 0.516666650772, 0.183333337307, 0.516666650772, 0.200000002980, - 0.516666650772, 0.216666668653, 0.516666650772, 0.233333334327, - 0.516666650772, 0.250000000000, 0.516666650772, 0.266666680574, - 0.516666650772, 0.283333331347, 0.516666650772, 0.300000011921, - 0.516666650772, 0.316666662693, 0.516666650772, 0.333333343267, - 0.516666650772, 0.349999994040, 0.516666650772, 0.366666674614, - 0.516666650772, 0.383333325386, 0.516666650772, 0.400000005960, - 0.516666650772, 0.416666656733, 0.516666650772, 0.433333337307, - 0.516666650772, 0.449999988079, 0.516666650772, 0.466666668653, - 0.516666650772, 0.483333319426, 0.516666650772, 0.500000000000, - 0.516666650772, 0.516666650772, 0.516666650772, 0.533333361149, - 0.516666650772, 0.550000011921, 0.516666650772, 0.566666662693, - 0.516666650772, 0.583333313465, 0.516666650772, 0.600000023842, - 0.516666650772, 0.616666674614, 0.516666650772, 0.633333325386, - 0.516666650772, 0.649999976158, 0.516666650772, 0.666666686535, - 0.516666650772, 0.683333337307, 0.516666650772, 0.699999988079, - 0.516666650772, 0.716666638851, 0.516666650772, 0.733333349228, - 0.516666650772, 0.750000000000, 0.516666650772, 0.766666650772, - 0.516666650772, 0.783333361149, 0.516666650772, 0.800000011921, - 0.516666650772, 0.816666662693, 0.516666650772, 0.833333313465, - 0.516666650772, 0.850000023842, 0.516666650772, 0.866666674614, - 0.516666650772, 0.883333325386, 0.516666650772, 0.899999976158, - 0.516666650772, 0.916666686535, 0.516666650772, 0.933333337307, - 0.516666650772, 0.949999988079, 0.516666650772, 0.966666638851, - 0.516666650772, 0.983333349228, 0.516666650772, 1.000000000000, - 0.533333361149, 0.000000000000, 0.533333361149, 0.016666667536, - 0.533333361149, 0.033333335072, 0.533333361149, 0.050000000745, - 0.533333361149, 0.066666670144, 0.533333361149, 0.083333335817, - 0.533333361149, 0.100000001490, 0.533333361149, 0.116666667163, - 0.533333361149, 0.133333340287, 0.533333361149, 0.150000005960, - 0.533333361149, 0.166666671634, 0.533333361149, 0.183333337307, - 0.533333361149, 0.200000002980, 0.533333361149, 0.216666668653, - 0.533333361149, 0.233333334327, 0.533333361149, 0.250000000000, - 0.533333361149, 0.266666680574, 0.533333361149, 0.283333331347, - 0.533333361149, 0.300000011921, 0.533333361149, 0.316666662693, - 0.533333361149, 0.333333343267, 0.533333361149, 0.349999994040, - 0.533333361149, 0.366666674614, 0.533333361149, 0.383333325386, - 0.533333361149, 0.400000005960, 0.533333361149, 0.416666656733, - 0.533333361149, 0.433333337307, 0.533333361149, 0.449999988079, - 0.533333361149, 0.466666668653, 0.533333361149, 0.483333319426, - 0.533333361149, 0.500000000000, 0.533333361149, 0.516666650772, - 0.533333361149, 0.533333361149, 0.533333361149, 0.550000011921, - 0.533333361149, 0.566666662693, 0.533333361149, 0.583333313465, - 0.533333361149, 0.600000023842, 0.533333361149, 0.616666674614, - 0.533333361149, 0.633333325386, 0.533333361149, 0.649999976158, - 0.533333361149, 0.666666686535, 0.533333361149, 0.683333337307, - 0.533333361149, 0.699999988079, 0.533333361149, 0.716666638851, - 0.533333361149, 0.733333349228, 0.533333361149, 0.750000000000, - 0.533333361149, 0.766666650772, 0.533333361149, 0.783333361149, - 0.533333361149, 0.800000011921, 0.533333361149, 0.816666662693, - 0.533333361149, 0.833333313465, 0.533333361149, 0.850000023842, - 0.533333361149, 0.866666674614, 0.533333361149, 0.883333325386, - 0.533333361149, 0.899999976158, 0.533333361149, 0.916666686535, - 0.533333361149, 0.933333337307, 0.533333361149, 0.949999988079, - 0.533333361149, 0.966666638851, 0.533333361149, 0.983333349228, - 0.533333361149, 1.000000000000, 0.550000011921, 0.000000000000, - 0.550000011921, 0.016666667536, 0.550000011921, 0.033333335072, - 0.550000011921, 0.050000000745, 0.550000011921, 0.066666670144, - 0.550000011921, 0.083333335817, 0.550000011921, 0.100000001490, - 0.550000011921, 0.116666667163, 0.550000011921, 0.133333340287, - 0.550000011921, 0.150000005960, 0.550000011921, 0.166666671634, - 0.550000011921, 0.183333337307, 0.550000011921, 0.200000002980, - 0.550000011921, 0.216666668653, 0.550000011921, 0.233333334327, - 0.550000011921, 0.250000000000, 0.550000011921, 0.266666680574, - 0.550000011921, 0.283333331347, 0.550000011921, 0.300000011921, - 0.550000011921, 0.316666662693, 0.550000011921, 0.333333343267, - 0.550000011921, 0.349999994040, 0.550000011921, 0.366666674614, - 0.550000011921, 0.383333325386, 0.550000011921, 0.400000005960, - 0.550000011921, 0.416666656733, 0.550000011921, 0.433333337307, - 0.550000011921, 0.449999988079, 0.550000011921, 0.466666668653, - 0.550000011921, 0.483333319426, 0.550000011921, 0.500000000000, - 0.550000011921, 0.516666650772, 0.550000011921, 0.533333361149, - 0.550000011921, 0.550000011921, 0.550000011921, 0.566666662693, - 0.550000011921, 0.583333313465, 0.550000011921, 0.600000023842, - 0.550000011921, 0.616666674614, 0.550000011921, 0.633333325386, - 0.550000011921, 0.649999976158, 0.550000011921, 0.666666686535, - 0.550000011921, 0.683333337307, 0.550000011921, 0.699999988079, - 0.550000011921, 0.716666638851, 0.550000011921, 0.733333349228, - 0.550000011921, 0.750000000000, 0.550000011921, 0.766666650772, - 0.550000011921, 0.783333361149, 0.550000011921, 0.800000011921, - 0.550000011921, 0.816666662693, 0.550000011921, 0.833333313465, - 0.550000011921, 0.850000023842, 0.550000011921, 0.866666674614, - 0.550000011921, 0.883333325386, 0.550000011921, 0.899999976158, - 0.550000011921, 0.916666686535, 0.550000011921, 0.933333337307, - 0.550000011921, 0.949999988079, 0.550000011921, 0.966666638851, - 0.550000011921, 0.983333349228, 0.550000011921, 1.000000000000, - 0.566666662693, 0.000000000000, 0.566666662693, 0.016666667536, - 0.566666662693, 0.033333335072, 0.566666662693, 0.050000000745, - 0.566666662693, 0.066666670144, 0.566666662693, 0.083333335817, - 0.566666662693, 0.100000001490, 0.566666662693, 0.116666667163, - 0.566666662693, 0.133333340287, 0.566666662693, 0.150000005960, - 0.566666662693, 0.166666671634, 0.566666662693, 0.183333337307, - 0.566666662693, 0.200000002980, 0.566666662693, 0.216666668653, - 0.566666662693, 0.233333334327, 0.566666662693, 0.250000000000, - 0.566666662693, 0.266666680574, 0.566666662693, 0.283333331347, - 0.566666662693, 0.300000011921, 0.566666662693, 0.316666662693, - 0.566666662693, 0.333333343267, 0.566666662693, 0.349999994040, - 0.566666662693, 0.366666674614, 0.566666662693, 0.383333325386, - 0.566666662693, 0.400000005960, 0.566666662693, 0.416666656733, - 0.566666662693, 0.433333337307, 0.566666662693, 0.449999988079, - 0.566666662693, 0.466666668653, 0.566666662693, 0.483333319426, - 0.566666662693, 0.500000000000, 0.566666662693, 0.516666650772, - 0.566666662693, 0.533333361149, 0.566666662693, 0.550000011921, - 0.566666662693, 0.566666662693, 0.566666662693, 0.583333313465, - 0.566666662693, 0.600000023842, 0.566666662693, 0.616666674614, - 0.566666662693, 0.633333325386, 0.566666662693, 0.649999976158, - 0.566666662693, 0.666666686535, 0.566666662693, 0.683333337307, - 0.566666662693, 0.699999988079, 0.566666662693, 0.716666638851, - 0.566666662693, 0.733333349228, 0.566666662693, 0.750000000000, - 0.566666662693, 0.766666650772, 0.566666662693, 0.783333361149, - 0.566666662693, 0.800000011921, 0.566666662693, 0.816666662693, - 0.566666662693, 0.833333313465, 0.566666662693, 0.850000023842, - 0.566666662693, 0.866666674614, 0.566666662693, 0.883333325386, - 0.566666662693, 0.899999976158, 0.566666662693, 0.916666686535, - 0.566666662693, 0.933333337307, 0.566666662693, 0.949999988079, - 0.566666662693, 0.966666638851, 0.566666662693, 0.983333349228, - 0.566666662693, 1.000000000000, 0.583333313465, 0.000000000000, - 0.583333313465, 0.016666667536, 0.583333313465, 0.033333335072, - 0.583333313465, 0.050000000745, 0.583333313465, 0.066666670144, - 0.583333313465, 0.083333335817, 0.583333313465, 0.100000001490, - 0.583333313465, 0.116666667163, 0.583333313465, 0.133333340287, - 0.583333313465, 0.150000005960, 0.583333313465, 0.166666671634, - 0.583333313465, 0.183333337307, 0.583333313465, 0.200000002980, - 0.583333313465, 0.216666668653, 0.583333313465, 0.233333334327, - 0.583333313465, 0.250000000000, 0.583333313465, 0.266666680574, - 0.583333313465, 0.283333331347, 0.583333313465, 0.300000011921, - 0.583333313465, 0.316666662693, 0.583333313465, 0.333333343267, - 0.583333313465, 0.349999994040, 0.583333313465, 0.366666674614, - 0.583333313465, 0.383333325386, 0.583333313465, 0.400000005960, - 0.583333313465, 0.416666656733, 0.583333313465, 0.433333337307, - 0.583333313465, 0.449999988079, 0.583333313465, 0.466666668653, - 0.583333313465, 0.483333319426, 0.583333313465, 0.500000000000, - 0.583333313465, 0.516666650772, 0.583333313465, 0.533333361149, - 0.583333313465, 0.550000011921, 0.583333313465, 0.566666662693, - 0.583333313465, 0.583333313465, 0.583333313465, 0.600000023842, - 0.583333313465, 0.616666674614, 0.583333313465, 0.633333325386, - 0.583333313465, 0.649999976158, 0.583333313465, 0.666666686535, - 0.583333313465, 0.683333337307, 0.583333313465, 0.699999988079, - 0.583333313465, 0.716666638851, 0.583333313465, 0.733333349228, - 0.583333313465, 0.750000000000, 0.583333313465, 0.766666650772, - 0.583333313465, 0.783333361149, 0.583333313465, 0.800000011921, - 0.583333313465, 0.816666662693, 0.583333313465, 0.833333313465, - 0.583333313465, 0.850000023842, 0.583333313465, 0.866666674614, - 0.583333313465, 0.883333325386, 0.583333313465, 0.899999976158, - 0.583333313465, 0.916666686535, 0.583333313465, 0.933333337307, - 0.583333313465, 0.949999988079, 0.583333313465, 0.966666638851, - 0.583333313465, 0.983333349228, 0.583333313465, 1.000000000000, - 0.600000023842, 0.000000000000, 0.600000023842, 0.016666667536, - 0.600000023842, 0.033333335072, 0.600000023842, 0.050000000745, - 0.600000023842, 0.066666670144, 0.600000023842, 0.083333335817, - 0.600000023842, 0.100000001490, 0.600000023842, 0.116666667163, - 0.600000023842, 0.133333340287, 0.600000023842, 0.150000005960, - 0.600000023842, 0.166666671634, 0.600000023842, 0.183333337307, - 0.600000023842, 0.200000002980, 0.600000023842, 0.216666668653, - 0.600000023842, 0.233333334327, 0.600000023842, 0.250000000000, - 0.600000023842, 0.266666680574, 0.600000023842, 0.283333331347, - 0.600000023842, 0.300000011921, 0.600000023842, 0.316666662693, - 0.600000023842, 0.333333343267, 0.600000023842, 0.349999994040, - 0.600000023842, 0.366666674614, 0.600000023842, 0.383333325386, - 0.600000023842, 0.400000005960, 0.600000023842, 0.416666656733, - 0.600000023842, 0.433333337307, 0.600000023842, 0.449999988079, - 0.600000023842, 0.466666668653, 0.600000023842, 0.483333319426, - 0.600000023842, 0.500000000000, 0.600000023842, 0.516666650772, - 0.600000023842, 0.533333361149, 0.600000023842, 0.550000011921, - 0.600000023842, 0.566666662693, 0.600000023842, 0.583333313465, - 0.600000023842, 0.600000023842, 0.600000023842, 0.616666674614, - 0.600000023842, 0.633333325386, 0.600000023842, 0.649999976158, - 0.600000023842, 0.666666686535, 0.600000023842, 0.683333337307, - 0.600000023842, 0.699999988079, 0.600000023842, 0.716666638851, - 0.600000023842, 0.733333349228, 0.600000023842, 0.750000000000, - 0.600000023842, 0.766666650772, 0.600000023842, 0.783333361149, - 0.600000023842, 0.800000011921, 0.600000023842, 0.816666662693, - 0.600000023842, 0.833333313465, 0.600000023842, 0.850000023842, - 0.600000023842, 0.866666674614, 0.600000023842, 0.883333325386, - 0.600000023842, 0.899999976158, 0.600000023842, 0.916666686535, - 0.600000023842, 0.933333337307, 0.600000023842, 0.949999988079, - 0.600000023842, 0.966666638851, 0.600000023842, 0.983333349228, - 0.600000023842, 1.000000000000, 0.616666674614, 0.000000000000, - 0.616666674614, 0.016666667536, 0.616666674614, 0.033333335072, - 0.616666674614, 0.050000000745, 0.616666674614, 0.066666670144, - 0.616666674614, 0.083333335817, 0.616666674614, 0.100000001490, - 0.616666674614, 0.116666667163, 0.616666674614, 0.133333340287, - 0.616666674614, 0.150000005960, 0.616666674614, 0.166666671634, - 0.616666674614, 0.183333337307, 0.616666674614, 0.200000002980, - 0.616666674614, 0.216666668653, 0.616666674614, 0.233333334327, - 0.616666674614, 0.250000000000, 0.616666674614, 0.266666680574, - 0.616666674614, 0.283333331347, 0.616666674614, 0.300000011921, - 0.616666674614, 0.316666662693, 0.616666674614, 0.333333343267, - 0.616666674614, 0.349999994040, 0.616666674614, 0.366666674614, - 0.616666674614, 0.383333325386, 0.616666674614, 0.400000005960, - 0.616666674614, 0.416666656733, 0.616666674614, 0.433333337307, - 0.616666674614, 0.449999988079, 0.616666674614, 0.466666668653, - 0.616666674614, 0.483333319426, 0.616666674614, 0.500000000000, - 0.616666674614, 0.516666650772, 0.616666674614, 0.533333361149, - 0.616666674614, 0.550000011921, 0.616666674614, 0.566666662693, - 0.616666674614, 0.583333313465, 0.616666674614, 0.600000023842, - 0.616666674614, 0.616666674614, 0.616666674614, 0.633333325386, - 0.616666674614, 0.649999976158, 0.616666674614, 0.666666686535, - 0.616666674614, 0.683333337307, 0.616666674614, 0.699999988079, - 0.616666674614, 0.716666638851, 0.616666674614, 0.733333349228, - 0.616666674614, 0.750000000000, 0.616666674614, 0.766666650772, - 0.616666674614, 0.783333361149, 0.616666674614, 0.800000011921, - 0.616666674614, 0.816666662693, 0.616666674614, 0.833333313465, - 0.616666674614, 0.850000023842, 0.616666674614, 0.866666674614, - 0.616666674614, 0.883333325386, 0.616666674614, 0.899999976158, - 0.616666674614, 0.916666686535, 0.616666674614, 0.933333337307, - 0.616666674614, 0.949999988079, 0.616666674614, 0.966666638851, - 0.616666674614, 0.983333349228, 0.616666674614, 1.000000000000, - 0.633333325386, 0.000000000000, 0.633333325386, 0.016666667536, - 0.633333325386, 0.033333335072, 0.633333325386, 0.050000000745, - 0.633333325386, 0.066666670144, 0.633333325386, 0.083333335817, - 0.633333325386, 0.100000001490, 0.633333325386, 0.116666667163, - 0.633333325386, 0.133333340287, 0.633333325386, 0.150000005960, - 0.633333325386, 0.166666671634, 0.633333325386, 0.183333337307, - 0.633333325386, 0.200000002980, 0.633333325386, 0.216666668653, - 0.633333325386, 0.233333334327, 0.633333325386, 0.250000000000, - 0.633333325386, 0.266666680574, 0.633333325386, 0.283333331347, - 0.633333325386, 0.300000011921, 0.633333325386, 0.316666662693, - 0.633333325386, 0.333333343267, 0.633333325386, 0.349999994040, - 0.633333325386, 0.366666674614, 0.633333325386, 0.383333325386, - 0.633333325386, 0.400000005960, 0.633333325386, 0.416666656733, - 0.633333325386, 0.433333337307, 0.633333325386, 0.449999988079, - 0.633333325386, 0.466666668653, 0.633333325386, 0.483333319426, - 0.633333325386, 0.500000000000, 0.633333325386, 0.516666650772, - 0.633333325386, 0.533333361149, 0.633333325386, 0.550000011921, - 0.633333325386, 0.566666662693, 0.633333325386, 0.583333313465, - 0.633333325386, 0.600000023842, 0.633333325386, 0.616666674614, - 0.633333325386, 0.633333325386, 0.633333325386, 0.649999976158, - 0.633333325386, 0.666666686535, 0.633333325386, 0.683333337307, - 0.633333325386, 0.699999988079, 0.633333325386, 0.716666638851, - 0.633333325386, 0.733333349228, 0.633333325386, 0.750000000000, - 0.633333325386, 0.766666650772, 0.633333325386, 0.783333361149, - 0.633333325386, 0.800000011921, 0.633333325386, 0.816666662693, - 0.633333325386, 0.833333313465, 0.633333325386, 0.850000023842, - 0.633333325386, 0.866666674614, 0.633333325386, 0.883333325386, - 0.633333325386, 0.899999976158, 0.633333325386, 0.916666686535, - 0.633333325386, 0.933333337307, 0.633333325386, 0.949999988079, - 0.633333325386, 0.966666638851, 0.633333325386, 0.983333349228, - 0.633333325386, 1.000000000000, 0.649999976158, 0.000000000000, - 0.649999976158, 0.016666667536, 0.649999976158, 0.033333335072, - 0.649999976158, 0.050000000745, 0.649999976158, 0.066666670144, - 0.649999976158, 0.083333335817, 0.649999976158, 0.100000001490, - 0.649999976158, 0.116666667163, 0.649999976158, 0.133333340287, - 0.649999976158, 0.150000005960, 0.649999976158, 0.166666671634, - 0.649999976158, 0.183333337307, 0.649999976158, 0.200000002980, - 0.649999976158, 0.216666668653, 0.649999976158, 0.233333334327, - 0.649999976158, 0.250000000000, 0.649999976158, 0.266666680574, - 0.649999976158, 0.283333331347, 0.649999976158, 0.300000011921, - 0.649999976158, 0.316666662693, 0.649999976158, 0.333333343267, - 0.649999976158, 0.349999994040, 0.649999976158, 0.366666674614, - 0.649999976158, 0.383333325386, 0.649999976158, 0.400000005960, - 0.649999976158, 0.416666656733, 0.649999976158, 0.433333337307, - 0.649999976158, 0.449999988079, 0.649999976158, 0.466666668653, - 0.649999976158, 0.483333319426, 0.649999976158, 0.500000000000, - 0.649999976158, 0.516666650772, 0.649999976158, 0.533333361149, - 0.649999976158, 0.550000011921, 0.649999976158, 0.566666662693, - 0.649999976158, 0.583333313465, 0.649999976158, 0.600000023842, - 0.649999976158, 0.616666674614, 0.649999976158, 0.633333325386, - 0.649999976158, 0.649999976158, 0.649999976158, 0.666666686535, - 0.649999976158, 0.683333337307, 0.649999976158, 0.699999988079, - 0.649999976158, 0.716666638851, 0.649999976158, 0.733333349228, - 0.649999976158, 0.750000000000, 0.649999976158, 0.766666650772, - 0.649999976158, 0.783333361149, 0.649999976158, 0.800000011921, - 0.649999976158, 0.816666662693, 0.649999976158, 0.833333313465, - 0.649999976158, 0.850000023842, 0.649999976158, 0.866666674614, - 0.649999976158, 0.883333325386, 0.649999976158, 0.899999976158, - 0.649999976158, 0.916666686535, 0.649999976158, 0.933333337307, - 0.649999976158, 0.949999988079, 0.649999976158, 0.966666638851, - 0.649999976158, 0.983333349228, 0.649999976158, 1.000000000000, - 0.666666686535, 0.000000000000, 0.666666686535, 0.016666667536, - 0.666666686535, 0.033333335072, 0.666666686535, 0.050000000745, - 0.666666686535, 0.066666670144, 0.666666686535, 0.083333335817, - 0.666666686535, 0.100000001490, 0.666666686535, 0.116666667163, - 0.666666686535, 0.133333340287, 0.666666686535, 0.150000005960, - 0.666666686535, 0.166666671634, 0.666666686535, 0.183333337307, - 0.666666686535, 0.200000002980, 0.666666686535, 0.216666668653, - 0.666666686535, 0.233333334327, 0.666666686535, 0.250000000000, - 0.666666686535, 0.266666680574, 0.666666686535, 0.283333331347, - 0.666666686535, 0.300000011921, 0.666666686535, 0.316666662693, - 0.666666686535, 0.333333343267, 0.666666686535, 0.349999994040, - 0.666666686535, 0.366666674614, 0.666666686535, 0.383333325386, - 0.666666686535, 0.400000005960, 0.666666686535, 0.416666656733, - 0.666666686535, 0.433333337307, 0.666666686535, 0.449999988079, - 0.666666686535, 0.466666668653, 0.666666686535, 0.483333319426, - 0.666666686535, 0.500000000000, 0.666666686535, 0.516666650772, - 0.666666686535, 0.533333361149, 0.666666686535, 0.550000011921, - 0.666666686535, 0.566666662693, 0.666666686535, 0.583333313465, - 0.666666686535, 0.600000023842, 0.666666686535, 0.616666674614, - 0.666666686535, 0.633333325386, 0.666666686535, 0.649999976158, - 0.666666686535, 0.666666686535, 0.666666686535, 0.683333337307, - 0.666666686535, 0.699999988079, 0.666666686535, 0.716666638851, - 0.666666686535, 0.733333349228, 0.666666686535, 0.750000000000, - 0.666666686535, 0.766666650772, 0.666666686535, 0.783333361149, - 0.666666686535, 0.800000011921, 0.666666686535, 0.816666662693, - 0.666666686535, 0.833333313465, 0.666666686535, 0.850000023842, - 0.666666686535, 0.866666674614, 0.666666686535, 0.883333325386, - 0.666666686535, 0.899999976158, 0.666666686535, 0.916666686535, - 0.666666686535, 0.933333337307, 0.666666686535, 0.949999988079, - 0.666666686535, 0.966666638851, 0.666666686535, 0.983333349228, - 0.666666686535, 1.000000000000, 0.683333337307, 0.000000000000, - 0.683333337307, 0.016666667536, 0.683333337307, 0.033333335072, - 0.683333337307, 0.050000000745, 0.683333337307, 0.066666670144, - 0.683333337307, 0.083333335817, 0.683333337307, 0.100000001490, - 0.683333337307, 0.116666667163, 0.683333337307, 0.133333340287, - 0.683333337307, 0.150000005960, 0.683333337307, 0.166666671634, - 0.683333337307, 0.183333337307, 0.683333337307, 0.200000002980, - 0.683333337307, 0.216666668653, 0.683333337307, 0.233333334327, - 0.683333337307, 0.250000000000, 0.683333337307, 0.266666680574, - 0.683333337307, 0.283333331347, 0.683333337307, 0.300000011921, - 0.683333337307, 0.316666662693, 0.683333337307, 0.333333343267, - 0.683333337307, 0.349999994040, 0.683333337307, 0.366666674614, - 0.683333337307, 0.383333325386, 0.683333337307, 0.400000005960, - 0.683333337307, 0.416666656733, 0.683333337307, 0.433333337307, - 0.683333337307, 0.449999988079, 0.683333337307, 0.466666668653, - 0.683333337307, 0.483333319426, 0.683333337307, 0.500000000000, - 0.683333337307, 0.516666650772, 0.683333337307, 0.533333361149, - 0.683333337307, 0.550000011921, 0.683333337307, 0.566666662693, - 0.683333337307, 0.583333313465, 0.683333337307, 0.600000023842, - 0.683333337307, 0.616666674614, 0.683333337307, 0.633333325386, - 0.683333337307, 0.649999976158, 0.683333337307, 0.666666686535, - 0.683333337307, 0.683333337307, 0.683333337307, 0.699999988079, - 0.683333337307, 0.716666638851, 0.683333337307, 0.733333349228, - 0.683333337307, 0.750000000000, 0.683333337307, 0.766666650772, - 0.683333337307, 0.783333361149, 0.683333337307, 0.800000011921, - 0.683333337307, 0.816666662693, 0.683333337307, 0.833333313465, - 0.683333337307, 0.850000023842, 0.683333337307, 0.866666674614, - 0.683333337307, 0.883333325386, 0.683333337307, 0.899999976158, - 0.683333337307, 0.916666686535, 0.683333337307, 0.933333337307, - 0.683333337307, 0.949999988079, 0.683333337307, 0.966666638851, - 0.683333337307, 0.983333349228, 0.683333337307, 1.000000000000, - 0.699999988079, 0.000000000000, 0.699999988079, 0.016666667536, - 0.699999988079, 0.033333335072, 0.699999988079, 0.050000000745, - 0.699999988079, 0.066666670144, 0.699999988079, 0.083333335817, - 0.699999988079, 0.100000001490, 0.699999988079, 0.116666667163, - 0.699999988079, 0.133333340287, 0.699999988079, 0.150000005960, - 0.699999988079, 0.166666671634, 0.699999988079, 0.183333337307, - 0.699999988079, 0.200000002980, 0.699999988079, 0.216666668653, - 0.699999988079, 0.233333334327, 0.699999988079, 0.250000000000, - 0.699999988079, 0.266666680574, 0.699999988079, 0.283333331347, - 0.699999988079, 0.300000011921, 0.699999988079, 0.316666662693, - 0.699999988079, 0.333333343267, 0.699999988079, 0.349999994040, - 0.699999988079, 0.366666674614, 0.699999988079, 0.383333325386, - 0.699999988079, 0.400000005960, 0.699999988079, 0.416666656733, - 0.699999988079, 0.433333337307, 0.699999988079, 0.449999988079, - 0.699999988079, 0.466666668653, 0.699999988079, 0.483333319426, - 0.699999988079, 0.500000000000, 0.699999988079, 0.516666650772, - 0.699999988079, 0.533333361149, 0.699999988079, 0.550000011921, - 0.699999988079, 0.566666662693, 0.699999988079, 0.583333313465, - 0.699999988079, 0.600000023842, 0.699999988079, 0.616666674614, - 0.699999988079, 0.633333325386, 0.699999988079, 0.649999976158, - 0.699999988079, 0.666666686535, 0.699999988079, 0.683333337307, - 0.699999988079, 0.699999988079, 0.699999988079, 0.716666638851, - 0.699999988079, 0.733333349228, 0.699999988079, 0.750000000000, - 0.699999988079, 0.766666650772, 0.699999988079, 0.783333361149, - 0.699999988079, 0.800000011921, 0.699999988079, 0.816666662693, - 0.699999988079, 0.833333313465, 0.699999988079, 0.850000023842, - 0.699999988079, 0.866666674614, 0.699999988079, 0.883333325386, - 0.699999988079, 0.899999976158, 0.699999988079, 0.916666686535, - 0.699999988079, 0.933333337307, 0.699999988079, 0.949999988079, - 0.699999988079, 0.966666638851, 0.699999988079, 0.983333349228, - 0.699999988079, 1.000000000000, 0.716666638851, 0.000000000000, - 0.716666638851, 0.016666667536, 0.716666638851, 0.033333335072, - 0.716666638851, 0.050000000745, 0.716666638851, 0.066666670144, - 0.716666638851, 0.083333335817, 0.716666638851, 0.100000001490, - 0.716666638851, 0.116666667163, 0.716666638851, 0.133333340287, - 0.716666638851, 0.150000005960, 0.716666638851, 0.166666671634, - 0.716666638851, 0.183333337307, 0.716666638851, 0.200000002980, - 0.716666638851, 0.216666668653, 0.716666638851, 0.233333334327, - 0.716666638851, 0.250000000000, 0.716666638851, 0.266666680574, - 0.716666638851, 0.283333331347, 0.716666638851, 0.300000011921, - 0.716666638851, 0.316666662693, 0.716666638851, 0.333333343267, - 0.716666638851, 0.349999994040, 0.716666638851, 0.366666674614, - 0.716666638851, 0.383333325386, 0.716666638851, 0.400000005960, - 0.716666638851, 0.416666656733, 0.716666638851, 0.433333337307, - 0.716666638851, 0.449999988079, 0.716666638851, 0.466666668653, - 0.716666638851, 0.483333319426, 0.716666638851, 0.500000000000, - 0.716666638851, 0.516666650772, 0.716666638851, 0.533333361149, - 0.716666638851, 0.550000011921, 0.716666638851, 0.566666662693, - 0.716666638851, 0.583333313465, 0.716666638851, 0.600000023842, - 0.716666638851, 0.616666674614, 0.716666638851, 0.633333325386, - 0.716666638851, 0.649999976158, 0.716666638851, 0.666666686535, - 0.716666638851, 0.683333337307, 0.716666638851, 0.699999988079, - 0.716666638851, 0.716666638851, 0.716666638851, 0.733333349228, - 0.716666638851, 0.750000000000, 0.716666638851, 0.766666650772, - 0.716666638851, 0.783333361149, 0.716666638851, 0.800000011921, - 0.716666638851, 0.816666662693, 0.716666638851, 0.833333313465, - 0.716666638851, 0.850000023842, 0.716666638851, 0.866666674614, - 0.716666638851, 0.883333325386, 0.716666638851, 0.899999976158, - 0.716666638851, 0.916666686535, 0.716666638851, 0.933333337307, - 0.716666638851, 0.949999988079, 0.716666638851, 0.966666638851, - 0.716666638851, 0.983333349228, 0.716666638851, 1.000000000000, - 0.733333349228, 0.000000000000, 0.733333349228, 0.016666667536, - 0.733333349228, 0.033333335072, 0.733333349228, 0.050000000745, - 0.733333349228, 0.066666670144, 0.733333349228, 0.083333335817, - 0.733333349228, 0.100000001490, 0.733333349228, 0.116666667163, - 0.733333349228, 0.133333340287, 0.733333349228, 0.150000005960, - 0.733333349228, 0.166666671634, 0.733333349228, 0.183333337307, - 0.733333349228, 0.200000002980, 0.733333349228, 0.216666668653, - 0.733333349228, 0.233333334327, 0.733333349228, 0.250000000000, - 0.733333349228, 0.266666680574, 0.733333349228, 0.283333331347, - 0.733333349228, 0.300000011921, 0.733333349228, 0.316666662693, - 0.733333349228, 0.333333343267, 0.733333349228, 0.349999994040, - 0.733333349228, 0.366666674614, 0.733333349228, 0.383333325386, - 0.733333349228, 0.400000005960, 0.733333349228, 0.416666656733, - 0.733333349228, 0.433333337307, 0.733333349228, 0.449999988079, - 0.733333349228, 0.466666668653, 0.733333349228, 0.483333319426, - 0.733333349228, 0.500000000000, 0.733333349228, 0.516666650772, - 0.733333349228, 0.533333361149, 0.733333349228, 0.550000011921, - 0.733333349228, 0.566666662693, 0.733333349228, 0.583333313465, - 0.733333349228, 0.600000023842, 0.733333349228, 0.616666674614, - 0.733333349228, 0.633333325386, 0.733333349228, 0.649999976158, - 0.733333349228, 0.666666686535, 0.733333349228, 0.683333337307, - 0.733333349228, 0.699999988079, 0.733333349228, 0.716666638851, - 0.733333349228, 0.733333349228, 0.733333349228, 0.750000000000, - 0.733333349228, 0.766666650772, 0.733333349228, 0.783333361149, - 0.733333349228, 0.800000011921, 0.733333349228, 0.816666662693, - 0.733333349228, 0.833333313465, 0.733333349228, 0.850000023842, - 0.733333349228, 0.866666674614, 0.733333349228, 0.883333325386, - 0.733333349228, 0.899999976158, 0.733333349228, 0.916666686535, - 0.733333349228, 0.933333337307, 0.733333349228, 0.949999988079, - 0.733333349228, 0.966666638851, 0.733333349228, 0.983333349228, - 0.733333349228, 1.000000000000, 0.750000000000, 0.000000000000, - 0.750000000000, 0.016666667536, 0.750000000000, 0.033333335072, - 0.750000000000, 0.050000000745, 0.750000000000, 0.066666670144, - 0.750000000000, 0.083333335817, 0.750000000000, 0.100000001490, - 0.750000000000, 0.116666667163, 0.750000000000, 0.133333340287, - 0.750000000000, 0.150000005960, 0.750000000000, 0.166666671634, - 0.750000000000, 0.183333337307, 0.750000000000, 0.200000002980, - 0.750000000000, 0.216666668653, 0.750000000000, 0.233333334327, - 0.750000000000, 0.250000000000, 0.750000000000, 0.266666680574, - 0.750000000000, 0.283333331347, 0.750000000000, 0.300000011921, - 0.750000000000, 0.316666662693, 0.750000000000, 0.333333343267, - 0.750000000000, 0.349999994040, 0.750000000000, 0.366666674614, - 0.750000000000, 0.383333325386, 0.750000000000, 0.400000005960, - 0.750000000000, 0.416666656733, 0.750000000000, 0.433333337307, - 0.750000000000, 0.449999988079, 0.750000000000, 0.466666668653, - 0.750000000000, 0.483333319426, 0.750000000000, 0.500000000000, - 0.750000000000, 0.516666650772, 0.750000000000, 0.533333361149, - 0.750000000000, 0.550000011921, 0.750000000000, 0.566666662693, - 0.750000000000, 0.583333313465, 0.750000000000, 0.600000023842, - 0.750000000000, 0.616666674614, 0.750000000000, 0.633333325386, - 0.750000000000, 0.649999976158, 0.750000000000, 0.666666686535, - 0.750000000000, 0.683333337307, 0.750000000000, 0.699999988079, - 0.750000000000, 0.716666638851, 0.750000000000, 0.733333349228, - 0.750000000000, 0.750000000000, 0.750000000000, 0.766666650772, - 0.750000000000, 0.783333361149, 0.750000000000, 0.800000011921, - 0.750000000000, 0.816666662693, 0.750000000000, 0.833333313465, - 0.750000000000, 0.850000023842, 0.750000000000, 0.866666674614, - 0.750000000000, 0.883333325386, 0.750000000000, 0.899999976158, - 0.750000000000, 0.916666686535, 0.750000000000, 0.933333337307, - 0.750000000000, 0.949999988079, 0.750000000000, 0.966666638851, - 0.750000000000, 0.983333349228, 0.750000000000, 1.000000000000, - 0.766666650772, 0.000000000000, 0.766666650772, 0.016666667536, - 0.766666650772, 0.033333335072, 0.766666650772, 0.050000000745, - 0.766666650772, 0.066666670144, 0.766666650772, 0.083333335817, - 0.766666650772, 0.100000001490, 0.766666650772, 0.116666667163, - 0.766666650772, 0.133333340287, 0.766666650772, 0.150000005960, - 0.766666650772, 0.166666671634, 0.766666650772, 0.183333337307, - 0.766666650772, 0.200000002980, 0.766666650772, 0.216666668653, - 0.766666650772, 0.233333334327, 0.766666650772, 0.250000000000, - 0.766666650772, 0.266666680574, 0.766666650772, 0.283333331347, - 0.766666650772, 0.300000011921, 0.766666650772, 0.316666662693, - 0.766666650772, 0.333333343267, 0.766666650772, 0.349999994040, - 0.766666650772, 0.366666674614, 0.766666650772, 0.383333325386, - 0.766666650772, 0.400000005960, 0.766666650772, 0.416666656733, - 0.766666650772, 0.433333337307, 0.766666650772, 0.449999988079, - 0.766666650772, 0.466666668653, 0.766666650772, 0.483333319426, - 0.766666650772, 0.500000000000, 0.766666650772, 0.516666650772, - 0.766666650772, 0.533333361149, 0.766666650772, 0.550000011921, - 0.766666650772, 0.566666662693, 0.766666650772, 0.583333313465, - 0.766666650772, 0.600000023842, 0.766666650772, 0.616666674614, - 0.766666650772, 0.633333325386, 0.766666650772, 0.649999976158, - 0.766666650772, 0.666666686535, 0.766666650772, 0.683333337307, - 0.766666650772, 0.699999988079, 0.766666650772, 0.716666638851, - 0.766666650772, 0.733333349228, 0.766666650772, 0.750000000000, - 0.766666650772, 0.766666650772, 0.766666650772, 0.783333361149, - 0.766666650772, 0.800000011921, 0.766666650772, 0.816666662693, - 0.766666650772, 0.833333313465, 0.766666650772, 0.850000023842, - 0.766666650772, 0.866666674614, 0.766666650772, 0.883333325386, - 0.766666650772, 0.899999976158, 0.766666650772, 0.916666686535, - 0.766666650772, 0.933333337307, 0.766666650772, 0.949999988079, - 0.766666650772, 0.966666638851, 0.766666650772, 0.983333349228, - 0.766666650772, 1.000000000000, 0.783333361149, 0.000000000000, - 0.783333361149, 0.016666667536, 0.783333361149, 0.033333335072, - 0.783333361149, 0.050000000745, 0.783333361149, 0.066666670144, - 0.783333361149, 0.083333335817, 0.783333361149, 0.100000001490, - 0.783333361149, 0.116666667163, 0.783333361149, 0.133333340287, - 0.783333361149, 0.150000005960, 0.783333361149, 0.166666671634, - 0.783333361149, 0.183333337307, 0.783333361149, 0.200000002980, - 0.783333361149, 0.216666668653, 0.783333361149, 0.233333334327, - 0.783333361149, 0.250000000000, 0.783333361149, 0.266666680574, - 0.783333361149, 0.283333331347, 0.783333361149, 0.300000011921, - 0.783333361149, 0.316666662693, 0.783333361149, 0.333333343267, - 0.783333361149, 0.349999994040, 0.783333361149, 0.366666674614, - 0.783333361149, 0.383333325386, 0.783333361149, 0.400000005960, - 0.783333361149, 0.416666656733, 0.783333361149, 0.433333337307, - 0.783333361149, 0.449999988079, 0.783333361149, 0.466666668653, - 0.783333361149, 0.483333319426, 0.783333361149, 0.500000000000, - 0.783333361149, 0.516666650772, 0.783333361149, 0.533333361149, - 0.783333361149, 0.550000011921, 0.783333361149, 0.566666662693, - 0.783333361149, 0.583333313465, 0.783333361149, 0.600000023842, - 0.783333361149, 0.616666674614, 0.783333361149, 0.633333325386, - 0.783333361149, 0.649999976158, 0.783333361149, 0.666666686535, - 0.783333361149, 0.683333337307, 0.783333361149, 0.699999988079, - 0.783333361149, 0.716666638851, 0.783333361149, 0.733333349228, - 0.783333361149, 0.750000000000, 0.783333361149, 0.766666650772, - 0.783333361149, 0.783333361149, 0.783333361149, 0.800000011921, - 0.783333361149, 0.816666662693, 0.783333361149, 0.833333313465, - 0.783333361149, 0.850000023842, 0.783333361149, 0.866666674614, - 0.783333361149, 0.883333325386, 0.783333361149, 0.899999976158, - 0.783333361149, 0.916666686535, 0.783333361149, 0.933333337307, - 0.783333361149, 0.949999988079, 0.783333361149, 0.966666638851, - 0.783333361149, 0.983333349228, 0.783333361149, 1.000000000000, - 0.800000011921, 0.000000000000, 0.800000011921, 0.016666667536, - 0.800000011921, 0.033333335072, 0.800000011921, 0.050000000745, - 0.800000011921, 0.066666670144, 0.800000011921, 0.083333335817, - 0.800000011921, 0.100000001490, 0.800000011921, 0.116666667163, - 0.800000011921, 0.133333340287, 0.800000011921, 0.150000005960, - 0.800000011921, 0.166666671634, 0.800000011921, 0.183333337307, - 0.800000011921, 0.200000002980, 0.800000011921, 0.216666668653, - 0.800000011921, 0.233333334327, 0.800000011921, 0.250000000000, - 0.800000011921, 0.266666680574, 0.800000011921, 0.283333331347, - 0.800000011921, 0.300000011921, 0.800000011921, 0.316666662693, - 0.800000011921, 0.333333343267, 0.800000011921, 0.349999994040, - 0.800000011921, 0.366666674614, 0.800000011921, 0.383333325386, - 0.800000011921, 0.400000005960, 0.800000011921, 0.416666656733, - 0.800000011921, 0.433333337307, 0.800000011921, 0.449999988079, - 0.800000011921, 0.466666668653, 0.800000011921, 0.483333319426, - 0.800000011921, 0.500000000000, 0.800000011921, 0.516666650772, - 0.800000011921, 0.533333361149, 0.800000011921, 0.550000011921, - 0.800000011921, 0.566666662693, 0.800000011921, 0.583333313465, - 0.800000011921, 0.600000023842, 0.800000011921, 0.616666674614, - 0.800000011921, 0.633333325386, 0.800000011921, 0.649999976158, - 0.800000011921, 0.666666686535, 0.800000011921, 0.683333337307, - 0.800000011921, 0.699999988079, 0.800000011921, 0.716666638851, - 0.800000011921, 0.733333349228, 0.800000011921, 0.750000000000, - 0.800000011921, 0.766666650772, 0.800000011921, 0.783333361149, - 0.800000011921, 0.800000011921, 0.800000011921, 0.816666662693, - 0.800000011921, 0.833333313465, 0.800000011921, 0.850000023842, - 0.800000011921, 0.866666674614, 0.800000011921, 0.883333325386, - 0.800000011921, 0.899999976158, 0.800000011921, 0.916666686535, - 0.800000011921, 0.933333337307, 0.800000011921, 0.949999988079, - 0.800000011921, 0.966666638851, 0.800000011921, 0.983333349228, - 0.800000011921, 1.000000000000, 0.816666662693, 0.000000000000, - 0.816666662693, 0.016666667536, 0.816666662693, 0.033333335072, - 0.816666662693, 0.050000000745, 0.816666662693, 0.066666670144, - 0.816666662693, 0.083333335817, 0.816666662693, 0.100000001490, - 0.816666662693, 0.116666667163, 0.816666662693, 0.133333340287, - 0.816666662693, 0.150000005960, 0.816666662693, 0.166666671634, - 0.816666662693, 0.183333337307, 0.816666662693, 0.200000002980, - 0.816666662693, 0.216666668653, 0.816666662693, 0.233333334327, - 0.816666662693, 0.250000000000, 0.816666662693, 0.266666680574, - 0.816666662693, 0.283333331347, 0.816666662693, 0.300000011921, - 0.816666662693, 0.316666662693, 0.816666662693, 0.333333343267, - 0.816666662693, 0.349999994040, 0.816666662693, 0.366666674614, - 0.816666662693, 0.383333325386, 0.816666662693, 0.400000005960, - 0.816666662693, 0.416666656733, 0.816666662693, 0.433333337307, - 0.816666662693, 0.449999988079, 0.816666662693, 0.466666668653, - 0.816666662693, 0.483333319426, 0.816666662693, 0.500000000000, - 0.816666662693, 0.516666650772, 0.816666662693, 0.533333361149, - 0.816666662693, 0.550000011921, 0.816666662693, 0.566666662693, - 0.816666662693, 0.583333313465, 0.816666662693, 0.600000023842, - 0.816666662693, 0.616666674614, 0.816666662693, 0.633333325386, - 0.816666662693, 0.649999976158, 0.816666662693, 0.666666686535, - 0.816666662693, 0.683333337307, 0.816666662693, 0.699999988079, - 0.816666662693, 0.716666638851, 0.816666662693, 0.733333349228, - 0.816666662693, 0.750000000000, 0.816666662693, 0.766666650772, - 0.816666662693, 0.783333361149, 0.816666662693, 0.800000011921, - 0.816666662693, 0.816666662693, 0.816666662693, 0.833333313465, - 0.816666662693, 0.850000023842, 0.816666662693, 0.866666674614, - 0.816666662693, 0.883333325386, 0.816666662693, 0.899999976158, - 0.816666662693, 0.916666686535, 0.816666662693, 0.933333337307, - 0.816666662693, 0.949999988079, 0.816666662693, 0.966666638851, - 0.816666662693, 0.983333349228, 0.816666662693, 1.000000000000, - 0.833333313465, 0.000000000000, 0.833333313465, 0.016666667536, - 0.833333313465, 0.033333335072, 0.833333313465, 0.050000000745, - 0.833333313465, 0.066666670144, 0.833333313465, 0.083333335817, - 0.833333313465, 0.100000001490, 0.833333313465, 0.116666667163, - 0.833333313465, 0.133333340287, 0.833333313465, 0.150000005960, - 0.833333313465, 0.166666671634, 0.833333313465, 0.183333337307, - 0.833333313465, 0.200000002980, 0.833333313465, 0.216666668653, - 0.833333313465, 0.233333334327, 0.833333313465, 0.250000000000, - 0.833333313465, 0.266666680574, 0.833333313465, 0.283333331347, - 0.833333313465, 0.300000011921, 0.833333313465, 0.316666662693, - 0.833333313465, 0.333333343267, 0.833333313465, 0.349999994040, - 0.833333313465, 0.366666674614, 0.833333313465, 0.383333325386, - 0.833333313465, 0.400000005960, 0.833333313465, 0.416666656733, - 0.833333313465, 0.433333337307, 0.833333313465, 0.449999988079, - 0.833333313465, 0.466666668653, 0.833333313465, 0.483333319426, - 0.833333313465, 0.500000000000, 0.833333313465, 0.516666650772, - 0.833333313465, 0.533333361149, 0.833333313465, 0.550000011921, - 0.833333313465, 0.566666662693, 0.833333313465, 0.583333313465, - 0.833333313465, 0.600000023842, 0.833333313465, 0.616666674614, - 0.833333313465, 0.633333325386, 0.833333313465, 0.649999976158, - 0.833333313465, 0.666666686535, 0.833333313465, 0.683333337307, - 0.833333313465, 0.699999988079, 0.833333313465, 0.716666638851, - 0.833333313465, 0.733333349228, 0.833333313465, 0.750000000000, - 0.833333313465, 0.766666650772, 0.833333313465, 0.783333361149, - 0.833333313465, 0.800000011921, 0.833333313465, 0.816666662693, - 0.833333313465, 0.833333313465, 0.833333313465, 0.850000023842, - 0.833333313465, 0.866666674614, 0.833333313465, 0.883333325386, - 0.833333313465, 0.899999976158, 0.833333313465, 0.916666686535, - 0.833333313465, 0.933333337307, 0.833333313465, 0.949999988079, - 0.833333313465, 0.966666638851, 0.833333313465, 0.983333349228, - 0.833333313465, 1.000000000000, 0.850000023842, 0.000000000000, - 0.850000023842, 0.016666667536, 0.850000023842, 0.033333335072, - 0.850000023842, 0.050000000745, 0.850000023842, 0.066666670144, - 0.850000023842, 0.083333335817, 0.850000023842, 0.100000001490, - 0.850000023842, 0.116666667163, 0.850000023842, 0.133333340287, - 0.850000023842, 0.150000005960, 0.850000023842, 0.166666671634, - 0.850000023842, 0.183333337307, 0.850000023842, 0.200000002980, - 0.850000023842, 0.216666668653, 0.850000023842, 0.233333334327, - 0.850000023842, 0.250000000000, 0.850000023842, 0.266666680574, - 0.850000023842, 0.283333331347, 0.850000023842, 0.300000011921, - 0.850000023842, 0.316666662693, 0.850000023842, 0.333333343267, - 0.850000023842, 0.349999994040, 0.850000023842, 0.366666674614, - 0.850000023842, 0.383333325386, 0.850000023842, 0.400000005960, - 0.850000023842, 0.416666656733, 0.850000023842, 0.433333337307, - 0.850000023842, 0.449999988079, 0.850000023842, 0.466666668653, - 0.850000023842, 0.483333319426, 0.850000023842, 0.500000000000, - 0.850000023842, 0.516666650772, 0.850000023842, 0.533333361149, - 0.850000023842, 0.550000011921, 0.850000023842, 0.566666662693, - 0.850000023842, 0.583333313465, 0.850000023842, 0.600000023842, - 0.850000023842, 0.616666674614, 0.850000023842, 0.633333325386, - 0.850000023842, 0.649999976158, 0.850000023842, 0.666666686535, - 0.850000023842, 0.683333337307, 0.850000023842, 0.699999988079, - 0.850000023842, 0.716666638851, 0.850000023842, 0.733333349228, - 0.850000023842, 0.750000000000, 0.850000023842, 0.766666650772, - 0.850000023842, 0.783333361149, 0.850000023842, 0.800000011921, - 0.850000023842, 0.816666662693, 0.850000023842, 0.833333313465, - 0.850000023842, 0.850000023842, 0.850000023842, 0.866666674614, - 0.850000023842, 0.883333325386, 0.850000023842, 0.899999976158, - 0.850000023842, 0.916666686535, 0.850000023842, 0.933333337307, - 0.850000023842, 0.949999988079, 0.850000023842, 0.966666638851, - 0.850000023842, 0.983333349228, 0.850000023842, 1.000000000000, - 0.866666674614, 0.000000000000, 0.866666674614, 0.016666667536, - 0.866666674614, 0.033333335072, 0.866666674614, 0.050000000745, - 0.866666674614, 0.066666670144, 0.866666674614, 0.083333335817, - 0.866666674614, 0.100000001490, 0.866666674614, 0.116666667163, - 0.866666674614, 0.133333340287, 0.866666674614, 0.150000005960, - 0.866666674614, 0.166666671634, 0.866666674614, 0.183333337307, - 0.866666674614, 0.200000002980, 0.866666674614, 0.216666668653, - 0.866666674614, 0.233333334327, 0.866666674614, 0.250000000000, - 0.866666674614, 0.266666680574, 0.866666674614, 0.283333331347, - 0.866666674614, 0.300000011921, 0.866666674614, 0.316666662693, - 0.866666674614, 0.333333343267, 0.866666674614, 0.349999994040, - 0.866666674614, 0.366666674614, 0.866666674614, 0.383333325386, - 0.866666674614, 0.400000005960, 0.866666674614, 0.416666656733, - 0.866666674614, 0.433333337307, 0.866666674614, 0.449999988079, - 0.866666674614, 0.466666668653, 0.866666674614, 0.483333319426, - 0.866666674614, 0.500000000000, 0.866666674614, 0.516666650772, - 0.866666674614, 0.533333361149, 0.866666674614, 0.550000011921, - 0.866666674614, 0.566666662693, 0.866666674614, 0.583333313465, - 0.866666674614, 0.600000023842, 0.866666674614, 0.616666674614, - 0.866666674614, 0.633333325386, 0.866666674614, 0.649999976158, - 0.866666674614, 0.666666686535, 0.866666674614, 0.683333337307, - 0.866666674614, 0.699999988079, 0.866666674614, 0.716666638851, - 0.866666674614, 0.733333349228, 0.866666674614, 0.750000000000, - 0.866666674614, 0.766666650772, 0.866666674614, 0.783333361149, - 0.866666674614, 0.800000011921, 0.866666674614, 0.816666662693, - 0.866666674614, 0.833333313465, 0.866666674614, 0.850000023842, - 0.866666674614, 0.866666674614, 0.866666674614, 0.883333325386, - 0.866666674614, 0.899999976158, 0.866666674614, 0.916666686535, - 0.866666674614, 0.933333337307, 0.866666674614, 0.949999988079, - 0.866666674614, 0.966666638851, 0.866666674614, 0.983333349228, - 0.866666674614, 1.000000000000, 0.883333325386, 0.000000000000, - 0.883333325386, 0.016666667536, 0.883333325386, 0.033333335072, - 0.883333325386, 0.050000000745, 0.883333325386, 0.066666670144, - 0.883333325386, 0.083333335817, 0.883333325386, 0.100000001490, - 0.883333325386, 0.116666667163, 0.883333325386, 0.133333340287, - 0.883333325386, 0.150000005960, 0.883333325386, 0.166666671634, - 0.883333325386, 0.183333337307, 0.883333325386, 0.200000002980, - 0.883333325386, 0.216666668653, 0.883333325386, 0.233333334327, - 0.883333325386, 0.250000000000, 0.883333325386, 0.266666680574, - 0.883333325386, 0.283333331347, 0.883333325386, 0.300000011921, - 0.883333325386, 0.316666662693, 0.883333325386, 0.333333343267, - 0.883333325386, 0.349999994040, 0.883333325386, 0.366666674614, - 0.883333325386, 0.383333325386, 0.883333325386, 0.400000005960, - 0.883333325386, 0.416666656733, 0.883333325386, 0.433333337307, - 0.883333325386, 0.449999988079, 0.883333325386, 0.466666668653, - 0.883333325386, 0.483333319426, 0.883333325386, 0.500000000000, - 0.883333325386, 0.516666650772, 0.883333325386, 0.533333361149, - 0.883333325386, 0.550000011921, 0.883333325386, 0.566666662693, - 0.883333325386, 0.583333313465, 0.883333325386, 0.600000023842, - 0.883333325386, 0.616666674614, 0.883333325386, 0.633333325386, - 0.883333325386, 0.649999976158, 0.883333325386, 0.666666686535, - 0.883333325386, 0.683333337307, 0.883333325386, 0.699999988079, - 0.883333325386, 0.716666638851, 0.883333325386, 0.733333349228, - 0.883333325386, 0.750000000000, 0.883333325386, 0.766666650772, - 0.883333325386, 0.783333361149, 0.883333325386, 0.800000011921, - 0.883333325386, 0.816666662693, 0.883333325386, 0.833333313465, - 0.883333325386, 0.850000023842, 0.883333325386, 0.866666674614, - 0.883333325386, 0.883333325386, 0.883333325386, 0.899999976158, - 0.883333325386, 0.916666686535, 0.883333325386, 0.933333337307, - 0.883333325386, 0.949999988079, 0.883333325386, 0.966666638851, - 0.883333325386, 0.983333349228, 0.883333325386, 1.000000000000, - 0.899999976158, 0.000000000000, 0.899999976158, 0.016666667536, - 0.899999976158, 0.033333335072, 0.899999976158, 0.050000000745, - 0.899999976158, 0.066666670144, 0.899999976158, 0.083333335817, - 0.899999976158, 0.100000001490, 0.899999976158, 0.116666667163, - 0.899999976158, 0.133333340287, 0.899999976158, 0.150000005960, - 0.899999976158, 0.166666671634, 0.899999976158, 0.183333337307, - 0.899999976158, 0.200000002980, 0.899999976158, 0.216666668653, - 0.899999976158, 0.233333334327, 0.899999976158, 0.250000000000, - 0.899999976158, 0.266666680574, 0.899999976158, 0.283333331347, - 0.899999976158, 0.300000011921, 0.899999976158, 0.316666662693, - 0.899999976158, 0.333333343267, 0.899999976158, 0.349999994040, - 0.899999976158, 0.366666674614, 0.899999976158, 0.383333325386, - 0.899999976158, 0.400000005960, 0.899999976158, 0.416666656733, - 0.899999976158, 0.433333337307, 0.899999976158, 0.449999988079, - 0.899999976158, 0.466666668653, 0.899999976158, 0.483333319426, - 0.899999976158, 0.500000000000, 0.899999976158, 0.516666650772, - 0.899999976158, 0.533333361149, 0.899999976158, 0.550000011921, - 0.899999976158, 0.566666662693, 0.899999976158, 0.583333313465, - 0.899999976158, 0.600000023842, 0.899999976158, 0.616666674614, - 0.899999976158, 0.633333325386, 0.899999976158, 0.649999976158, - 0.899999976158, 0.666666686535, 0.899999976158, 0.683333337307, - 0.899999976158, 0.699999988079, 0.899999976158, 0.716666638851, - 0.899999976158, 0.733333349228, 0.899999976158, 0.750000000000, - 0.899999976158, 0.766666650772, 0.899999976158, 0.783333361149, - 0.899999976158, 0.800000011921, 0.899999976158, 0.816666662693, - 0.899999976158, 0.833333313465, 0.899999976158, 0.850000023842, - 0.899999976158, 0.866666674614, 0.899999976158, 0.883333325386, - 0.899999976158, 0.899999976158, 0.899999976158, 0.916666686535, - 0.899999976158, 0.933333337307, 0.899999976158, 0.949999988079, - 0.899999976158, 0.966666638851, 0.899999976158, 0.983333349228, - 0.899999976158, 1.000000000000, 0.916666686535, 0.000000000000, - 0.916666686535, 0.016666667536, 0.916666686535, 0.033333335072, - 0.916666686535, 0.050000000745, 0.916666686535, 0.066666670144, - 0.916666686535, 0.083333335817, 0.916666686535, 0.100000001490, - 0.916666686535, 0.116666667163, 0.916666686535, 0.133333340287, - 0.916666686535, 0.150000005960, 0.916666686535, 0.166666671634, - 0.916666686535, 0.183333337307, 0.916666686535, 0.200000002980, - 0.916666686535, 0.216666668653, 0.916666686535, 0.233333334327, - 0.916666686535, 0.250000000000, 0.916666686535, 0.266666680574, - 0.916666686535, 0.283333331347, 0.916666686535, 0.300000011921, - 0.916666686535, 0.316666662693, 0.916666686535, 0.333333343267, - 0.916666686535, 0.349999994040, 0.916666686535, 0.366666674614, - 0.916666686535, 0.383333325386, 0.916666686535, 0.400000005960, - 0.916666686535, 0.416666656733, 0.916666686535, 0.433333337307, - 0.916666686535, 0.449999988079, 0.916666686535, 0.466666668653, - 0.916666686535, 0.483333319426, 0.916666686535, 0.500000000000, - 0.916666686535, 0.516666650772, 0.916666686535, 0.533333361149, - 0.916666686535, 0.550000011921, 0.916666686535, 0.566666662693, - 0.916666686535, 0.583333313465, 0.916666686535, 0.600000023842, - 0.916666686535, 0.616666674614, 0.916666686535, 0.633333325386, - 0.916666686535, 0.649999976158, 0.916666686535, 0.666666686535, - 0.916666686535, 0.683333337307, 0.916666686535, 0.699999988079, - 0.916666686535, 0.716666638851, 0.916666686535, 0.733333349228, - 0.916666686535, 0.750000000000, 0.916666686535, 0.766666650772, - 0.916666686535, 0.783333361149, 0.916666686535, 0.800000011921, - 0.916666686535, 0.816666662693, 0.916666686535, 0.833333313465, - 0.916666686535, 0.850000023842, 0.916666686535, 0.866666674614, - 0.916666686535, 0.883333325386, 0.916666686535, 0.899999976158, - 0.916666686535, 0.916666686535, 0.916666686535, 0.933333337307, - 0.916666686535, 0.949999988079, 0.916666686535, 0.966666638851, - 0.916666686535, 0.983333349228, 0.916666686535, 1.000000000000, - 0.933333337307, 0.000000000000, 0.933333337307, 0.016666667536, - 0.933333337307, 0.033333335072, 0.933333337307, 0.050000000745, - 0.933333337307, 0.066666670144, 0.933333337307, 0.083333335817, - 0.933333337307, 0.100000001490, 0.933333337307, 0.116666667163, - 0.933333337307, 0.133333340287, 0.933333337307, 0.150000005960, - 0.933333337307, 0.166666671634, 0.933333337307, 0.183333337307, - 0.933333337307, 0.200000002980, 0.933333337307, 0.216666668653, - 0.933333337307, 0.233333334327, 0.933333337307, 0.250000000000, - 0.933333337307, 0.266666680574, 0.933333337307, 0.283333331347, - 0.933333337307, 0.300000011921, 0.933333337307, 0.316666662693, - 0.933333337307, 0.333333343267, 0.933333337307, 0.349999994040, - 0.933333337307, 0.366666674614, 0.933333337307, 0.383333325386, - 0.933333337307, 0.400000005960, 0.933333337307, 0.416666656733, - 0.933333337307, 0.433333337307, 0.933333337307, 0.449999988079, - 0.933333337307, 0.466666668653, 0.933333337307, 0.483333319426, - 0.933333337307, 0.500000000000, 0.933333337307, 0.516666650772, - 0.933333337307, 0.533333361149, 0.933333337307, 0.550000011921, - 0.933333337307, 0.566666662693, 0.933333337307, 0.583333313465, - 0.933333337307, 0.600000023842, 0.933333337307, 0.616666674614, - 0.933333337307, 0.633333325386, 0.933333337307, 0.649999976158, - 0.933333337307, 0.666666686535, 0.933333337307, 0.683333337307, - 0.933333337307, 0.699999988079, 0.933333337307, 0.716666638851, - 0.933333337307, 0.733333349228, 0.933333337307, 0.750000000000, - 0.933333337307, 0.766666650772, 0.933333337307, 0.783333361149, - 0.933333337307, 0.800000011921, 0.933333337307, 0.816666662693, - 0.933333337307, 0.833333313465, 0.933333337307, 0.850000023842, - 0.933333337307, 0.866666674614, 0.933333337307, 0.883333325386, - 0.933333337307, 0.899999976158, 0.933333337307, 0.916666686535, - 0.933333337307, 0.933333337307, 0.933333337307, 0.949999988079, - 0.933333337307, 0.966666638851, 0.933333337307, 0.983333349228, - 0.933333337307, 1.000000000000, 0.949999988079, 0.000000000000, - 0.949999988079, 0.016666667536, 0.949999988079, 0.033333335072, - 0.949999988079, 0.050000000745, 0.949999988079, 0.066666670144, - 0.949999988079, 0.083333335817, 0.949999988079, 0.100000001490, - 0.949999988079, 0.116666667163, 0.949999988079, 0.133333340287, - 0.949999988079, 0.150000005960, 0.949999988079, 0.166666671634, - 0.949999988079, 0.183333337307, 0.949999988079, 0.200000002980, - 0.949999988079, 0.216666668653, 0.949999988079, 0.233333334327, - 0.949999988079, 0.250000000000, 0.949999988079, 0.266666680574, - 0.949999988079, 0.283333331347, 0.949999988079, 0.300000011921, - 0.949999988079, 0.316666662693, 0.949999988079, 0.333333343267, - 0.949999988079, 0.349999994040, 0.949999988079, 0.366666674614, - 0.949999988079, 0.383333325386, 0.949999988079, 0.400000005960, - 0.949999988079, 0.416666656733, 0.949999988079, 0.433333337307, - 0.949999988079, 0.449999988079, 0.949999988079, 0.466666668653, - 0.949999988079, 0.483333319426, 0.949999988079, 0.500000000000, - 0.949999988079, 0.516666650772, 0.949999988079, 0.533333361149, - 0.949999988079, 0.550000011921, 0.949999988079, 0.566666662693, - 0.949999988079, 0.583333313465, 0.949999988079, 0.600000023842, - 0.949999988079, 0.616666674614, 0.949999988079, 0.633333325386, - 0.949999988079, 0.649999976158, 0.949999988079, 0.666666686535, - 0.949999988079, 0.683333337307, 0.949999988079, 0.699999988079, - 0.949999988079, 0.716666638851, 0.949999988079, 0.733333349228, - 0.949999988079, 0.750000000000, 0.949999988079, 0.766666650772, - 0.949999988079, 0.783333361149, 0.949999988079, 0.800000011921, - 0.949999988079, 0.816666662693, 0.949999988079, 0.833333313465, - 0.949999988079, 0.850000023842, 0.949999988079, 0.866666674614, - 0.949999988079, 0.883333325386, 0.949999988079, 0.899999976158, - 0.949999988079, 0.916666686535, 0.949999988079, 0.933333337307, - 0.949999988079, 0.949999988079, 0.949999988079, 0.966666638851, - 0.949999988079, 0.983333349228, 0.949999988079, 1.000000000000, - 0.966666638851, 0.000000000000, 0.966666638851, 0.016666667536, - 0.966666638851, 0.033333335072, 0.966666638851, 0.050000000745, - 0.966666638851, 0.066666670144, 0.966666638851, 0.083333335817, - 0.966666638851, 0.100000001490, 0.966666638851, 0.116666667163, - 0.966666638851, 0.133333340287, 0.966666638851, 0.150000005960, - 0.966666638851, 0.166666671634, 0.966666638851, 0.183333337307, - 0.966666638851, 0.200000002980, 0.966666638851, 0.216666668653, - 0.966666638851, 0.233333334327, 0.966666638851, 0.250000000000, - 0.966666638851, 0.266666680574, 0.966666638851, 0.283333331347, - 0.966666638851, 0.300000011921, 0.966666638851, 0.316666662693, - 0.966666638851, 0.333333343267, 0.966666638851, 0.349999994040, - 0.966666638851, 0.366666674614, 0.966666638851, 0.383333325386, - 0.966666638851, 0.400000005960, 0.966666638851, 0.416666656733, - 0.966666638851, 0.433333337307, 0.966666638851, 0.449999988079, - 0.966666638851, 0.466666668653, 0.966666638851, 0.483333319426, - 0.966666638851, 0.500000000000, 0.966666638851, 0.516666650772, - 0.966666638851, 0.533333361149, 0.966666638851, 0.550000011921, - 0.966666638851, 0.566666662693, 0.966666638851, 0.583333313465, - 0.966666638851, 0.600000023842, 0.966666638851, 0.616666674614, - 0.966666638851, 0.633333325386, 0.966666638851, 0.649999976158, - 0.966666638851, 0.666666686535, 0.966666638851, 0.683333337307, - 0.966666638851, 0.699999988079, 0.966666638851, 0.716666638851, - 0.966666638851, 0.733333349228, 0.966666638851, 0.750000000000, - 0.966666638851, 0.766666650772, 0.966666638851, 0.783333361149, - 0.966666638851, 0.800000011921, 0.966666638851, 0.816666662693, - 0.966666638851, 0.833333313465, 0.966666638851, 0.850000023842, - 0.966666638851, 0.866666674614, 0.966666638851, 0.883333325386, - 0.966666638851, 0.899999976158, 0.966666638851, 0.916666686535, - 0.966666638851, 0.933333337307, 0.966666638851, 0.949999988079, - 0.966666638851, 0.966666638851, 0.966666638851, 0.983333349228, - 0.966666638851, 1.000000000000, 0.983333349228, 0.000000000000, - 0.983333349228, 0.016666667536, 0.983333349228, 0.033333335072, - 0.983333349228, 0.050000000745, 0.983333349228, 0.066666670144, - 0.983333349228, 0.083333335817, 0.983333349228, 0.100000001490, - 0.983333349228, 0.116666667163, 0.983333349228, 0.133333340287, - 0.983333349228, 0.150000005960, 0.983333349228, 0.166666671634, - 0.983333349228, 0.183333337307, 0.983333349228, 0.200000002980, - 0.983333349228, 0.216666668653, 0.983333349228, 0.233333334327, - 0.983333349228, 0.250000000000, 0.983333349228, 0.266666680574, - 0.983333349228, 0.283333331347, 0.983333349228, 0.300000011921, - 0.983333349228, 0.316666662693, 0.983333349228, 0.333333343267, - 0.983333349228, 0.349999994040, 0.983333349228, 0.366666674614, - 0.983333349228, 0.383333325386, 0.983333349228, 0.400000005960, - 0.983333349228, 0.416666656733, 0.983333349228, 0.433333337307, - 0.983333349228, 0.449999988079, 0.983333349228, 0.466666668653, - 0.983333349228, 0.483333319426, 0.983333349228, 0.500000000000, - 0.983333349228, 0.516666650772, 0.983333349228, 0.533333361149, - 0.983333349228, 0.550000011921, 0.983333349228, 0.566666662693, - 0.983333349228, 0.583333313465, 0.983333349228, 0.600000023842, - 0.983333349228, 0.616666674614, 0.983333349228, 0.633333325386, - 0.983333349228, 0.649999976158, 0.983333349228, 0.666666686535, - 0.983333349228, 0.683333337307, 0.983333349228, 0.699999988079, - 0.983333349228, 0.716666638851, 0.983333349228, 0.733333349228, - 0.983333349228, 0.750000000000, 0.983333349228, 0.766666650772, - 0.983333349228, 0.783333361149, 0.983333349228, 0.800000011921, - 0.983333349228, 0.816666662693, 0.983333349228, 0.833333313465, - 0.983333349228, 0.850000023842, 0.983333349228, 0.866666674614, - 0.983333349228, 0.883333325386, 0.983333349228, 0.899999976158, - 0.983333349228, 0.916666686535, 0.983333349228, 0.933333337307, - 0.983333349228, 0.949999988079, 0.983333349228, 0.966666638851, - 0.983333349228, 0.983333349228, 0.983333349228, 1.000000000000, - 1.000000000000, 0.000000000000, 1.000000000000, 0.016666667536, - 1.000000000000, 0.033333335072, 1.000000000000, 0.050000000745, - 1.000000000000, 0.066666670144, 1.000000000000, 0.083333335817, - 1.000000000000, 0.100000001490, 1.000000000000, 0.116666667163, - 1.000000000000, 0.133333340287, 1.000000000000, 0.150000005960, - 1.000000000000, 0.166666671634, 1.000000000000, 0.183333337307, - 1.000000000000, 0.200000002980, 1.000000000000, 0.216666668653, - 1.000000000000, 0.233333334327, 1.000000000000, 0.250000000000, - 1.000000000000, 0.266666680574, 1.000000000000, 0.283333331347, - 1.000000000000, 0.300000011921, 1.000000000000, 0.316666662693, - 1.000000000000, 0.333333343267, 1.000000000000, 0.349999994040, - 1.000000000000, 0.366666674614, 1.000000000000, 0.383333325386, - 1.000000000000, 0.400000005960, 1.000000000000, 0.416666656733, - 1.000000000000, 0.433333337307, 1.000000000000, 0.449999988079, - 1.000000000000, 0.466666668653, 1.000000000000, 0.483333319426, - 1.000000000000, 0.500000000000, 1.000000000000, 0.516666650772, - 1.000000000000, 0.533333361149, 1.000000000000, 0.550000011921, - 1.000000000000, 0.566666662693, 1.000000000000, 0.583333313465, - 1.000000000000, 0.600000023842, 1.000000000000, 0.616666674614, - 1.000000000000, 0.633333325386, 1.000000000000, 0.649999976158, - 1.000000000000, 0.666666686535, 1.000000000000, 0.683333337307, - 1.000000000000, 0.699999988079, 1.000000000000, 0.716666638851, - 1.000000000000, 0.733333349228, 1.000000000000, 0.750000000000, - 1.000000000000, 0.766666650772, 1.000000000000, 0.783333361149, - 1.000000000000, 0.800000011921, 1.000000000000, 0.816666662693, - 1.000000000000, 0.833333313465, 1.000000000000, 0.850000023842, - 1.000000000000, 0.866666674614, 1.000000000000, 0.883333325386, - 1.000000000000, 0.899999976158, 1.000000000000, 0.916666686535, - 1.000000000000, 0.933333337307, 1.000000000000, 0.949999988079, - 1.000000000000, 0.966666638851, 1.000000000000, 0.983333349228, - 1.000000000000, 1.000000000000, -}; - -} /* namespace Pdraw */ - -#endif /* USE_GLES2 */ +/** + * Parrot Drones Awesome Video Viewer Library + * OpenGL ES 2.0 HMD distortion correction + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Extracted from FPVToolbox by Frédéric Bertolus + * https://github.com/niavok/fpvtoolbox + * and translated from Java to C++ + */ + +#ifdef USE_GLES2 + +namespace Pdraw { + +extern const float pdraw_gles2HmdTexCoords[7442] = { + 0.000000000000, 0.000000000000, 0.000000000000, 0.016666667536, + 0.000000000000, 0.033333335072, 0.000000000000, 0.050000000745, + 0.000000000000, 0.066666670144, 0.000000000000, 0.083333335817, + 0.000000000000, 0.100000001490, 0.000000000000, 0.116666667163, + 0.000000000000, 0.133333340287, 0.000000000000, 0.150000005960, + 0.000000000000, 0.166666671634, 0.000000000000, 0.183333337307, + 0.000000000000, 0.200000002980, 0.000000000000, 0.216666668653, + 0.000000000000, 0.233333334327, 0.000000000000, 0.250000000000, + 0.000000000000, 0.266666680574, 0.000000000000, 0.283333331347, + 0.000000000000, 0.300000011921, 0.000000000000, 0.316666662693, + 0.000000000000, 0.333333343267, 0.000000000000, 0.349999994040, + 0.000000000000, 0.366666674614, 0.000000000000, 0.383333325386, + 0.000000000000, 0.400000005960, 0.000000000000, 0.416666656733, + 0.000000000000, 0.433333337307, 0.000000000000, 0.449999988079, + 0.000000000000, 0.466666668653, 0.000000000000, 0.483333319426, + 0.000000000000, 0.500000000000, 0.000000000000, 0.516666650772, + 0.000000000000, 0.533333361149, 0.000000000000, 0.550000011921, + 0.000000000000, 0.566666662693, 0.000000000000, 0.583333313465, + 0.000000000000, 0.600000023842, 0.000000000000, 0.616666674614, + 0.000000000000, 0.633333325386, 0.000000000000, 0.649999976158, + 0.000000000000, 0.666666686535, 0.000000000000, 0.683333337307, + 0.000000000000, 0.699999988079, 0.000000000000, 0.716666638851, + 0.000000000000, 0.733333349228, 0.000000000000, 0.750000000000, + 0.000000000000, 0.766666650772, 0.000000000000, 0.783333361149, + 0.000000000000, 0.800000011921, 0.000000000000, 0.816666662693, + 0.000000000000, 0.833333313465, 0.000000000000, 0.850000023842, + 0.000000000000, 0.866666674614, 0.000000000000, 0.883333325386, + 0.000000000000, 0.899999976158, 0.000000000000, 0.916666686535, + 0.000000000000, 0.933333337307, 0.000000000000, 0.949999988079, + 0.000000000000, 0.966666638851, 0.000000000000, 0.983333349228, + 0.000000000000, 1.000000000000, 0.016666667536, 0.000000000000, + 0.016666667536, 0.016666667536, 0.016666667536, 0.033333335072, + 0.016666667536, 0.050000000745, 0.016666667536, 0.066666670144, + 0.016666667536, 0.083333335817, 0.016666667536, 0.100000001490, + 0.016666667536, 0.116666667163, 0.016666667536, 0.133333340287, + 0.016666667536, 0.150000005960, 0.016666667536, 0.166666671634, + 0.016666667536, 0.183333337307, 0.016666667536, 0.200000002980, + 0.016666667536, 0.216666668653, 0.016666667536, 0.233333334327, + 0.016666667536, 0.250000000000, 0.016666667536, 0.266666680574, + 0.016666667536, 0.283333331347, 0.016666667536, 0.300000011921, + 0.016666667536, 0.316666662693, 0.016666667536, 0.333333343267, + 0.016666667536, 0.349999994040, 0.016666667536, 0.366666674614, + 0.016666667536, 0.383333325386, 0.016666667536, 0.400000005960, + 0.016666667536, 0.416666656733, 0.016666667536, 0.433333337307, + 0.016666667536, 0.449999988079, 0.016666667536, 0.466666668653, + 0.016666667536, 0.483333319426, 0.016666667536, 0.500000000000, + 0.016666667536, 0.516666650772, 0.016666667536, 0.533333361149, + 0.016666667536, 0.550000011921, 0.016666667536, 0.566666662693, + 0.016666667536, 0.583333313465, 0.016666667536, 0.600000023842, + 0.016666667536, 0.616666674614, 0.016666667536, 0.633333325386, + 0.016666667536, 0.649999976158, 0.016666667536, 0.666666686535, + 0.016666667536, 0.683333337307, 0.016666667536, 0.699999988079, + 0.016666667536, 0.716666638851, 0.016666667536, 0.733333349228, + 0.016666667536, 0.750000000000, 0.016666667536, 0.766666650772, + 0.016666667536, 0.783333361149, 0.016666667536, 0.800000011921, + 0.016666667536, 0.816666662693, 0.016666667536, 0.833333313465, + 0.016666667536, 0.850000023842, 0.016666667536, 0.866666674614, + 0.016666667536, 0.883333325386, 0.016666667536, 0.899999976158, + 0.016666667536, 0.916666686535, 0.016666667536, 0.933333337307, + 0.016666667536, 0.949999988079, 0.016666667536, 0.966666638851, + 0.016666667536, 0.983333349228, 0.016666667536, 1.000000000000, + 0.033333335072, 0.000000000000, 0.033333335072, 0.016666667536, + 0.033333335072, 0.033333335072, 0.033333335072, 0.050000000745, + 0.033333335072, 0.066666670144, 0.033333335072, 0.083333335817, + 0.033333335072, 0.100000001490, 0.033333335072, 0.116666667163, + 0.033333335072, 0.133333340287, 0.033333335072, 0.150000005960, + 0.033333335072, 0.166666671634, 0.033333335072, 0.183333337307, + 0.033333335072, 0.200000002980, 0.033333335072, 0.216666668653, + 0.033333335072, 0.233333334327, 0.033333335072, 0.250000000000, + 0.033333335072, 0.266666680574, 0.033333335072, 0.283333331347, + 0.033333335072, 0.300000011921, 0.033333335072, 0.316666662693, + 0.033333335072, 0.333333343267, 0.033333335072, 0.349999994040, + 0.033333335072, 0.366666674614, 0.033333335072, 0.383333325386, + 0.033333335072, 0.400000005960, 0.033333335072, 0.416666656733, + 0.033333335072, 0.433333337307, 0.033333335072, 0.449999988079, + 0.033333335072, 0.466666668653, 0.033333335072, 0.483333319426, + 0.033333335072, 0.500000000000, 0.033333335072, 0.516666650772, + 0.033333335072, 0.533333361149, 0.033333335072, 0.550000011921, + 0.033333335072, 0.566666662693, 0.033333335072, 0.583333313465, + 0.033333335072, 0.600000023842, 0.033333335072, 0.616666674614, + 0.033333335072, 0.633333325386, 0.033333335072, 0.649999976158, + 0.033333335072, 0.666666686535, 0.033333335072, 0.683333337307, + 0.033333335072, 0.699999988079, 0.033333335072, 0.716666638851, + 0.033333335072, 0.733333349228, 0.033333335072, 0.750000000000, + 0.033333335072, 0.766666650772, 0.033333335072, 0.783333361149, + 0.033333335072, 0.800000011921, 0.033333335072, 0.816666662693, + 0.033333335072, 0.833333313465, 0.033333335072, 0.850000023842, + 0.033333335072, 0.866666674614, 0.033333335072, 0.883333325386, + 0.033333335072, 0.899999976158, 0.033333335072, 0.916666686535, + 0.033333335072, 0.933333337307, 0.033333335072, 0.949999988079, + 0.033333335072, 0.966666638851, 0.033333335072, 0.983333349228, + 0.033333335072, 1.000000000000, 0.050000000745, 0.000000000000, + 0.050000000745, 0.016666667536, 0.050000000745, 0.033333335072, + 0.050000000745, 0.050000000745, 0.050000000745, 0.066666670144, + 0.050000000745, 0.083333335817, 0.050000000745, 0.100000001490, + 0.050000000745, 0.116666667163, 0.050000000745, 0.133333340287, + 0.050000000745, 0.150000005960, 0.050000000745, 0.166666671634, + 0.050000000745, 0.183333337307, 0.050000000745, 0.200000002980, + 0.050000000745, 0.216666668653, 0.050000000745, 0.233333334327, + 0.050000000745, 0.250000000000, 0.050000000745, 0.266666680574, + 0.050000000745, 0.283333331347, 0.050000000745, 0.300000011921, + 0.050000000745, 0.316666662693, 0.050000000745, 0.333333343267, + 0.050000000745, 0.349999994040, 0.050000000745, 0.366666674614, + 0.050000000745, 0.383333325386, 0.050000000745, 0.400000005960, + 0.050000000745, 0.416666656733, 0.050000000745, 0.433333337307, + 0.050000000745, 0.449999988079, 0.050000000745, 0.466666668653, + 0.050000000745, 0.483333319426, 0.050000000745, 0.500000000000, + 0.050000000745, 0.516666650772, 0.050000000745, 0.533333361149, + 0.050000000745, 0.550000011921, 0.050000000745, 0.566666662693, + 0.050000000745, 0.583333313465, 0.050000000745, 0.600000023842, + 0.050000000745, 0.616666674614, 0.050000000745, 0.633333325386, + 0.050000000745, 0.649999976158, 0.050000000745, 0.666666686535, + 0.050000000745, 0.683333337307, 0.050000000745, 0.699999988079, + 0.050000000745, 0.716666638851, 0.050000000745, 0.733333349228, + 0.050000000745, 0.750000000000, 0.050000000745, 0.766666650772, + 0.050000000745, 0.783333361149, 0.050000000745, 0.800000011921, + 0.050000000745, 0.816666662693, 0.050000000745, 0.833333313465, + 0.050000000745, 0.850000023842, 0.050000000745, 0.866666674614, + 0.050000000745, 0.883333325386, 0.050000000745, 0.899999976158, + 0.050000000745, 0.916666686535, 0.050000000745, 0.933333337307, + 0.050000000745, 0.949999988079, 0.050000000745, 0.966666638851, + 0.050000000745, 0.983333349228, 0.050000000745, 1.000000000000, + 0.066666670144, 0.000000000000, 0.066666670144, 0.016666667536, + 0.066666670144, 0.033333335072, 0.066666670144, 0.050000000745, + 0.066666670144, 0.066666670144, 0.066666670144, 0.083333335817, + 0.066666670144, 0.100000001490, 0.066666670144, 0.116666667163, + 0.066666670144, 0.133333340287, 0.066666670144, 0.150000005960, + 0.066666670144, 0.166666671634, 0.066666670144, 0.183333337307, + 0.066666670144, 0.200000002980, 0.066666670144, 0.216666668653, + 0.066666670144, 0.233333334327, 0.066666670144, 0.250000000000, + 0.066666670144, 0.266666680574, 0.066666670144, 0.283333331347, + 0.066666670144, 0.300000011921, 0.066666670144, 0.316666662693, + 0.066666670144, 0.333333343267, 0.066666670144, 0.349999994040, + 0.066666670144, 0.366666674614, 0.066666670144, 0.383333325386, + 0.066666670144, 0.400000005960, 0.066666670144, 0.416666656733, + 0.066666670144, 0.433333337307, 0.066666670144, 0.449999988079, + 0.066666670144, 0.466666668653, 0.066666670144, 0.483333319426, + 0.066666670144, 0.500000000000, 0.066666670144, 0.516666650772, + 0.066666670144, 0.533333361149, 0.066666670144, 0.550000011921, + 0.066666670144, 0.566666662693, 0.066666670144, 0.583333313465, + 0.066666670144, 0.600000023842, 0.066666670144, 0.616666674614, + 0.066666670144, 0.633333325386, 0.066666670144, 0.649999976158, + 0.066666670144, 0.666666686535, 0.066666670144, 0.683333337307, + 0.066666670144, 0.699999988079, 0.066666670144, 0.716666638851, + 0.066666670144, 0.733333349228, 0.066666670144, 0.750000000000, + 0.066666670144, 0.766666650772, 0.066666670144, 0.783333361149, + 0.066666670144, 0.800000011921, 0.066666670144, 0.816666662693, + 0.066666670144, 0.833333313465, 0.066666670144, 0.850000023842, + 0.066666670144, 0.866666674614, 0.066666670144, 0.883333325386, + 0.066666670144, 0.899999976158, 0.066666670144, 0.916666686535, + 0.066666670144, 0.933333337307, 0.066666670144, 0.949999988079, + 0.066666670144, 0.966666638851, 0.066666670144, 0.983333349228, + 0.066666670144, 1.000000000000, 0.083333335817, 0.000000000000, + 0.083333335817, 0.016666667536, 0.083333335817, 0.033333335072, + 0.083333335817, 0.050000000745, 0.083333335817, 0.066666670144, + 0.083333335817, 0.083333335817, 0.083333335817, 0.100000001490, + 0.083333335817, 0.116666667163, 0.083333335817, 0.133333340287, + 0.083333335817, 0.150000005960, 0.083333335817, 0.166666671634, + 0.083333335817, 0.183333337307, 0.083333335817, 0.200000002980, + 0.083333335817, 0.216666668653, 0.083333335817, 0.233333334327, + 0.083333335817, 0.250000000000, 0.083333335817, 0.266666680574, + 0.083333335817, 0.283333331347, 0.083333335817, 0.300000011921, + 0.083333335817, 0.316666662693, 0.083333335817, 0.333333343267, + 0.083333335817, 0.349999994040, 0.083333335817, 0.366666674614, + 0.083333335817, 0.383333325386, 0.083333335817, 0.400000005960, + 0.083333335817, 0.416666656733, 0.083333335817, 0.433333337307, + 0.083333335817, 0.449999988079, 0.083333335817, 0.466666668653, + 0.083333335817, 0.483333319426, 0.083333335817, 0.500000000000, + 0.083333335817, 0.516666650772, 0.083333335817, 0.533333361149, + 0.083333335817, 0.550000011921, 0.083333335817, 0.566666662693, + 0.083333335817, 0.583333313465, 0.083333335817, 0.600000023842, + 0.083333335817, 0.616666674614, 0.083333335817, 0.633333325386, + 0.083333335817, 0.649999976158, 0.083333335817, 0.666666686535, + 0.083333335817, 0.683333337307, 0.083333335817, 0.699999988079, + 0.083333335817, 0.716666638851, 0.083333335817, 0.733333349228, + 0.083333335817, 0.750000000000, 0.083333335817, 0.766666650772, + 0.083333335817, 0.783333361149, 0.083333335817, 0.800000011921, + 0.083333335817, 0.816666662693, 0.083333335817, 0.833333313465, + 0.083333335817, 0.850000023842, 0.083333335817, 0.866666674614, + 0.083333335817, 0.883333325386, 0.083333335817, 0.899999976158, + 0.083333335817, 0.916666686535, 0.083333335817, 0.933333337307, + 0.083333335817, 0.949999988079, 0.083333335817, 0.966666638851, + 0.083333335817, 0.983333349228, 0.083333335817, 1.000000000000, + 0.100000001490, 0.000000000000, 0.100000001490, 0.016666667536, + 0.100000001490, 0.033333335072, 0.100000001490, 0.050000000745, + 0.100000001490, 0.066666670144, 0.100000001490, 0.083333335817, + 0.100000001490, 0.100000001490, 0.100000001490, 0.116666667163, + 0.100000001490, 0.133333340287, 0.100000001490, 0.150000005960, + 0.100000001490, 0.166666671634, 0.100000001490, 0.183333337307, + 0.100000001490, 0.200000002980, 0.100000001490, 0.216666668653, + 0.100000001490, 0.233333334327, 0.100000001490, 0.250000000000, + 0.100000001490, 0.266666680574, 0.100000001490, 0.283333331347, + 0.100000001490, 0.300000011921, 0.100000001490, 0.316666662693, + 0.100000001490, 0.333333343267, 0.100000001490, 0.349999994040, + 0.100000001490, 0.366666674614, 0.100000001490, 0.383333325386, + 0.100000001490, 0.400000005960, 0.100000001490, 0.416666656733, + 0.100000001490, 0.433333337307, 0.100000001490, 0.449999988079, + 0.100000001490, 0.466666668653, 0.100000001490, 0.483333319426, + 0.100000001490, 0.500000000000, 0.100000001490, 0.516666650772, + 0.100000001490, 0.533333361149, 0.100000001490, 0.550000011921, + 0.100000001490, 0.566666662693, 0.100000001490, 0.583333313465, + 0.100000001490, 0.600000023842, 0.100000001490, 0.616666674614, + 0.100000001490, 0.633333325386, 0.100000001490, 0.649999976158, + 0.100000001490, 0.666666686535, 0.100000001490, 0.683333337307, + 0.100000001490, 0.699999988079, 0.100000001490, 0.716666638851, + 0.100000001490, 0.733333349228, 0.100000001490, 0.750000000000, + 0.100000001490, 0.766666650772, 0.100000001490, 0.783333361149, + 0.100000001490, 0.800000011921, 0.100000001490, 0.816666662693, + 0.100000001490, 0.833333313465, 0.100000001490, 0.850000023842, + 0.100000001490, 0.866666674614, 0.100000001490, 0.883333325386, + 0.100000001490, 0.899999976158, 0.100000001490, 0.916666686535, + 0.100000001490, 0.933333337307, 0.100000001490, 0.949999988079, + 0.100000001490, 0.966666638851, 0.100000001490, 0.983333349228, + 0.100000001490, 1.000000000000, 0.116666667163, 0.000000000000, + 0.116666667163, 0.016666667536, 0.116666667163, 0.033333335072, + 0.116666667163, 0.050000000745, 0.116666667163, 0.066666670144, + 0.116666667163, 0.083333335817, 0.116666667163, 0.100000001490, + 0.116666667163, 0.116666667163, 0.116666667163, 0.133333340287, + 0.116666667163, 0.150000005960, 0.116666667163, 0.166666671634, + 0.116666667163, 0.183333337307, 0.116666667163, 0.200000002980, + 0.116666667163, 0.216666668653, 0.116666667163, 0.233333334327, + 0.116666667163, 0.250000000000, 0.116666667163, 0.266666680574, + 0.116666667163, 0.283333331347, 0.116666667163, 0.300000011921, + 0.116666667163, 0.316666662693, 0.116666667163, 0.333333343267, + 0.116666667163, 0.349999994040, 0.116666667163, 0.366666674614, + 0.116666667163, 0.383333325386, 0.116666667163, 0.400000005960, + 0.116666667163, 0.416666656733, 0.116666667163, 0.433333337307, + 0.116666667163, 0.449999988079, 0.116666667163, 0.466666668653, + 0.116666667163, 0.483333319426, 0.116666667163, 0.500000000000, + 0.116666667163, 0.516666650772, 0.116666667163, 0.533333361149, + 0.116666667163, 0.550000011921, 0.116666667163, 0.566666662693, + 0.116666667163, 0.583333313465, 0.116666667163, 0.600000023842, + 0.116666667163, 0.616666674614, 0.116666667163, 0.633333325386, + 0.116666667163, 0.649999976158, 0.116666667163, 0.666666686535, + 0.116666667163, 0.683333337307, 0.116666667163, 0.699999988079, + 0.116666667163, 0.716666638851, 0.116666667163, 0.733333349228, + 0.116666667163, 0.750000000000, 0.116666667163, 0.766666650772, + 0.116666667163, 0.783333361149, 0.116666667163, 0.800000011921, + 0.116666667163, 0.816666662693, 0.116666667163, 0.833333313465, + 0.116666667163, 0.850000023842, 0.116666667163, 0.866666674614, + 0.116666667163, 0.883333325386, 0.116666667163, 0.899999976158, + 0.116666667163, 0.916666686535, 0.116666667163, 0.933333337307, + 0.116666667163, 0.949999988079, 0.116666667163, 0.966666638851, + 0.116666667163, 0.983333349228, 0.116666667163, 1.000000000000, + 0.133333340287, 0.000000000000, 0.133333340287, 0.016666667536, + 0.133333340287, 0.033333335072, 0.133333340287, 0.050000000745, + 0.133333340287, 0.066666670144, 0.133333340287, 0.083333335817, + 0.133333340287, 0.100000001490, 0.133333340287, 0.116666667163, + 0.133333340287, 0.133333340287, 0.133333340287, 0.150000005960, + 0.133333340287, 0.166666671634, 0.133333340287, 0.183333337307, + 0.133333340287, 0.200000002980, 0.133333340287, 0.216666668653, + 0.133333340287, 0.233333334327, 0.133333340287, 0.250000000000, + 0.133333340287, 0.266666680574, 0.133333340287, 0.283333331347, + 0.133333340287, 0.300000011921, 0.133333340287, 0.316666662693, + 0.133333340287, 0.333333343267, 0.133333340287, 0.349999994040, + 0.133333340287, 0.366666674614, 0.133333340287, 0.383333325386, + 0.133333340287, 0.400000005960, 0.133333340287, 0.416666656733, + 0.133333340287, 0.433333337307, 0.133333340287, 0.449999988079, + 0.133333340287, 0.466666668653, 0.133333340287, 0.483333319426, + 0.133333340287, 0.500000000000, 0.133333340287, 0.516666650772, + 0.133333340287, 0.533333361149, 0.133333340287, 0.550000011921, + 0.133333340287, 0.566666662693, 0.133333340287, 0.583333313465, + 0.133333340287, 0.600000023842, 0.133333340287, 0.616666674614, + 0.133333340287, 0.633333325386, 0.133333340287, 0.649999976158, + 0.133333340287, 0.666666686535, 0.133333340287, 0.683333337307, + 0.133333340287, 0.699999988079, 0.133333340287, 0.716666638851, + 0.133333340287, 0.733333349228, 0.133333340287, 0.750000000000, + 0.133333340287, 0.766666650772, 0.133333340287, 0.783333361149, + 0.133333340287, 0.800000011921, 0.133333340287, 0.816666662693, + 0.133333340287, 0.833333313465, 0.133333340287, 0.850000023842, + 0.133333340287, 0.866666674614, 0.133333340287, 0.883333325386, + 0.133333340287, 0.899999976158, 0.133333340287, 0.916666686535, + 0.133333340287, 0.933333337307, 0.133333340287, 0.949999988079, + 0.133333340287, 0.966666638851, 0.133333340287, 0.983333349228, + 0.133333340287, 1.000000000000, 0.150000005960, 0.000000000000, + 0.150000005960, 0.016666667536, 0.150000005960, 0.033333335072, + 0.150000005960, 0.050000000745, 0.150000005960, 0.066666670144, + 0.150000005960, 0.083333335817, 0.150000005960, 0.100000001490, + 0.150000005960, 0.116666667163, 0.150000005960, 0.133333340287, + 0.150000005960, 0.150000005960, 0.150000005960, 0.166666671634, + 0.150000005960, 0.183333337307, 0.150000005960, 0.200000002980, + 0.150000005960, 0.216666668653, 0.150000005960, 0.233333334327, + 0.150000005960, 0.250000000000, 0.150000005960, 0.266666680574, + 0.150000005960, 0.283333331347, 0.150000005960, 0.300000011921, + 0.150000005960, 0.316666662693, 0.150000005960, 0.333333343267, + 0.150000005960, 0.349999994040, 0.150000005960, 0.366666674614, + 0.150000005960, 0.383333325386, 0.150000005960, 0.400000005960, + 0.150000005960, 0.416666656733, 0.150000005960, 0.433333337307, + 0.150000005960, 0.449999988079, 0.150000005960, 0.466666668653, + 0.150000005960, 0.483333319426, 0.150000005960, 0.500000000000, + 0.150000005960, 0.516666650772, 0.150000005960, 0.533333361149, + 0.150000005960, 0.550000011921, 0.150000005960, 0.566666662693, + 0.150000005960, 0.583333313465, 0.150000005960, 0.600000023842, + 0.150000005960, 0.616666674614, 0.150000005960, 0.633333325386, + 0.150000005960, 0.649999976158, 0.150000005960, 0.666666686535, + 0.150000005960, 0.683333337307, 0.150000005960, 0.699999988079, + 0.150000005960, 0.716666638851, 0.150000005960, 0.733333349228, + 0.150000005960, 0.750000000000, 0.150000005960, 0.766666650772, + 0.150000005960, 0.783333361149, 0.150000005960, 0.800000011921, + 0.150000005960, 0.816666662693, 0.150000005960, 0.833333313465, + 0.150000005960, 0.850000023842, 0.150000005960, 0.866666674614, + 0.150000005960, 0.883333325386, 0.150000005960, 0.899999976158, + 0.150000005960, 0.916666686535, 0.150000005960, 0.933333337307, + 0.150000005960, 0.949999988079, 0.150000005960, 0.966666638851, + 0.150000005960, 0.983333349228, 0.150000005960, 1.000000000000, + 0.166666671634, 0.000000000000, 0.166666671634, 0.016666667536, + 0.166666671634, 0.033333335072, 0.166666671634, 0.050000000745, + 0.166666671634, 0.066666670144, 0.166666671634, 0.083333335817, + 0.166666671634, 0.100000001490, 0.166666671634, 0.116666667163, + 0.166666671634, 0.133333340287, 0.166666671634, 0.150000005960, + 0.166666671634, 0.166666671634, 0.166666671634, 0.183333337307, + 0.166666671634, 0.200000002980, 0.166666671634, 0.216666668653, + 0.166666671634, 0.233333334327, 0.166666671634, 0.250000000000, + 0.166666671634, 0.266666680574, 0.166666671634, 0.283333331347, + 0.166666671634, 0.300000011921, 0.166666671634, 0.316666662693, + 0.166666671634, 0.333333343267, 0.166666671634, 0.349999994040, + 0.166666671634, 0.366666674614, 0.166666671634, 0.383333325386, + 0.166666671634, 0.400000005960, 0.166666671634, 0.416666656733, + 0.166666671634, 0.433333337307, 0.166666671634, 0.449999988079, + 0.166666671634, 0.466666668653, 0.166666671634, 0.483333319426, + 0.166666671634, 0.500000000000, 0.166666671634, 0.516666650772, + 0.166666671634, 0.533333361149, 0.166666671634, 0.550000011921, + 0.166666671634, 0.566666662693, 0.166666671634, 0.583333313465, + 0.166666671634, 0.600000023842, 0.166666671634, 0.616666674614, + 0.166666671634, 0.633333325386, 0.166666671634, 0.649999976158, + 0.166666671634, 0.666666686535, 0.166666671634, 0.683333337307, + 0.166666671634, 0.699999988079, 0.166666671634, 0.716666638851, + 0.166666671634, 0.733333349228, 0.166666671634, 0.750000000000, + 0.166666671634, 0.766666650772, 0.166666671634, 0.783333361149, + 0.166666671634, 0.800000011921, 0.166666671634, 0.816666662693, + 0.166666671634, 0.833333313465, 0.166666671634, 0.850000023842, + 0.166666671634, 0.866666674614, 0.166666671634, 0.883333325386, + 0.166666671634, 0.899999976158, 0.166666671634, 0.916666686535, + 0.166666671634, 0.933333337307, 0.166666671634, 0.949999988079, + 0.166666671634, 0.966666638851, 0.166666671634, 0.983333349228, + 0.166666671634, 1.000000000000, 0.183333337307, 0.000000000000, + 0.183333337307, 0.016666667536, 0.183333337307, 0.033333335072, + 0.183333337307, 0.050000000745, 0.183333337307, 0.066666670144, + 0.183333337307, 0.083333335817, 0.183333337307, 0.100000001490, + 0.183333337307, 0.116666667163, 0.183333337307, 0.133333340287, + 0.183333337307, 0.150000005960, 0.183333337307, 0.166666671634, + 0.183333337307, 0.183333337307, 0.183333337307, 0.200000002980, + 0.183333337307, 0.216666668653, 0.183333337307, 0.233333334327, + 0.183333337307, 0.250000000000, 0.183333337307, 0.266666680574, + 0.183333337307, 0.283333331347, 0.183333337307, 0.300000011921, + 0.183333337307, 0.316666662693, 0.183333337307, 0.333333343267, + 0.183333337307, 0.349999994040, 0.183333337307, 0.366666674614, + 0.183333337307, 0.383333325386, 0.183333337307, 0.400000005960, + 0.183333337307, 0.416666656733, 0.183333337307, 0.433333337307, + 0.183333337307, 0.449999988079, 0.183333337307, 0.466666668653, + 0.183333337307, 0.483333319426, 0.183333337307, 0.500000000000, + 0.183333337307, 0.516666650772, 0.183333337307, 0.533333361149, + 0.183333337307, 0.550000011921, 0.183333337307, 0.566666662693, + 0.183333337307, 0.583333313465, 0.183333337307, 0.600000023842, + 0.183333337307, 0.616666674614, 0.183333337307, 0.633333325386, + 0.183333337307, 0.649999976158, 0.183333337307, 0.666666686535, + 0.183333337307, 0.683333337307, 0.183333337307, 0.699999988079, + 0.183333337307, 0.716666638851, 0.183333337307, 0.733333349228, + 0.183333337307, 0.750000000000, 0.183333337307, 0.766666650772, + 0.183333337307, 0.783333361149, 0.183333337307, 0.800000011921, + 0.183333337307, 0.816666662693, 0.183333337307, 0.833333313465, + 0.183333337307, 0.850000023842, 0.183333337307, 0.866666674614, + 0.183333337307, 0.883333325386, 0.183333337307, 0.899999976158, + 0.183333337307, 0.916666686535, 0.183333337307, 0.933333337307, + 0.183333337307, 0.949999988079, 0.183333337307, 0.966666638851, + 0.183333337307, 0.983333349228, 0.183333337307, 1.000000000000, + 0.200000002980, 0.000000000000, 0.200000002980, 0.016666667536, + 0.200000002980, 0.033333335072, 0.200000002980, 0.050000000745, + 0.200000002980, 0.066666670144, 0.200000002980, 0.083333335817, + 0.200000002980, 0.100000001490, 0.200000002980, 0.116666667163, + 0.200000002980, 0.133333340287, 0.200000002980, 0.150000005960, + 0.200000002980, 0.166666671634, 0.200000002980, 0.183333337307, + 0.200000002980, 0.200000002980, 0.200000002980, 0.216666668653, + 0.200000002980, 0.233333334327, 0.200000002980, 0.250000000000, + 0.200000002980, 0.266666680574, 0.200000002980, 0.283333331347, + 0.200000002980, 0.300000011921, 0.200000002980, 0.316666662693, + 0.200000002980, 0.333333343267, 0.200000002980, 0.349999994040, + 0.200000002980, 0.366666674614, 0.200000002980, 0.383333325386, + 0.200000002980, 0.400000005960, 0.200000002980, 0.416666656733, + 0.200000002980, 0.433333337307, 0.200000002980, 0.449999988079, + 0.200000002980, 0.466666668653, 0.200000002980, 0.483333319426, + 0.200000002980, 0.500000000000, 0.200000002980, 0.516666650772, + 0.200000002980, 0.533333361149, 0.200000002980, 0.550000011921, + 0.200000002980, 0.566666662693, 0.200000002980, 0.583333313465, + 0.200000002980, 0.600000023842, 0.200000002980, 0.616666674614, + 0.200000002980, 0.633333325386, 0.200000002980, 0.649999976158, + 0.200000002980, 0.666666686535, 0.200000002980, 0.683333337307, + 0.200000002980, 0.699999988079, 0.200000002980, 0.716666638851, + 0.200000002980, 0.733333349228, 0.200000002980, 0.750000000000, + 0.200000002980, 0.766666650772, 0.200000002980, 0.783333361149, + 0.200000002980, 0.800000011921, 0.200000002980, 0.816666662693, + 0.200000002980, 0.833333313465, 0.200000002980, 0.850000023842, + 0.200000002980, 0.866666674614, 0.200000002980, 0.883333325386, + 0.200000002980, 0.899999976158, 0.200000002980, 0.916666686535, + 0.200000002980, 0.933333337307, 0.200000002980, 0.949999988079, + 0.200000002980, 0.966666638851, 0.200000002980, 0.983333349228, + 0.200000002980, 1.000000000000, 0.216666668653, 0.000000000000, + 0.216666668653, 0.016666667536, 0.216666668653, 0.033333335072, + 0.216666668653, 0.050000000745, 0.216666668653, 0.066666670144, + 0.216666668653, 0.083333335817, 0.216666668653, 0.100000001490, + 0.216666668653, 0.116666667163, 0.216666668653, 0.133333340287, + 0.216666668653, 0.150000005960, 0.216666668653, 0.166666671634, + 0.216666668653, 0.183333337307, 0.216666668653, 0.200000002980, + 0.216666668653, 0.216666668653, 0.216666668653, 0.233333334327, + 0.216666668653, 0.250000000000, 0.216666668653, 0.266666680574, + 0.216666668653, 0.283333331347, 0.216666668653, 0.300000011921, + 0.216666668653, 0.316666662693, 0.216666668653, 0.333333343267, + 0.216666668653, 0.349999994040, 0.216666668653, 0.366666674614, + 0.216666668653, 0.383333325386, 0.216666668653, 0.400000005960, + 0.216666668653, 0.416666656733, 0.216666668653, 0.433333337307, + 0.216666668653, 0.449999988079, 0.216666668653, 0.466666668653, + 0.216666668653, 0.483333319426, 0.216666668653, 0.500000000000, + 0.216666668653, 0.516666650772, 0.216666668653, 0.533333361149, + 0.216666668653, 0.550000011921, 0.216666668653, 0.566666662693, + 0.216666668653, 0.583333313465, 0.216666668653, 0.600000023842, + 0.216666668653, 0.616666674614, 0.216666668653, 0.633333325386, + 0.216666668653, 0.649999976158, 0.216666668653, 0.666666686535, + 0.216666668653, 0.683333337307, 0.216666668653, 0.699999988079, + 0.216666668653, 0.716666638851, 0.216666668653, 0.733333349228, + 0.216666668653, 0.750000000000, 0.216666668653, 0.766666650772, + 0.216666668653, 0.783333361149, 0.216666668653, 0.800000011921, + 0.216666668653, 0.816666662693, 0.216666668653, 0.833333313465, + 0.216666668653, 0.850000023842, 0.216666668653, 0.866666674614, + 0.216666668653, 0.883333325386, 0.216666668653, 0.899999976158, + 0.216666668653, 0.916666686535, 0.216666668653, 0.933333337307, + 0.216666668653, 0.949999988079, 0.216666668653, 0.966666638851, + 0.216666668653, 0.983333349228, 0.216666668653, 1.000000000000, + 0.233333334327, 0.000000000000, 0.233333334327, 0.016666667536, + 0.233333334327, 0.033333335072, 0.233333334327, 0.050000000745, + 0.233333334327, 0.066666670144, 0.233333334327, 0.083333335817, + 0.233333334327, 0.100000001490, 0.233333334327, 0.116666667163, + 0.233333334327, 0.133333340287, 0.233333334327, 0.150000005960, + 0.233333334327, 0.166666671634, 0.233333334327, 0.183333337307, + 0.233333334327, 0.200000002980, 0.233333334327, 0.216666668653, + 0.233333334327, 0.233333334327, 0.233333334327, 0.250000000000, + 0.233333334327, 0.266666680574, 0.233333334327, 0.283333331347, + 0.233333334327, 0.300000011921, 0.233333334327, 0.316666662693, + 0.233333334327, 0.333333343267, 0.233333334327, 0.349999994040, + 0.233333334327, 0.366666674614, 0.233333334327, 0.383333325386, + 0.233333334327, 0.400000005960, 0.233333334327, 0.416666656733, + 0.233333334327, 0.433333337307, 0.233333334327, 0.449999988079, + 0.233333334327, 0.466666668653, 0.233333334327, 0.483333319426, + 0.233333334327, 0.500000000000, 0.233333334327, 0.516666650772, + 0.233333334327, 0.533333361149, 0.233333334327, 0.550000011921, + 0.233333334327, 0.566666662693, 0.233333334327, 0.583333313465, + 0.233333334327, 0.600000023842, 0.233333334327, 0.616666674614, + 0.233333334327, 0.633333325386, 0.233333334327, 0.649999976158, + 0.233333334327, 0.666666686535, 0.233333334327, 0.683333337307, + 0.233333334327, 0.699999988079, 0.233333334327, 0.716666638851, + 0.233333334327, 0.733333349228, 0.233333334327, 0.750000000000, + 0.233333334327, 0.766666650772, 0.233333334327, 0.783333361149, + 0.233333334327, 0.800000011921, 0.233333334327, 0.816666662693, + 0.233333334327, 0.833333313465, 0.233333334327, 0.850000023842, + 0.233333334327, 0.866666674614, 0.233333334327, 0.883333325386, + 0.233333334327, 0.899999976158, 0.233333334327, 0.916666686535, + 0.233333334327, 0.933333337307, 0.233333334327, 0.949999988079, + 0.233333334327, 0.966666638851, 0.233333334327, 0.983333349228, + 0.233333334327, 1.000000000000, 0.250000000000, 0.000000000000, + 0.250000000000, 0.016666667536, 0.250000000000, 0.033333335072, + 0.250000000000, 0.050000000745, 0.250000000000, 0.066666670144, + 0.250000000000, 0.083333335817, 0.250000000000, 0.100000001490, + 0.250000000000, 0.116666667163, 0.250000000000, 0.133333340287, + 0.250000000000, 0.150000005960, 0.250000000000, 0.166666671634, + 0.250000000000, 0.183333337307, 0.250000000000, 0.200000002980, + 0.250000000000, 0.216666668653, 0.250000000000, 0.233333334327, + 0.250000000000, 0.250000000000, 0.250000000000, 0.266666680574, + 0.250000000000, 0.283333331347, 0.250000000000, 0.300000011921, + 0.250000000000, 0.316666662693, 0.250000000000, 0.333333343267, + 0.250000000000, 0.349999994040, 0.250000000000, 0.366666674614, + 0.250000000000, 0.383333325386, 0.250000000000, 0.400000005960, + 0.250000000000, 0.416666656733, 0.250000000000, 0.433333337307, + 0.250000000000, 0.449999988079, 0.250000000000, 0.466666668653, + 0.250000000000, 0.483333319426, 0.250000000000, 0.500000000000, + 0.250000000000, 0.516666650772, 0.250000000000, 0.533333361149, + 0.250000000000, 0.550000011921, 0.250000000000, 0.566666662693, + 0.250000000000, 0.583333313465, 0.250000000000, 0.600000023842, + 0.250000000000, 0.616666674614, 0.250000000000, 0.633333325386, + 0.250000000000, 0.649999976158, 0.250000000000, 0.666666686535, + 0.250000000000, 0.683333337307, 0.250000000000, 0.699999988079, + 0.250000000000, 0.716666638851, 0.250000000000, 0.733333349228, + 0.250000000000, 0.750000000000, 0.250000000000, 0.766666650772, + 0.250000000000, 0.783333361149, 0.250000000000, 0.800000011921, + 0.250000000000, 0.816666662693, 0.250000000000, 0.833333313465, + 0.250000000000, 0.850000023842, 0.250000000000, 0.866666674614, + 0.250000000000, 0.883333325386, 0.250000000000, 0.899999976158, + 0.250000000000, 0.916666686535, 0.250000000000, 0.933333337307, + 0.250000000000, 0.949999988079, 0.250000000000, 0.966666638851, + 0.250000000000, 0.983333349228, 0.250000000000, 1.000000000000, + 0.266666680574, 0.000000000000, 0.266666680574, 0.016666667536, + 0.266666680574, 0.033333335072, 0.266666680574, 0.050000000745, + 0.266666680574, 0.066666670144, 0.266666680574, 0.083333335817, + 0.266666680574, 0.100000001490, 0.266666680574, 0.116666667163, + 0.266666680574, 0.133333340287, 0.266666680574, 0.150000005960, + 0.266666680574, 0.166666671634, 0.266666680574, 0.183333337307, + 0.266666680574, 0.200000002980, 0.266666680574, 0.216666668653, + 0.266666680574, 0.233333334327, 0.266666680574, 0.250000000000, + 0.266666680574, 0.266666680574, 0.266666680574, 0.283333331347, + 0.266666680574, 0.300000011921, 0.266666680574, 0.316666662693, + 0.266666680574, 0.333333343267, 0.266666680574, 0.349999994040, + 0.266666680574, 0.366666674614, 0.266666680574, 0.383333325386, + 0.266666680574, 0.400000005960, 0.266666680574, 0.416666656733, + 0.266666680574, 0.433333337307, 0.266666680574, 0.449999988079, + 0.266666680574, 0.466666668653, 0.266666680574, 0.483333319426, + 0.266666680574, 0.500000000000, 0.266666680574, 0.516666650772, + 0.266666680574, 0.533333361149, 0.266666680574, 0.550000011921, + 0.266666680574, 0.566666662693, 0.266666680574, 0.583333313465, + 0.266666680574, 0.600000023842, 0.266666680574, 0.616666674614, + 0.266666680574, 0.633333325386, 0.266666680574, 0.649999976158, + 0.266666680574, 0.666666686535, 0.266666680574, 0.683333337307, + 0.266666680574, 0.699999988079, 0.266666680574, 0.716666638851, + 0.266666680574, 0.733333349228, 0.266666680574, 0.750000000000, + 0.266666680574, 0.766666650772, 0.266666680574, 0.783333361149, + 0.266666680574, 0.800000011921, 0.266666680574, 0.816666662693, + 0.266666680574, 0.833333313465, 0.266666680574, 0.850000023842, + 0.266666680574, 0.866666674614, 0.266666680574, 0.883333325386, + 0.266666680574, 0.899999976158, 0.266666680574, 0.916666686535, + 0.266666680574, 0.933333337307, 0.266666680574, 0.949999988079, + 0.266666680574, 0.966666638851, 0.266666680574, 0.983333349228, + 0.266666680574, 1.000000000000, 0.283333331347, 0.000000000000, + 0.283333331347, 0.016666667536, 0.283333331347, 0.033333335072, + 0.283333331347, 0.050000000745, 0.283333331347, 0.066666670144, + 0.283333331347, 0.083333335817, 0.283333331347, 0.100000001490, + 0.283333331347, 0.116666667163, 0.283333331347, 0.133333340287, + 0.283333331347, 0.150000005960, 0.283333331347, 0.166666671634, + 0.283333331347, 0.183333337307, 0.283333331347, 0.200000002980, + 0.283333331347, 0.216666668653, 0.283333331347, 0.233333334327, + 0.283333331347, 0.250000000000, 0.283333331347, 0.266666680574, + 0.283333331347, 0.283333331347, 0.283333331347, 0.300000011921, + 0.283333331347, 0.316666662693, 0.283333331347, 0.333333343267, + 0.283333331347, 0.349999994040, 0.283333331347, 0.366666674614, + 0.283333331347, 0.383333325386, 0.283333331347, 0.400000005960, + 0.283333331347, 0.416666656733, 0.283333331347, 0.433333337307, + 0.283333331347, 0.449999988079, 0.283333331347, 0.466666668653, + 0.283333331347, 0.483333319426, 0.283333331347, 0.500000000000, + 0.283333331347, 0.516666650772, 0.283333331347, 0.533333361149, + 0.283333331347, 0.550000011921, 0.283333331347, 0.566666662693, + 0.283333331347, 0.583333313465, 0.283333331347, 0.600000023842, + 0.283333331347, 0.616666674614, 0.283333331347, 0.633333325386, + 0.283333331347, 0.649999976158, 0.283333331347, 0.666666686535, + 0.283333331347, 0.683333337307, 0.283333331347, 0.699999988079, + 0.283333331347, 0.716666638851, 0.283333331347, 0.733333349228, + 0.283333331347, 0.750000000000, 0.283333331347, 0.766666650772, + 0.283333331347, 0.783333361149, 0.283333331347, 0.800000011921, + 0.283333331347, 0.816666662693, 0.283333331347, 0.833333313465, + 0.283333331347, 0.850000023842, 0.283333331347, 0.866666674614, + 0.283333331347, 0.883333325386, 0.283333331347, 0.899999976158, + 0.283333331347, 0.916666686535, 0.283333331347, 0.933333337307, + 0.283333331347, 0.949999988079, 0.283333331347, 0.966666638851, + 0.283333331347, 0.983333349228, 0.283333331347, 1.000000000000, + 0.300000011921, 0.000000000000, 0.300000011921, 0.016666667536, + 0.300000011921, 0.033333335072, 0.300000011921, 0.050000000745, + 0.300000011921, 0.066666670144, 0.300000011921, 0.083333335817, + 0.300000011921, 0.100000001490, 0.300000011921, 0.116666667163, + 0.300000011921, 0.133333340287, 0.300000011921, 0.150000005960, + 0.300000011921, 0.166666671634, 0.300000011921, 0.183333337307, + 0.300000011921, 0.200000002980, 0.300000011921, 0.216666668653, + 0.300000011921, 0.233333334327, 0.300000011921, 0.250000000000, + 0.300000011921, 0.266666680574, 0.300000011921, 0.283333331347, + 0.300000011921, 0.300000011921, 0.300000011921, 0.316666662693, + 0.300000011921, 0.333333343267, 0.300000011921, 0.349999994040, + 0.300000011921, 0.366666674614, 0.300000011921, 0.383333325386, + 0.300000011921, 0.400000005960, 0.300000011921, 0.416666656733, + 0.300000011921, 0.433333337307, 0.300000011921, 0.449999988079, + 0.300000011921, 0.466666668653, 0.300000011921, 0.483333319426, + 0.300000011921, 0.500000000000, 0.300000011921, 0.516666650772, + 0.300000011921, 0.533333361149, 0.300000011921, 0.550000011921, + 0.300000011921, 0.566666662693, 0.300000011921, 0.583333313465, + 0.300000011921, 0.600000023842, 0.300000011921, 0.616666674614, + 0.300000011921, 0.633333325386, 0.300000011921, 0.649999976158, + 0.300000011921, 0.666666686535, 0.300000011921, 0.683333337307, + 0.300000011921, 0.699999988079, 0.300000011921, 0.716666638851, + 0.300000011921, 0.733333349228, 0.300000011921, 0.750000000000, + 0.300000011921, 0.766666650772, 0.300000011921, 0.783333361149, + 0.300000011921, 0.800000011921, 0.300000011921, 0.816666662693, + 0.300000011921, 0.833333313465, 0.300000011921, 0.850000023842, + 0.300000011921, 0.866666674614, 0.300000011921, 0.883333325386, + 0.300000011921, 0.899999976158, 0.300000011921, 0.916666686535, + 0.300000011921, 0.933333337307, 0.300000011921, 0.949999988079, + 0.300000011921, 0.966666638851, 0.300000011921, 0.983333349228, + 0.300000011921, 1.000000000000, 0.316666662693, 0.000000000000, + 0.316666662693, 0.016666667536, 0.316666662693, 0.033333335072, + 0.316666662693, 0.050000000745, 0.316666662693, 0.066666670144, + 0.316666662693, 0.083333335817, 0.316666662693, 0.100000001490, + 0.316666662693, 0.116666667163, 0.316666662693, 0.133333340287, + 0.316666662693, 0.150000005960, 0.316666662693, 0.166666671634, + 0.316666662693, 0.183333337307, 0.316666662693, 0.200000002980, + 0.316666662693, 0.216666668653, 0.316666662693, 0.233333334327, + 0.316666662693, 0.250000000000, 0.316666662693, 0.266666680574, + 0.316666662693, 0.283333331347, 0.316666662693, 0.300000011921, + 0.316666662693, 0.316666662693, 0.316666662693, 0.333333343267, + 0.316666662693, 0.349999994040, 0.316666662693, 0.366666674614, + 0.316666662693, 0.383333325386, 0.316666662693, 0.400000005960, + 0.316666662693, 0.416666656733, 0.316666662693, 0.433333337307, + 0.316666662693, 0.449999988079, 0.316666662693, 0.466666668653, + 0.316666662693, 0.483333319426, 0.316666662693, 0.500000000000, + 0.316666662693, 0.516666650772, 0.316666662693, 0.533333361149, + 0.316666662693, 0.550000011921, 0.316666662693, 0.566666662693, + 0.316666662693, 0.583333313465, 0.316666662693, 0.600000023842, + 0.316666662693, 0.616666674614, 0.316666662693, 0.633333325386, + 0.316666662693, 0.649999976158, 0.316666662693, 0.666666686535, + 0.316666662693, 0.683333337307, 0.316666662693, 0.699999988079, + 0.316666662693, 0.716666638851, 0.316666662693, 0.733333349228, + 0.316666662693, 0.750000000000, 0.316666662693, 0.766666650772, + 0.316666662693, 0.783333361149, 0.316666662693, 0.800000011921, + 0.316666662693, 0.816666662693, 0.316666662693, 0.833333313465, + 0.316666662693, 0.850000023842, 0.316666662693, 0.866666674614, + 0.316666662693, 0.883333325386, 0.316666662693, 0.899999976158, + 0.316666662693, 0.916666686535, 0.316666662693, 0.933333337307, + 0.316666662693, 0.949999988079, 0.316666662693, 0.966666638851, + 0.316666662693, 0.983333349228, 0.316666662693, 1.000000000000, + 0.333333343267, 0.000000000000, 0.333333343267, 0.016666667536, + 0.333333343267, 0.033333335072, 0.333333343267, 0.050000000745, + 0.333333343267, 0.066666670144, 0.333333343267, 0.083333335817, + 0.333333343267, 0.100000001490, 0.333333343267, 0.116666667163, + 0.333333343267, 0.133333340287, 0.333333343267, 0.150000005960, + 0.333333343267, 0.166666671634, 0.333333343267, 0.183333337307, + 0.333333343267, 0.200000002980, 0.333333343267, 0.216666668653, + 0.333333343267, 0.233333334327, 0.333333343267, 0.250000000000, + 0.333333343267, 0.266666680574, 0.333333343267, 0.283333331347, + 0.333333343267, 0.300000011921, 0.333333343267, 0.316666662693, + 0.333333343267, 0.333333343267, 0.333333343267, 0.349999994040, + 0.333333343267, 0.366666674614, 0.333333343267, 0.383333325386, + 0.333333343267, 0.400000005960, 0.333333343267, 0.416666656733, + 0.333333343267, 0.433333337307, 0.333333343267, 0.449999988079, + 0.333333343267, 0.466666668653, 0.333333343267, 0.483333319426, + 0.333333343267, 0.500000000000, 0.333333343267, 0.516666650772, + 0.333333343267, 0.533333361149, 0.333333343267, 0.550000011921, + 0.333333343267, 0.566666662693, 0.333333343267, 0.583333313465, + 0.333333343267, 0.600000023842, 0.333333343267, 0.616666674614, + 0.333333343267, 0.633333325386, 0.333333343267, 0.649999976158, + 0.333333343267, 0.666666686535, 0.333333343267, 0.683333337307, + 0.333333343267, 0.699999988079, 0.333333343267, 0.716666638851, + 0.333333343267, 0.733333349228, 0.333333343267, 0.750000000000, + 0.333333343267, 0.766666650772, 0.333333343267, 0.783333361149, + 0.333333343267, 0.800000011921, 0.333333343267, 0.816666662693, + 0.333333343267, 0.833333313465, 0.333333343267, 0.850000023842, + 0.333333343267, 0.866666674614, 0.333333343267, 0.883333325386, + 0.333333343267, 0.899999976158, 0.333333343267, 0.916666686535, + 0.333333343267, 0.933333337307, 0.333333343267, 0.949999988079, + 0.333333343267, 0.966666638851, 0.333333343267, 0.983333349228, + 0.333333343267, 1.000000000000, 0.349999994040, 0.000000000000, + 0.349999994040, 0.016666667536, 0.349999994040, 0.033333335072, + 0.349999994040, 0.050000000745, 0.349999994040, 0.066666670144, + 0.349999994040, 0.083333335817, 0.349999994040, 0.100000001490, + 0.349999994040, 0.116666667163, 0.349999994040, 0.133333340287, + 0.349999994040, 0.150000005960, 0.349999994040, 0.166666671634, + 0.349999994040, 0.183333337307, 0.349999994040, 0.200000002980, + 0.349999994040, 0.216666668653, 0.349999994040, 0.233333334327, + 0.349999994040, 0.250000000000, 0.349999994040, 0.266666680574, + 0.349999994040, 0.283333331347, 0.349999994040, 0.300000011921, + 0.349999994040, 0.316666662693, 0.349999994040, 0.333333343267, + 0.349999994040, 0.349999994040, 0.349999994040, 0.366666674614, + 0.349999994040, 0.383333325386, 0.349999994040, 0.400000005960, + 0.349999994040, 0.416666656733, 0.349999994040, 0.433333337307, + 0.349999994040, 0.449999988079, 0.349999994040, 0.466666668653, + 0.349999994040, 0.483333319426, 0.349999994040, 0.500000000000, + 0.349999994040, 0.516666650772, 0.349999994040, 0.533333361149, + 0.349999994040, 0.550000011921, 0.349999994040, 0.566666662693, + 0.349999994040, 0.583333313465, 0.349999994040, 0.600000023842, + 0.349999994040, 0.616666674614, 0.349999994040, 0.633333325386, + 0.349999994040, 0.649999976158, 0.349999994040, 0.666666686535, + 0.349999994040, 0.683333337307, 0.349999994040, 0.699999988079, + 0.349999994040, 0.716666638851, 0.349999994040, 0.733333349228, + 0.349999994040, 0.750000000000, 0.349999994040, 0.766666650772, + 0.349999994040, 0.783333361149, 0.349999994040, 0.800000011921, + 0.349999994040, 0.816666662693, 0.349999994040, 0.833333313465, + 0.349999994040, 0.850000023842, 0.349999994040, 0.866666674614, + 0.349999994040, 0.883333325386, 0.349999994040, 0.899999976158, + 0.349999994040, 0.916666686535, 0.349999994040, 0.933333337307, + 0.349999994040, 0.949999988079, 0.349999994040, 0.966666638851, + 0.349999994040, 0.983333349228, 0.349999994040, 1.000000000000, + 0.366666674614, 0.000000000000, 0.366666674614, 0.016666667536, + 0.366666674614, 0.033333335072, 0.366666674614, 0.050000000745, + 0.366666674614, 0.066666670144, 0.366666674614, 0.083333335817, + 0.366666674614, 0.100000001490, 0.366666674614, 0.116666667163, + 0.366666674614, 0.133333340287, 0.366666674614, 0.150000005960, + 0.366666674614, 0.166666671634, 0.366666674614, 0.183333337307, + 0.366666674614, 0.200000002980, 0.366666674614, 0.216666668653, + 0.366666674614, 0.233333334327, 0.366666674614, 0.250000000000, + 0.366666674614, 0.266666680574, 0.366666674614, 0.283333331347, + 0.366666674614, 0.300000011921, 0.366666674614, 0.316666662693, + 0.366666674614, 0.333333343267, 0.366666674614, 0.349999994040, + 0.366666674614, 0.366666674614, 0.366666674614, 0.383333325386, + 0.366666674614, 0.400000005960, 0.366666674614, 0.416666656733, + 0.366666674614, 0.433333337307, 0.366666674614, 0.449999988079, + 0.366666674614, 0.466666668653, 0.366666674614, 0.483333319426, + 0.366666674614, 0.500000000000, 0.366666674614, 0.516666650772, + 0.366666674614, 0.533333361149, 0.366666674614, 0.550000011921, + 0.366666674614, 0.566666662693, 0.366666674614, 0.583333313465, + 0.366666674614, 0.600000023842, 0.366666674614, 0.616666674614, + 0.366666674614, 0.633333325386, 0.366666674614, 0.649999976158, + 0.366666674614, 0.666666686535, 0.366666674614, 0.683333337307, + 0.366666674614, 0.699999988079, 0.366666674614, 0.716666638851, + 0.366666674614, 0.733333349228, 0.366666674614, 0.750000000000, + 0.366666674614, 0.766666650772, 0.366666674614, 0.783333361149, + 0.366666674614, 0.800000011921, 0.366666674614, 0.816666662693, + 0.366666674614, 0.833333313465, 0.366666674614, 0.850000023842, + 0.366666674614, 0.866666674614, 0.366666674614, 0.883333325386, + 0.366666674614, 0.899999976158, 0.366666674614, 0.916666686535, + 0.366666674614, 0.933333337307, 0.366666674614, 0.949999988079, + 0.366666674614, 0.966666638851, 0.366666674614, 0.983333349228, + 0.366666674614, 1.000000000000, 0.383333325386, 0.000000000000, + 0.383333325386, 0.016666667536, 0.383333325386, 0.033333335072, + 0.383333325386, 0.050000000745, 0.383333325386, 0.066666670144, + 0.383333325386, 0.083333335817, 0.383333325386, 0.100000001490, + 0.383333325386, 0.116666667163, 0.383333325386, 0.133333340287, + 0.383333325386, 0.150000005960, 0.383333325386, 0.166666671634, + 0.383333325386, 0.183333337307, 0.383333325386, 0.200000002980, + 0.383333325386, 0.216666668653, 0.383333325386, 0.233333334327, + 0.383333325386, 0.250000000000, 0.383333325386, 0.266666680574, + 0.383333325386, 0.283333331347, 0.383333325386, 0.300000011921, + 0.383333325386, 0.316666662693, 0.383333325386, 0.333333343267, + 0.383333325386, 0.349999994040, 0.383333325386, 0.366666674614, + 0.383333325386, 0.383333325386, 0.383333325386, 0.400000005960, + 0.383333325386, 0.416666656733, 0.383333325386, 0.433333337307, + 0.383333325386, 0.449999988079, 0.383333325386, 0.466666668653, + 0.383333325386, 0.483333319426, 0.383333325386, 0.500000000000, + 0.383333325386, 0.516666650772, 0.383333325386, 0.533333361149, + 0.383333325386, 0.550000011921, 0.383333325386, 0.566666662693, + 0.383333325386, 0.583333313465, 0.383333325386, 0.600000023842, + 0.383333325386, 0.616666674614, 0.383333325386, 0.633333325386, + 0.383333325386, 0.649999976158, 0.383333325386, 0.666666686535, + 0.383333325386, 0.683333337307, 0.383333325386, 0.699999988079, + 0.383333325386, 0.716666638851, 0.383333325386, 0.733333349228, + 0.383333325386, 0.750000000000, 0.383333325386, 0.766666650772, + 0.383333325386, 0.783333361149, 0.383333325386, 0.800000011921, + 0.383333325386, 0.816666662693, 0.383333325386, 0.833333313465, + 0.383333325386, 0.850000023842, 0.383333325386, 0.866666674614, + 0.383333325386, 0.883333325386, 0.383333325386, 0.899999976158, + 0.383333325386, 0.916666686535, 0.383333325386, 0.933333337307, + 0.383333325386, 0.949999988079, 0.383333325386, 0.966666638851, + 0.383333325386, 0.983333349228, 0.383333325386, 1.000000000000, + 0.400000005960, 0.000000000000, 0.400000005960, 0.016666667536, + 0.400000005960, 0.033333335072, 0.400000005960, 0.050000000745, + 0.400000005960, 0.066666670144, 0.400000005960, 0.083333335817, + 0.400000005960, 0.100000001490, 0.400000005960, 0.116666667163, + 0.400000005960, 0.133333340287, 0.400000005960, 0.150000005960, + 0.400000005960, 0.166666671634, 0.400000005960, 0.183333337307, + 0.400000005960, 0.200000002980, 0.400000005960, 0.216666668653, + 0.400000005960, 0.233333334327, 0.400000005960, 0.250000000000, + 0.400000005960, 0.266666680574, 0.400000005960, 0.283333331347, + 0.400000005960, 0.300000011921, 0.400000005960, 0.316666662693, + 0.400000005960, 0.333333343267, 0.400000005960, 0.349999994040, + 0.400000005960, 0.366666674614, 0.400000005960, 0.383333325386, + 0.400000005960, 0.400000005960, 0.400000005960, 0.416666656733, + 0.400000005960, 0.433333337307, 0.400000005960, 0.449999988079, + 0.400000005960, 0.466666668653, 0.400000005960, 0.483333319426, + 0.400000005960, 0.500000000000, 0.400000005960, 0.516666650772, + 0.400000005960, 0.533333361149, 0.400000005960, 0.550000011921, + 0.400000005960, 0.566666662693, 0.400000005960, 0.583333313465, + 0.400000005960, 0.600000023842, 0.400000005960, 0.616666674614, + 0.400000005960, 0.633333325386, 0.400000005960, 0.649999976158, + 0.400000005960, 0.666666686535, 0.400000005960, 0.683333337307, + 0.400000005960, 0.699999988079, 0.400000005960, 0.716666638851, + 0.400000005960, 0.733333349228, 0.400000005960, 0.750000000000, + 0.400000005960, 0.766666650772, 0.400000005960, 0.783333361149, + 0.400000005960, 0.800000011921, 0.400000005960, 0.816666662693, + 0.400000005960, 0.833333313465, 0.400000005960, 0.850000023842, + 0.400000005960, 0.866666674614, 0.400000005960, 0.883333325386, + 0.400000005960, 0.899999976158, 0.400000005960, 0.916666686535, + 0.400000005960, 0.933333337307, 0.400000005960, 0.949999988079, + 0.400000005960, 0.966666638851, 0.400000005960, 0.983333349228, + 0.400000005960, 1.000000000000, 0.416666656733, 0.000000000000, + 0.416666656733, 0.016666667536, 0.416666656733, 0.033333335072, + 0.416666656733, 0.050000000745, 0.416666656733, 0.066666670144, + 0.416666656733, 0.083333335817, 0.416666656733, 0.100000001490, + 0.416666656733, 0.116666667163, 0.416666656733, 0.133333340287, + 0.416666656733, 0.150000005960, 0.416666656733, 0.166666671634, + 0.416666656733, 0.183333337307, 0.416666656733, 0.200000002980, + 0.416666656733, 0.216666668653, 0.416666656733, 0.233333334327, + 0.416666656733, 0.250000000000, 0.416666656733, 0.266666680574, + 0.416666656733, 0.283333331347, 0.416666656733, 0.300000011921, + 0.416666656733, 0.316666662693, 0.416666656733, 0.333333343267, + 0.416666656733, 0.349999994040, 0.416666656733, 0.366666674614, + 0.416666656733, 0.383333325386, 0.416666656733, 0.400000005960, + 0.416666656733, 0.416666656733, 0.416666656733, 0.433333337307, + 0.416666656733, 0.449999988079, 0.416666656733, 0.466666668653, + 0.416666656733, 0.483333319426, 0.416666656733, 0.500000000000, + 0.416666656733, 0.516666650772, 0.416666656733, 0.533333361149, + 0.416666656733, 0.550000011921, 0.416666656733, 0.566666662693, + 0.416666656733, 0.583333313465, 0.416666656733, 0.600000023842, + 0.416666656733, 0.616666674614, 0.416666656733, 0.633333325386, + 0.416666656733, 0.649999976158, 0.416666656733, 0.666666686535, + 0.416666656733, 0.683333337307, 0.416666656733, 0.699999988079, + 0.416666656733, 0.716666638851, 0.416666656733, 0.733333349228, + 0.416666656733, 0.750000000000, 0.416666656733, 0.766666650772, + 0.416666656733, 0.783333361149, 0.416666656733, 0.800000011921, + 0.416666656733, 0.816666662693, 0.416666656733, 0.833333313465, + 0.416666656733, 0.850000023842, 0.416666656733, 0.866666674614, + 0.416666656733, 0.883333325386, 0.416666656733, 0.899999976158, + 0.416666656733, 0.916666686535, 0.416666656733, 0.933333337307, + 0.416666656733, 0.949999988079, 0.416666656733, 0.966666638851, + 0.416666656733, 0.983333349228, 0.416666656733, 1.000000000000, + 0.433333337307, 0.000000000000, 0.433333337307, 0.016666667536, + 0.433333337307, 0.033333335072, 0.433333337307, 0.050000000745, + 0.433333337307, 0.066666670144, 0.433333337307, 0.083333335817, + 0.433333337307, 0.100000001490, 0.433333337307, 0.116666667163, + 0.433333337307, 0.133333340287, 0.433333337307, 0.150000005960, + 0.433333337307, 0.166666671634, 0.433333337307, 0.183333337307, + 0.433333337307, 0.200000002980, 0.433333337307, 0.216666668653, + 0.433333337307, 0.233333334327, 0.433333337307, 0.250000000000, + 0.433333337307, 0.266666680574, 0.433333337307, 0.283333331347, + 0.433333337307, 0.300000011921, 0.433333337307, 0.316666662693, + 0.433333337307, 0.333333343267, 0.433333337307, 0.349999994040, + 0.433333337307, 0.366666674614, 0.433333337307, 0.383333325386, + 0.433333337307, 0.400000005960, 0.433333337307, 0.416666656733, + 0.433333337307, 0.433333337307, 0.433333337307, 0.449999988079, + 0.433333337307, 0.466666668653, 0.433333337307, 0.483333319426, + 0.433333337307, 0.500000000000, 0.433333337307, 0.516666650772, + 0.433333337307, 0.533333361149, 0.433333337307, 0.550000011921, + 0.433333337307, 0.566666662693, 0.433333337307, 0.583333313465, + 0.433333337307, 0.600000023842, 0.433333337307, 0.616666674614, + 0.433333337307, 0.633333325386, 0.433333337307, 0.649999976158, + 0.433333337307, 0.666666686535, 0.433333337307, 0.683333337307, + 0.433333337307, 0.699999988079, 0.433333337307, 0.716666638851, + 0.433333337307, 0.733333349228, 0.433333337307, 0.750000000000, + 0.433333337307, 0.766666650772, 0.433333337307, 0.783333361149, + 0.433333337307, 0.800000011921, 0.433333337307, 0.816666662693, + 0.433333337307, 0.833333313465, 0.433333337307, 0.850000023842, + 0.433333337307, 0.866666674614, 0.433333337307, 0.883333325386, + 0.433333337307, 0.899999976158, 0.433333337307, 0.916666686535, + 0.433333337307, 0.933333337307, 0.433333337307, 0.949999988079, + 0.433333337307, 0.966666638851, 0.433333337307, 0.983333349228, + 0.433333337307, 1.000000000000, 0.449999988079, 0.000000000000, + 0.449999988079, 0.016666667536, 0.449999988079, 0.033333335072, + 0.449999988079, 0.050000000745, 0.449999988079, 0.066666670144, + 0.449999988079, 0.083333335817, 0.449999988079, 0.100000001490, + 0.449999988079, 0.116666667163, 0.449999988079, 0.133333340287, + 0.449999988079, 0.150000005960, 0.449999988079, 0.166666671634, + 0.449999988079, 0.183333337307, 0.449999988079, 0.200000002980, + 0.449999988079, 0.216666668653, 0.449999988079, 0.233333334327, + 0.449999988079, 0.250000000000, 0.449999988079, 0.266666680574, + 0.449999988079, 0.283333331347, 0.449999988079, 0.300000011921, + 0.449999988079, 0.316666662693, 0.449999988079, 0.333333343267, + 0.449999988079, 0.349999994040, 0.449999988079, 0.366666674614, + 0.449999988079, 0.383333325386, 0.449999988079, 0.400000005960, + 0.449999988079, 0.416666656733, 0.449999988079, 0.433333337307, + 0.449999988079, 0.449999988079, 0.449999988079, 0.466666668653, + 0.449999988079, 0.483333319426, 0.449999988079, 0.500000000000, + 0.449999988079, 0.516666650772, 0.449999988079, 0.533333361149, + 0.449999988079, 0.550000011921, 0.449999988079, 0.566666662693, + 0.449999988079, 0.583333313465, 0.449999988079, 0.600000023842, + 0.449999988079, 0.616666674614, 0.449999988079, 0.633333325386, + 0.449999988079, 0.649999976158, 0.449999988079, 0.666666686535, + 0.449999988079, 0.683333337307, 0.449999988079, 0.699999988079, + 0.449999988079, 0.716666638851, 0.449999988079, 0.733333349228, + 0.449999988079, 0.750000000000, 0.449999988079, 0.766666650772, + 0.449999988079, 0.783333361149, 0.449999988079, 0.800000011921, + 0.449999988079, 0.816666662693, 0.449999988079, 0.833333313465, + 0.449999988079, 0.850000023842, 0.449999988079, 0.866666674614, + 0.449999988079, 0.883333325386, 0.449999988079, 0.899999976158, + 0.449999988079, 0.916666686535, 0.449999988079, 0.933333337307, + 0.449999988079, 0.949999988079, 0.449999988079, 0.966666638851, + 0.449999988079, 0.983333349228, 0.449999988079, 1.000000000000, + 0.466666668653, 0.000000000000, 0.466666668653, 0.016666667536, + 0.466666668653, 0.033333335072, 0.466666668653, 0.050000000745, + 0.466666668653, 0.066666670144, 0.466666668653, 0.083333335817, + 0.466666668653, 0.100000001490, 0.466666668653, 0.116666667163, + 0.466666668653, 0.133333340287, 0.466666668653, 0.150000005960, + 0.466666668653, 0.166666671634, 0.466666668653, 0.183333337307, + 0.466666668653, 0.200000002980, 0.466666668653, 0.216666668653, + 0.466666668653, 0.233333334327, 0.466666668653, 0.250000000000, + 0.466666668653, 0.266666680574, 0.466666668653, 0.283333331347, + 0.466666668653, 0.300000011921, 0.466666668653, 0.316666662693, + 0.466666668653, 0.333333343267, 0.466666668653, 0.349999994040, + 0.466666668653, 0.366666674614, 0.466666668653, 0.383333325386, + 0.466666668653, 0.400000005960, 0.466666668653, 0.416666656733, + 0.466666668653, 0.433333337307, 0.466666668653, 0.449999988079, + 0.466666668653, 0.466666668653, 0.466666668653, 0.483333319426, + 0.466666668653, 0.500000000000, 0.466666668653, 0.516666650772, + 0.466666668653, 0.533333361149, 0.466666668653, 0.550000011921, + 0.466666668653, 0.566666662693, 0.466666668653, 0.583333313465, + 0.466666668653, 0.600000023842, 0.466666668653, 0.616666674614, + 0.466666668653, 0.633333325386, 0.466666668653, 0.649999976158, + 0.466666668653, 0.666666686535, 0.466666668653, 0.683333337307, + 0.466666668653, 0.699999988079, 0.466666668653, 0.716666638851, + 0.466666668653, 0.733333349228, 0.466666668653, 0.750000000000, + 0.466666668653, 0.766666650772, 0.466666668653, 0.783333361149, + 0.466666668653, 0.800000011921, 0.466666668653, 0.816666662693, + 0.466666668653, 0.833333313465, 0.466666668653, 0.850000023842, + 0.466666668653, 0.866666674614, 0.466666668653, 0.883333325386, + 0.466666668653, 0.899999976158, 0.466666668653, 0.916666686535, + 0.466666668653, 0.933333337307, 0.466666668653, 0.949999988079, + 0.466666668653, 0.966666638851, 0.466666668653, 0.983333349228, + 0.466666668653, 1.000000000000, 0.483333319426, 0.000000000000, + 0.483333319426, 0.016666667536, 0.483333319426, 0.033333335072, + 0.483333319426, 0.050000000745, 0.483333319426, 0.066666670144, + 0.483333319426, 0.083333335817, 0.483333319426, 0.100000001490, + 0.483333319426, 0.116666667163, 0.483333319426, 0.133333340287, + 0.483333319426, 0.150000005960, 0.483333319426, 0.166666671634, + 0.483333319426, 0.183333337307, 0.483333319426, 0.200000002980, + 0.483333319426, 0.216666668653, 0.483333319426, 0.233333334327, + 0.483333319426, 0.250000000000, 0.483333319426, 0.266666680574, + 0.483333319426, 0.283333331347, 0.483333319426, 0.300000011921, + 0.483333319426, 0.316666662693, 0.483333319426, 0.333333343267, + 0.483333319426, 0.349999994040, 0.483333319426, 0.366666674614, + 0.483333319426, 0.383333325386, 0.483333319426, 0.400000005960, + 0.483333319426, 0.416666656733, 0.483333319426, 0.433333337307, + 0.483333319426, 0.449999988079, 0.483333319426, 0.466666668653, + 0.483333319426, 0.483333319426, 0.483333319426, 0.500000000000, + 0.483333319426, 0.516666650772, 0.483333319426, 0.533333361149, + 0.483333319426, 0.550000011921, 0.483333319426, 0.566666662693, + 0.483333319426, 0.583333313465, 0.483333319426, 0.600000023842, + 0.483333319426, 0.616666674614, 0.483333319426, 0.633333325386, + 0.483333319426, 0.649999976158, 0.483333319426, 0.666666686535, + 0.483333319426, 0.683333337307, 0.483333319426, 0.699999988079, + 0.483333319426, 0.716666638851, 0.483333319426, 0.733333349228, + 0.483333319426, 0.750000000000, 0.483333319426, 0.766666650772, + 0.483333319426, 0.783333361149, 0.483333319426, 0.800000011921, + 0.483333319426, 0.816666662693, 0.483333319426, 0.833333313465, + 0.483333319426, 0.850000023842, 0.483333319426, 0.866666674614, + 0.483333319426, 0.883333325386, 0.483333319426, 0.899999976158, + 0.483333319426, 0.916666686535, 0.483333319426, 0.933333337307, + 0.483333319426, 0.949999988079, 0.483333319426, 0.966666638851, + 0.483333319426, 0.983333349228, 0.483333319426, 1.000000000000, + 0.500000000000, 0.000000000000, 0.500000000000, 0.016666667536, + 0.500000000000, 0.033333335072, 0.500000000000, 0.050000000745, + 0.500000000000, 0.066666670144, 0.500000000000, 0.083333335817, + 0.500000000000, 0.100000001490, 0.500000000000, 0.116666667163, + 0.500000000000, 0.133333340287, 0.500000000000, 0.150000005960, + 0.500000000000, 0.166666671634, 0.500000000000, 0.183333337307, + 0.500000000000, 0.200000002980, 0.500000000000, 0.216666668653, + 0.500000000000, 0.233333334327, 0.500000000000, 0.250000000000, + 0.500000000000, 0.266666680574, 0.500000000000, 0.283333331347, + 0.500000000000, 0.300000011921, 0.500000000000, 0.316666662693, + 0.500000000000, 0.333333343267, 0.500000000000, 0.349999994040, + 0.500000000000, 0.366666674614, 0.500000000000, 0.383333325386, + 0.500000000000, 0.400000005960, 0.500000000000, 0.416666656733, + 0.500000000000, 0.433333337307, 0.500000000000, 0.449999988079, + 0.500000000000, 0.466666668653, 0.500000000000, 0.483333319426, + 0.500000000000, 0.500000000000, 0.500000000000, 0.516666650772, + 0.500000000000, 0.533333361149, 0.500000000000, 0.550000011921, + 0.500000000000, 0.566666662693, 0.500000000000, 0.583333313465, + 0.500000000000, 0.600000023842, 0.500000000000, 0.616666674614, + 0.500000000000, 0.633333325386, 0.500000000000, 0.649999976158, + 0.500000000000, 0.666666686535, 0.500000000000, 0.683333337307, + 0.500000000000, 0.699999988079, 0.500000000000, 0.716666638851, + 0.500000000000, 0.733333349228, 0.500000000000, 0.750000000000, + 0.500000000000, 0.766666650772, 0.500000000000, 0.783333361149, + 0.500000000000, 0.800000011921, 0.500000000000, 0.816666662693, + 0.500000000000, 0.833333313465, 0.500000000000, 0.850000023842, + 0.500000000000, 0.866666674614, 0.500000000000, 0.883333325386, + 0.500000000000, 0.899999976158, 0.500000000000, 0.916666686535, + 0.500000000000, 0.933333337307, 0.500000000000, 0.949999988079, + 0.500000000000, 0.966666638851, 0.500000000000, 0.983333349228, + 0.500000000000, 1.000000000000, 0.516666650772, 0.000000000000, + 0.516666650772, 0.016666667536, 0.516666650772, 0.033333335072, + 0.516666650772, 0.050000000745, 0.516666650772, 0.066666670144, + 0.516666650772, 0.083333335817, 0.516666650772, 0.100000001490, + 0.516666650772, 0.116666667163, 0.516666650772, 0.133333340287, + 0.516666650772, 0.150000005960, 0.516666650772, 0.166666671634, + 0.516666650772, 0.183333337307, 0.516666650772, 0.200000002980, + 0.516666650772, 0.216666668653, 0.516666650772, 0.233333334327, + 0.516666650772, 0.250000000000, 0.516666650772, 0.266666680574, + 0.516666650772, 0.283333331347, 0.516666650772, 0.300000011921, + 0.516666650772, 0.316666662693, 0.516666650772, 0.333333343267, + 0.516666650772, 0.349999994040, 0.516666650772, 0.366666674614, + 0.516666650772, 0.383333325386, 0.516666650772, 0.400000005960, + 0.516666650772, 0.416666656733, 0.516666650772, 0.433333337307, + 0.516666650772, 0.449999988079, 0.516666650772, 0.466666668653, + 0.516666650772, 0.483333319426, 0.516666650772, 0.500000000000, + 0.516666650772, 0.516666650772, 0.516666650772, 0.533333361149, + 0.516666650772, 0.550000011921, 0.516666650772, 0.566666662693, + 0.516666650772, 0.583333313465, 0.516666650772, 0.600000023842, + 0.516666650772, 0.616666674614, 0.516666650772, 0.633333325386, + 0.516666650772, 0.649999976158, 0.516666650772, 0.666666686535, + 0.516666650772, 0.683333337307, 0.516666650772, 0.699999988079, + 0.516666650772, 0.716666638851, 0.516666650772, 0.733333349228, + 0.516666650772, 0.750000000000, 0.516666650772, 0.766666650772, + 0.516666650772, 0.783333361149, 0.516666650772, 0.800000011921, + 0.516666650772, 0.816666662693, 0.516666650772, 0.833333313465, + 0.516666650772, 0.850000023842, 0.516666650772, 0.866666674614, + 0.516666650772, 0.883333325386, 0.516666650772, 0.899999976158, + 0.516666650772, 0.916666686535, 0.516666650772, 0.933333337307, + 0.516666650772, 0.949999988079, 0.516666650772, 0.966666638851, + 0.516666650772, 0.983333349228, 0.516666650772, 1.000000000000, + 0.533333361149, 0.000000000000, 0.533333361149, 0.016666667536, + 0.533333361149, 0.033333335072, 0.533333361149, 0.050000000745, + 0.533333361149, 0.066666670144, 0.533333361149, 0.083333335817, + 0.533333361149, 0.100000001490, 0.533333361149, 0.116666667163, + 0.533333361149, 0.133333340287, 0.533333361149, 0.150000005960, + 0.533333361149, 0.166666671634, 0.533333361149, 0.183333337307, + 0.533333361149, 0.200000002980, 0.533333361149, 0.216666668653, + 0.533333361149, 0.233333334327, 0.533333361149, 0.250000000000, + 0.533333361149, 0.266666680574, 0.533333361149, 0.283333331347, + 0.533333361149, 0.300000011921, 0.533333361149, 0.316666662693, + 0.533333361149, 0.333333343267, 0.533333361149, 0.349999994040, + 0.533333361149, 0.366666674614, 0.533333361149, 0.383333325386, + 0.533333361149, 0.400000005960, 0.533333361149, 0.416666656733, + 0.533333361149, 0.433333337307, 0.533333361149, 0.449999988079, + 0.533333361149, 0.466666668653, 0.533333361149, 0.483333319426, + 0.533333361149, 0.500000000000, 0.533333361149, 0.516666650772, + 0.533333361149, 0.533333361149, 0.533333361149, 0.550000011921, + 0.533333361149, 0.566666662693, 0.533333361149, 0.583333313465, + 0.533333361149, 0.600000023842, 0.533333361149, 0.616666674614, + 0.533333361149, 0.633333325386, 0.533333361149, 0.649999976158, + 0.533333361149, 0.666666686535, 0.533333361149, 0.683333337307, + 0.533333361149, 0.699999988079, 0.533333361149, 0.716666638851, + 0.533333361149, 0.733333349228, 0.533333361149, 0.750000000000, + 0.533333361149, 0.766666650772, 0.533333361149, 0.783333361149, + 0.533333361149, 0.800000011921, 0.533333361149, 0.816666662693, + 0.533333361149, 0.833333313465, 0.533333361149, 0.850000023842, + 0.533333361149, 0.866666674614, 0.533333361149, 0.883333325386, + 0.533333361149, 0.899999976158, 0.533333361149, 0.916666686535, + 0.533333361149, 0.933333337307, 0.533333361149, 0.949999988079, + 0.533333361149, 0.966666638851, 0.533333361149, 0.983333349228, + 0.533333361149, 1.000000000000, 0.550000011921, 0.000000000000, + 0.550000011921, 0.016666667536, 0.550000011921, 0.033333335072, + 0.550000011921, 0.050000000745, 0.550000011921, 0.066666670144, + 0.550000011921, 0.083333335817, 0.550000011921, 0.100000001490, + 0.550000011921, 0.116666667163, 0.550000011921, 0.133333340287, + 0.550000011921, 0.150000005960, 0.550000011921, 0.166666671634, + 0.550000011921, 0.183333337307, 0.550000011921, 0.200000002980, + 0.550000011921, 0.216666668653, 0.550000011921, 0.233333334327, + 0.550000011921, 0.250000000000, 0.550000011921, 0.266666680574, + 0.550000011921, 0.283333331347, 0.550000011921, 0.300000011921, + 0.550000011921, 0.316666662693, 0.550000011921, 0.333333343267, + 0.550000011921, 0.349999994040, 0.550000011921, 0.366666674614, + 0.550000011921, 0.383333325386, 0.550000011921, 0.400000005960, + 0.550000011921, 0.416666656733, 0.550000011921, 0.433333337307, + 0.550000011921, 0.449999988079, 0.550000011921, 0.466666668653, + 0.550000011921, 0.483333319426, 0.550000011921, 0.500000000000, + 0.550000011921, 0.516666650772, 0.550000011921, 0.533333361149, + 0.550000011921, 0.550000011921, 0.550000011921, 0.566666662693, + 0.550000011921, 0.583333313465, 0.550000011921, 0.600000023842, + 0.550000011921, 0.616666674614, 0.550000011921, 0.633333325386, + 0.550000011921, 0.649999976158, 0.550000011921, 0.666666686535, + 0.550000011921, 0.683333337307, 0.550000011921, 0.699999988079, + 0.550000011921, 0.716666638851, 0.550000011921, 0.733333349228, + 0.550000011921, 0.750000000000, 0.550000011921, 0.766666650772, + 0.550000011921, 0.783333361149, 0.550000011921, 0.800000011921, + 0.550000011921, 0.816666662693, 0.550000011921, 0.833333313465, + 0.550000011921, 0.850000023842, 0.550000011921, 0.866666674614, + 0.550000011921, 0.883333325386, 0.550000011921, 0.899999976158, + 0.550000011921, 0.916666686535, 0.550000011921, 0.933333337307, + 0.550000011921, 0.949999988079, 0.550000011921, 0.966666638851, + 0.550000011921, 0.983333349228, 0.550000011921, 1.000000000000, + 0.566666662693, 0.000000000000, 0.566666662693, 0.016666667536, + 0.566666662693, 0.033333335072, 0.566666662693, 0.050000000745, + 0.566666662693, 0.066666670144, 0.566666662693, 0.083333335817, + 0.566666662693, 0.100000001490, 0.566666662693, 0.116666667163, + 0.566666662693, 0.133333340287, 0.566666662693, 0.150000005960, + 0.566666662693, 0.166666671634, 0.566666662693, 0.183333337307, + 0.566666662693, 0.200000002980, 0.566666662693, 0.216666668653, + 0.566666662693, 0.233333334327, 0.566666662693, 0.250000000000, + 0.566666662693, 0.266666680574, 0.566666662693, 0.283333331347, + 0.566666662693, 0.300000011921, 0.566666662693, 0.316666662693, + 0.566666662693, 0.333333343267, 0.566666662693, 0.349999994040, + 0.566666662693, 0.366666674614, 0.566666662693, 0.383333325386, + 0.566666662693, 0.400000005960, 0.566666662693, 0.416666656733, + 0.566666662693, 0.433333337307, 0.566666662693, 0.449999988079, + 0.566666662693, 0.466666668653, 0.566666662693, 0.483333319426, + 0.566666662693, 0.500000000000, 0.566666662693, 0.516666650772, + 0.566666662693, 0.533333361149, 0.566666662693, 0.550000011921, + 0.566666662693, 0.566666662693, 0.566666662693, 0.583333313465, + 0.566666662693, 0.600000023842, 0.566666662693, 0.616666674614, + 0.566666662693, 0.633333325386, 0.566666662693, 0.649999976158, + 0.566666662693, 0.666666686535, 0.566666662693, 0.683333337307, + 0.566666662693, 0.699999988079, 0.566666662693, 0.716666638851, + 0.566666662693, 0.733333349228, 0.566666662693, 0.750000000000, + 0.566666662693, 0.766666650772, 0.566666662693, 0.783333361149, + 0.566666662693, 0.800000011921, 0.566666662693, 0.816666662693, + 0.566666662693, 0.833333313465, 0.566666662693, 0.850000023842, + 0.566666662693, 0.866666674614, 0.566666662693, 0.883333325386, + 0.566666662693, 0.899999976158, 0.566666662693, 0.916666686535, + 0.566666662693, 0.933333337307, 0.566666662693, 0.949999988079, + 0.566666662693, 0.966666638851, 0.566666662693, 0.983333349228, + 0.566666662693, 1.000000000000, 0.583333313465, 0.000000000000, + 0.583333313465, 0.016666667536, 0.583333313465, 0.033333335072, + 0.583333313465, 0.050000000745, 0.583333313465, 0.066666670144, + 0.583333313465, 0.083333335817, 0.583333313465, 0.100000001490, + 0.583333313465, 0.116666667163, 0.583333313465, 0.133333340287, + 0.583333313465, 0.150000005960, 0.583333313465, 0.166666671634, + 0.583333313465, 0.183333337307, 0.583333313465, 0.200000002980, + 0.583333313465, 0.216666668653, 0.583333313465, 0.233333334327, + 0.583333313465, 0.250000000000, 0.583333313465, 0.266666680574, + 0.583333313465, 0.283333331347, 0.583333313465, 0.300000011921, + 0.583333313465, 0.316666662693, 0.583333313465, 0.333333343267, + 0.583333313465, 0.349999994040, 0.583333313465, 0.366666674614, + 0.583333313465, 0.383333325386, 0.583333313465, 0.400000005960, + 0.583333313465, 0.416666656733, 0.583333313465, 0.433333337307, + 0.583333313465, 0.449999988079, 0.583333313465, 0.466666668653, + 0.583333313465, 0.483333319426, 0.583333313465, 0.500000000000, + 0.583333313465, 0.516666650772, 0.583333313465, 0.533333361149, + 0.583333313465, 0.550000011921, 0.583333313465, 0.566666662693, + 0.583333313465, 0.583333313465, 0.583333313465, 0.600000023842, + 0.583333313465, 0.616666674614, 0.583333313465, 0.633333325386, + 0.583333313465, 0.649999976158, 0.583333313465, 0.666666686535, + 0.583333313465, 0.683333337307, 0.583333313465, 0.699999988079, + 0.583333313465, 0.716666638851, 0.583333313465, 0.733333349228, + 0.583333313465, 0.750000000000, 0.583333313465, 0.766666650772, + 0.583333313465, 0.783333361149, 0.583333313465, 0.800000011921, + 0.583333313465, 0.816666662693, 0.583333313465, 0.833333313465, + 0.583333313465, 0.850000023842, 0.583333313465, 0.866666674614, + 0.583333313465, 0.883333325386, 0.583333313465, 0.899999976158, + 0.583333313465, 0.916666686535, 0.583333313465, 0.933333337307, + 0.583333313465, 0.949999988079, 0.583333313465, 0.966666638851, + 0.583333313465, 0.983333349228, 0.583333313465, 1.000000000000, + 0.600000023842, 0.000000000000, 0.600000023842, 0.016666667536, + 0.600000023842, 0.033333335072, 0.600000023842, 0.050000000745, + 0.600000023842, 0.066666670144, 0.600000023842, 0.083333335817, + 0.600000023842, 0.100000001490, 0.600000023842, 0.116666667163, + 0.600000023842, 0.133333340287, 0.600000023842, 0.150000005960, + 0.600000023842, 0.166666671634, 0.600000023842, 0.183333337307, + 0.600000023842, 0.200000002980, 0.600000023842, 0.216666668653, + 0.600000023842, 0.233333334327, 0.600000023842, 0.250000000000, + 0.600000023842, 0.266666680574, 0.600000023842, 0.283333331347, + 0.600000023842, 0.300000011921, 0.600000023842, 0.316666662693, + 0.600000023842, 0.333333343267, 0.600000023842, 0.349999994040, + 0.600000023842, 0.366666674614, 0.600000023842, 0.383333325386, + 0.600000023842, 0.400000005960, 0.600000023842, 0.416666656733, + 0.600000023842, 0.433333337307, 0.600000023842, 0.449999988079, + 0.600000023842, 0.466666668653, 0.600000023842, 0.483333319426, + 0.600000023842, 0.500000000000, 0.600000023842, 0.516666650772, + 0.600000023842, 0.533333361149, 0.600000023842, 0.550000011921, + 0.600000023842, 0.566666662693, 0.600000023842, 0.583333313465, + 0.600000023842, 0.600000023842, 0.600000023842, 0.616666674614, + 0.600000023842, 0.633333325386, 0.600000023842, 0.649999976158, + 0.600000023842, 0.666666686535, 0.600000023842, 0.683333337307, + 0.600000023842, 0.699999988079, 0.600000023842, 0.716666638851, + 0.600000023842, 0.733333349228, 0.600000023842, 0.750000000000, + 0.600000023842, 0.766666650772, 0.600000023842, 0.783333361149, + 0.600000023842, 0.800000011921, 0.600000023842, 0.816666662693, + 0.600000023842, 0.833333313465, 0.600000023842, 0.850000023842, + 0.600000023842, 0.866666674614, 0.600000023842, 0.883333325386, + 0.600000023842, 0.899999976158, 0.600000023842, 0.916666686535, + 0.600000023842, 0.933333337307, 0.600000023842, 0.949999988079, + 0.600000023842, 0.966666638851, 0.600000023842, 0.983333349228, + 0.600000023842, 1.000000000000, 0.616666674614, 0.000000000000, + 0.616666674614, 0.016666667536, 0.616666674614, 0.033333335072, + 0.616666674614, 0.050000000745, 0.616666674614, 0.066666670144, + 0.616666674614, 0.083333335817, 0.616666674614, 0.100000001490, + 0.616666674614, 0.116666667163, 0.616666674614, 0.133333340287, + 0.616666674614, 0.150000005960, 0.616666674614, 0.166666671634, + 0.616666674614, 0.183333337307, 0.616666674614, 0.200000002980, + 0.616666674614, 0.216666668653, 0.616666674614, 0.233333334327, + 0.616666674614, 0.250000000000, 0.616666674614, 0.266666680574, + 0.616666674614, 0.283333331347, 0.616666674614, 0.300000011921, + 0.616666674614, 0.316666662693, 0.616666674614, 0.333333343267, + 0.616666674614, 0.349999994040, 0.616666674614, 0.366666674614, + 0.616666674614, 0.383333325386, 0.616666674614, 0.400000005960, + 0.616666674614, 0.416666656733, 0.616666674614, 0.433333337307, + 0.616666674614, 0.449999988079, 0.616666674614, 0.466666668653, + 0.616666674614, 0.483333319426, 0.616666674614, 0.500000000000, + 0.616666674614, 0.516666650772, 0.616666674614, 0.533333361149, + 0.616666674614, 0.550000011921, 0.616666674614, 0.566666662693, + 0.616666674614, 0.583333313465, 0.616666674614, 0.600000023842, + 0.616666674614, 0.616666674614, 0.616666674614, 0.633333325386, + 0.616666674614, 0.649999976158, 0.616666674614, 0.666666686535, + 0.616666674614, 0.683333337307, 0.616666674614, 0.699999988079, + 0.616666674614, 0.716666638851, 0.616666674614, 0.733333349228, + 0.616666674614, 0.750000000000, 0.616666674614, 0.766666650772, + 0.616666674614, 0.783333361149, 0.616666674614, 0.800000011921, + 0.616666674614, 0.816666662693, 0.616666674614, 0.833333313465, + 0.616666674614, 0.850000023842, 0.616666674614, 0.866666674614, + 0.616666674614, 0.883333325386, 0.616666674614, 0.899999976158, + 0.616666674614, 0.916666686535, 0.616666674614, 0.933333337307, + 0.616666674614, 0.949999988079, 0.616666674614, 0.966666638851, + 0.616666674614, 0.983333349228, 0.616666674614, 1.000000000000, + 0.633333325386, 0.000000000000, 0.633333325386, 0.016666667536, + 0.633333325386, 0.033333335072, 0.633333325386, 0.050000000745, + 0.633333325386, 0.066666670144, 0.633333325386, 0.083333335817, + 0.633333325386, 0.100000001490, 0.633333325386, 0.116666667163, + 0.633333325386, 0.133333340287, 0.633333325386, 0.150000005960, + 0.633333325386, 0.166666671634, 0.633333325386, 0.183333337307, + 0.633333325386, 0.200000002980, 0.633333325386, 0.216666668653, + 0.633333325386, 0.233333334327, 0.633333325386, 0.250000000000, + 0.633333325386, 0.266666680574, 0.633333325386, 0.283333331347, + 0.633333325386, 0.300000011921, 0.633333325386, 0.316666662693, + 0.633333325386, 0.333333343267, 0.633333325386, 0.349999994040, + 0.633333325386, 0.366666674614, 0.633333325386, 0.383333325386, + 0.633333325386, 0.400000005960, 0.633333325386, 0.416666656733, + 0.633333325386, 0.433333337307, 0.633333325386, 0.449999988079, + 0.633333325386, 0.466666668653, 0.633333325386, 0.483333319426, + 0.633333325386, 0.500000000000, 0.633333325386, 0.516666650772, + 0.633333325386, 0.533333361149, 0.633333325386, 0.550000011921, + 0.633333325386, 0.566666662693, 0.633333325386, 0.583333313465, + 0.633333325386, 0.600000023842, 0.633333325386, 0.616666674614, + 0.633333325386, 0.633333325386, 0.633333325386, 0.649999976158, + 0.633333325386, 0.666666686535, 0.633333325386, 0.683333337307, + 0.633333325386, 0.699999988079, 0.633333325386, 0.716666638851, + 0.633333325386, 0.733333349228, 0.633333325386, 0.750000000000, + 0.633333325386, 0.766666650772, 0.633333325386, 0.783333361149, + 0.633333325386, 0.800000011921, 0.633333325386, 0.816666662693, + 0.633333325386, 0.833333313465, 0.633333325386, 0.850000023842, + 0.633333325386, 0.866666674614, 0.633333325386, 0.883333325386, + 0.633333325386, 0.899999976158, 0.633333325386, 0.916666686535, + 0.633333325386, 0.933333337307, 0.633333325386, 0.949999988079, + 0.633333325386, 0.966666638851, 0.633333325386, 0.983333349228, + 0.633333325386, 1.000000000000, 0.649999976158, 0.000000000000, + 0.649999976158, 0.016666667536, 0.649999976158, 0.033333335072, + 0.649999976158, 0.050000000745, 0.649999976158, 0.066666670144, + 0.649999976158, 0.083333335817, 0.649999976158, 0.100000001490, + 0.649999976158, 0.116666667163, 0.649999976158, 0.133333340287, + 0.649999976158, 0.150000005960, 0.649999976158, 0.166666671634, + 0.649999976158, 0.183333337307, 0.649999976158, 0.200000002980, + 0.649999976158, 0.216666668653, 0.649999976158, 0.233333334327, + 0.649999976158, 0.250000000000, 0.649999976158, 0.266666680574, + 0.649999976158, 0.283333331347, 0.649999976158, 0.300000011921, + 0.649999976158, 0.316666662693, 0.649999976158, 0.333333343267, + 0.649999976158, 0.349999994040, 0.649999976158, 0.366666674614, + 0.649999976158, 0.383333325386, 0.649999976158, 0.400000005960, + 0.649999976158, 0.416666656733, 0.649999976158, 0.433333337307, + 0.649999976158, 0.449999988079, 0.649999976158, 0.466666668653, + 0.649999976158, 0.483333319426, 0.649999976158, 0.500000000000, + 0.649999976158, 0.516666650772, 0.649999976158, 0.533333361149, + 0.649999976158, 0.550000011921, 0.649999976158, 0.566666662693, + 0.649999976158, 0.583333313465, 0.649999976158, 0.600000023842, + 0.649999976158, 0.616666674614, 0.649999976158, 0.633333325386, + 0.649999976158, 0.649999976158, 0.649999976158, 0.666666686535, + 0.649999976158, 0.683333337307, 0.649999976158, 0.699999988079, + 0.649999976158, 0.716666638851, 0.649999976158, 0.733333349228, + 0.649999976158, 0.750000000000, 0.649999976158, 0.766666650772, + 0.649999976158, 0.783333361149, 0.649999976158, 0.800000011921, + 0.649999976158, 0.816666662693, 0.649999976158, 0.833333313465, + 0.649999976158, 0.850000023842, 0.649999976158, 0.866666674614, + 0.649999976158, 0.883333325386, 0.649999976158, 0.899999976158, + 0.649999976158, 0.916666686535, 0.649999976158, 0.933333337307, + 0.649999976158, 0.949999988079, 0.649999976158, 0.966666638851, + 0.649999976158, 0.983333349228, 0.649999976158, 1.000000000000, + 0.666666686535, 0.000000000000, 0.666666686535, 0.016666667536, + 0.666666686535, 0.033333335072, 0.666666686535, 0.050000000745, + 0.666666686535, 0.066666670144, 0.666666686535, 0.083333335817, + 0.666666686535, 0.100000001490, 0.666666686535, 0.116666667163, + 0.666666686535, 0.133333340287, 0.666666686535, 0.150000005960, + 0.666666686535, 0.166666671634, 0.666666686535, 0.183333337307, + 0.666666686535, 0.200000002980, 0.666666686535, 0.216666668653, + 0.666666686535, 0.233333334327, 0.666666686535, 0.250000000000, + 0.666666686535, 0.266666680574, 0.666666686535, 0.283333331347, + 0.666666686535, 0.300000011921, 0.666666686535, 0.316666662693, + 0.666666686535, 0.333333343267, 0.666666686535, 0.349999994040, + 0.666666686535, 0.366666674614, 0.666666686535, 0.383333325386, + 0.666666686535, 0.400000005960, 0.666666686535, 0.416666656733, + 0.666666686535, 0.433333337307, 0.666666686535, 0.449999988079, + 0.666666686535, 0.466666668653, 0.666666686535, 0.483333319426, + 0.666666686535, 0.500000000000, 0.666666686535, 0.516666650772, + 0.666666686535, 0.533333361149, 0.666666686535, 0.550000011921, + 0.666666686535, 0.566666662693, 0.666666686535, 0.583333313465, + 0.666666686535, 0.600000023842, 0.666666686535, 0.616666674614, + 0.666666686535, 0.633333325386, 0.666666686535, 0.649999976158, + 0.666666686535, 0.666666686535, 0.666666686535, 0.683333337307, + 0.666666686535, 0.699999988079, 0.666666686535, 0.716666638851, + 0.666666686535, 0.733333349228, 0.666666686535, 0.750000000000, + 0.666666686535, 0.766666650772, 0.666666686535, 0.783333361149, + 0.666666686535, 0.800000011921, 0.666666686535, 0.816666662693, + 0.666666686535, 0.833333313465, 0.666666686535, 0.850000023842, + 0.666666686535, 0.866666674614, 0.666666686535, 0.883333325386, + 0.666666686535, 0.899999976158, 0.666666686535, 0.916666686535, + 0.666666686535, 0.933333337307, 0.666666686535, 0.949999988079, + 0.666666686535, 0.966666638851, 0.666666686535, 0.983333349228, + 0.666666686535, 1.000000000000, 0.683333337307, 0.000000000000, + 0.683333337307, 0.016666667536, 0.683333337307, 0.033333335072, + 0.683333337307, 0.050000000745, 0.683333337307, 0.066666670144, + 0.683333337307, 0.083333335817, 0.683333337307, 0.100000001490, + 0.683333337307, 0.116666667163, 0.683333337307, 0.133333340287, + 0.683333337307, 0.150000005960, 0.683333337307, 0.166666671634, + 0.683333337307, 0.183333337307, 0.683333337307, 0.200000002980, + 0.683333337307, 0.216666668653, 0.683333337307, 0.233333334327, + 0.683333337307, 0.250000000000, 0.683333337307, 0.266666680574, + 0.683333337307, 0.283333331347, 0.683333337307, 0.300000011921, + 0.683333337307, 0.316666662693, 0.683333337307, 0.333333343267, + 0.683333337307, 0.349999994040, 0.683333337307, 0.366666674614, + 0.683333337307, 0.383333325386, 0.683333337307, 0.400000005960, + 0.683333337307, 0.416666656733, 0.683333337307, 0.433333337307, + 0.683333337307, 0.449999988079, 0.683333337307, 0.466666668653, + 0.683333337307, 0.483333319426, 0.683333337307, 0.500000000000, + 0.683333337307, 0.516666650772, 0.683333337307, 0.533333361149, + 0.683333337307, 0.550000011921, 0.683333337307, 0.566666662693, + 0.683333337307, 0.583333313465, 0.683333337307, 0.600000023842, + 0.683333337307, 0.616666674614, 0.683333337307, 0.633333325386, + 0.683333337307, 0.649999976158, 0.683333337307, 0.666666686535, + 0.683333337307, 0.683333337307, 0.683333337307, 0.699999988079, + 0.683333337307, 0.716666638851, 0.683333337307, 0.733333349228, + 0.683333337307, 0.750000000000, 0.683333337307, 0.766666650772, + 0.683333337307, 0.783333361149, 0.683333337307, 0.800000011921, + 0.683333337307, 0.816666662693, 0.683333337307, 0.833333313465, + 0.683333337307, 0.850000023842, 0.683333337307, 0.866666674614, + 0.683333337307, 0.883333325386, 0.683333337307, 0.899999976158, + 0.683333337307, 0.916666686535, 0.683333337307, 0.933333337307, + 0.683333337307, 0.949999988079, 0.683333337307, 0.966666638851, + 0.683333337307, 0.983333349228, 0.683333337307, 1.000000000000, + 0.699999988079, 0.000000000000, 0.699999988079, 0.016666667536, + 0.699999988079, 0.033333335072, 0.699999988079, 0.050000000745, + 0.699999988079, 0.066666670144, 0.699999988079, 0.083333335817, + 0.699999988079, 0.100000001490, 0.699999988079, 0.116666667163, + 0.699999988079, 0.133333340287, 0.699999988079, 0.150000005960, + 0.699999988079, 0.166666671634, 0.699999988079, 0.183333337307, + 0.699999988079, 0.200000002980, 0.699999988079, 0.216666668653, + 0.699999988079, 0.233333334327, 0.699999988079, 0.250000000000, + 0.699999988079, 0.266666680574, 0.699999988079, 0.283333331347, + 0.699999988079, 0.300000011921, 0.699999988079, 0.316666662693, + 0.699999988079, 0.333333343267, 0.699999988079, 0.349999994040, + 0.699999988079, 0.366666674614, 0.699999988079, 0.383333325386, + 0.699999988079, 0.400000005960, 0.699999988079, 0.416666656733, + 0.699999988079, 0.433333337307, 0.699999988079, 0.449999988079, + 0.699999988079, 0.466666668653, 0.699999988079, 0.483333319426, + 0.699999988079, 0.500000000000, 0.699999988079, 0.516666650772, + 0.699999988079, 0.533333361149, 0.699999988079, 0.550000011921, + 0.699999988079, 0.566666662693, 0.699999988079, 0.583333313465, + 0.699999988079, 0.600000023842, 0.699999988079, 0.616666674614, + 0.699999988079, 0.633333325386, 0.699999988079, 0.649999976158, + 0.699999988079, 0.666666686535, 0.699999988079, 0.683333337307, + 0.699999988079, 0.699999988079, 0.699999988079, 0.716666638851, + 0.699999988079, 0.733333349228, 0.699999988079, 0.750000000000, + 0.699999988079, 0.766666650772, 0.699999988079, 0.783333361149, + 0.699999988079, 0.800000011921, 0.699999988079, 0.816666662693, + 0.699999988079, 0.833333313465, 0.699999988079, 0.850000023842, + 0.699999988079, 0.866666674614, 0.699999988079, 0.883333325386, + 0.699999988079, 0.899999976158, 0.699999988079, 0.916666686535, + 0.699999988079, 0.933333337307, 0.699999988079, 0.949999988079, + 0.699999988079, 0.966666638851, 0.699999988079, 0.983333349228, + 0.699999988079, 1.000000000000, 0.716666638851, 0.000000000000, + 0.716666638851, 0.016666667536, 0.716666638851, 0.033333335072, + 0.716666638851, 0.050000000745, 0.716666638851, 0.066666670144, + 0.716666638851, 0.083333335817, 0.716666638851, 0.100000001490, + 0.716666638851, 0.116666667163, 0.716666638851, 0.133333340287, + 0.716666638851, 0.150000005960, 0.716666638851, 0.166666671634, + 0.716666638851, 0.183333337307, 0.716666638851, 0.200000002980, + 0.716666638851, 0.216666668653, 0.716666638851, 0.233333334327, + 0.716666638851, 0.250000000000, 0.716666638851, 0.266666680574, + 0.716666638851, 0.283333331347, 0.716666638851, 0.300000011921, + 0.716666638851, 0.316666662693, 0.716666638851, 0.333333343267, + 0.716666638851, 0.349999994040, 0.716666638851, 0.366666674614, + 0.716666638851, 0.383333325386, 0.716666638851, 0.400000005960, + 0.716666638851, 0.416666656733, 0.716666638851, 0.433333337307, + 0.716666638851, 0.449999988079, 0.716666638851, 0.466666668653, + 0.716666638851, 0.483333319426, 0.716666638851, 0.500000000000, + 0.716666638851, 0.516666650772, 0.716666638851, 0.533333361149, + 0.716666638851, 0.550000011921, 0.716666638851, 0.566666662693, + 0.716666638851, 0.583333313465, 0.716666638851, 0.600000023842, + 0.716666638851, 0.616666674614, 0.716666638851, 0.633333325386, + 0.716666638851, 0.649999976158, 0.716666638851, 0.666666686535, + 0.716666638851, 0.683333337307, 0.716666638851, 0.699999988079, + 0.716666638851, 0.716666638851, 0.716666638851, 0.733333349228, + 0.716666638851, 0.750000000000, 0.716666638851, 0.766666650772, + 0.716666638851, 0.783333361149, 0.716666638851, 0.800000011921, + 0.716666638851, 0.816666662693, 0.716666638851, 0.833333313465, + 0.716666638851, 0.850000023842, 0.716666638851, 0.866666674614, + 0.716666638851, 0.883333325386, 0.716666638851, 0.899999976158, + 0.716666638851, 0.916666686535, 0.716666638851, 0.933333337307, + 0.716666638851, 0.949999988079, 0.716666638851, 0.966666638851, + 0.716666638851, 0.983333349228, 0.716666638851, 1.000000000000, + 0.733333349228, 0.000000000000, 0.733333349228, 0.016666667536, + 0.733333349228, 0.033333335072, 0.733333349228, 0.050000000745, + 0.733333349228, 0.066666670144, 0.733333349228, 0.083333335817, + 0.733333349228, 0.100000001490, 0.733333349228, 0.116666667163, + 0.733333349228, 0.133333340287, 0.733333349228, 0.150000005960, + 0.733333349228, 0.166666671634, 0.733333349228, 0.183333337307, + 0.733333349228, 0.200000002980, 0.733333349228, 0.216666668653, + 0.733333349228, 0.233333334327, 0.733333349228, 0.250000000000, + 0.733333349228, 0.266666680574, 0.733333349228, 0.283333331347, + 0.733333349228, 0.300000011921, 0.733333349228, 0.316666662693, + 0.733333349228, 0.333333343267, 0.733333349228, 0.349999994040, + 0.733333349228, 0.366666674614, 0.733333349228, 0.383333325386, + 0.733333349228, 0.400000005960, 0.733333349228, 0.416666656733, + 0.733333349228, 0.433333337307, 0.733333349228, 0.449999988079, + 0.733333349228, 0.466666668653, 0.733333349228, 0.483333319426, + 0.733333349228, 0.500000000000, 0.733333349228, 0.516666650772, + 0.733333349228, 0.533333361149, 0.733333349228, 0.550000011921, + 0.733333349228, 0.566666662693, 0.733333349228, 0.583333313465, + 0.733333349228, 0.600000023842, 0.733333349228, 0.616666674614, + 0.733333349228, 0.633333325386, 0.733333349228, 0.649999976158, + 0.733333349228, 0.666666686535, 0.733333349228, 0.683333337307, + 0.733333349228, 0.699999988079, 0.733333349228, 0.716666638851, + 0.733333349228, 0.733333349228, 0.733333349228, 0.750000000000, + 0.733333349228, 0.766666650772, 0.733333349228, 0.783333361149, + 0.733333349228, 0.800000011921, 0.733333349228, 0.816666662693, + 0.733333349228, 0.833333313465, 0.733333349228, 0.850000023842, + 0.733333349228, 0.866666674614, 0.733333349228, 0.883333325386, + 0.733333349228, 0.899999976158, 0.733333349228, 0.916666686535, + 0.733333349228, 0.933333337307, 0.733333349228, 0.949999988079, + 0.733333349228, 0.966666638851, 0.733333349228, 0.983333349228, + 0.733333349228, 1.000000000000, 0.750000000000, 0.000000000000, + 0.750000000000, 0.016666667536, 0.750000000000, 0.033333335072, + 0.750000000000, 0.050000000745, 0.750000000000, 0.066666670144, + 0.750000000000, 0.083333335817, 0.750000000000, 0.100000001490, + 0.750000000000, 0.116666667163, 0.750000000000, 0.133333340287, + 0.750000000000, 0.150000005960, 0.750000000000, 0.166666671634, + 0.750000000000, 0.183333337307, 0.750000000000, 0.200000002980, + 0.750000000000, 0.216666668653, 0.750000000000, 0.233333334327, + 0.750000000000, 0.250000000000, 0.750000000000, 0.266666680574, + 0.750000000000, 0.283333331347, 0.750000000000, 0.300000011921, + 0.750000000000, 0.316666662693, 0.750000000000, 0.333333343267, + 0.750000000000, 0.349999994040, 0.750000000000, 0.366666674614, + 0.750000000000, 0.383333325386, 0.750000000000, 0.400000005960, + 0.750000000000, 0.416666656733, 0.750000000000, 0.433333337307, + 0.750000000000, 0.449999988079, 0.750000000000, 0.466666668653, + 0.750000000000, 0.483333319426, 0.750000000000, 0.500000000000, + 0.750000000000, 0.516666650772, 0.750000000000, 0.533333361149, + 0.750000000000, 0.550000011921, 0.750000000000, 0.566666662693, + 0.750000000000, 0.583333313465, 0.750000000000, 0.600000023842, + 0.750000000000, 0.616666674614, 0.750000000000, 0.633333325386, + 0.750000000000, 0.649999976158, 0.750000000000, 0.666666686535, + 0.750000000000, 0.683333337307, 0.750000000000, 0.699999988079, + 0.750000000000, 0.716666638851, 0.750000000000, 0.733333349228, + 0.750000000000, 0.750000000000, 0.750000000000, 0.766666650772, + 0.750000000000, 0.783333361149, 0.750000000000, 0.800000011921, + 0.750000000000, 0.816666662693, 0.750000000000, 0.833333313465, + 0.750000000000, 0.850000023842, 0.750000000000, 0.866666674614, + 0.750000000000, 0.883333325386, 0.750000000000, 0.899999976158, + 0.750000000000, 0.916666686535, 0.750000000000, 0.933333337307, + 0.750000000000, 0.949999988079, 0.750000000000, 0.966666638851, + 0.750000000000, 0.983333349228, 0.750000000000, 1.000000000000, + 0.766666650772, 0.000000000000, 0.766666650772, 0.016666667536, + 0.766666650772, 0.033333335072, 0.766666650772, 0.050000000745, + 0.766666650772, 0.066666670144, 0.766666650772, 0.083333335817, + 0.766666650772, 0.100000001490, 0.766666650772, 0.116666667163, + 0.766666650772, 0.133333340287, 0.766666650772, 0.150000005960, + 0.766666650772, 0.166666671634, 0.766666650772, 0.183333337307, + 0.766666650772, 0.200000002980, 0.766666650772, 0.216666668653, + 0.766666650772, 0.233333334327, 0.766666650772, 0.250000000000, + 0.766666650772, 0.266666680574, 0.766666650772, 0.283333331347, + 0.766666650772, 0.300000011921, 0.766666650772, 0.316666662693, + 0.766666650772, 0.333333343267, 0.766666650772, 0.349999994040, + 0.766666650772, 0.366666674614, 0.766666650772, 0.383333325386, + 0.766666650772, 0.400000005960, 0.766666650772, 0.416666656733, + 0.766666650772, 0.433333337307, 0.766666650772, 0.449999988079, + 0.766666650772, 0.466666668653, 0.766666650772, 0.483333319426, + 0.766666650772, 0.500000000000, 0.766666650772, 0.516666650772, + 0.766666650772, 0.533333361149, 0.766666650772, 0.550000011921, + 0.766666650772, 0.566666662693, 0.766666650772, 0.583333313465, + 0.766666650772, 0.600000023842, 0.766666650772, 0.616666674614, + 0.766666650772, 0.633333325386, 0.766666650772, 0.649999976158, + 0.766666650772, 0.666666686535, 0.766666650772, 0.683333337307, + 0.766666650772, 0.699999988079, 0.766666650772, 0.716666638851, + 0.766666650772, 0.733333349228, 0.766666650772, 0.750000000000, + 0.766666650772, 0.766666650772, 0.766666650772, 0.783333361149, + 0.766666650772, 0.800000011921, 0.766666650772, 0.816666662693, + 0.766666650772, 0.833333313465, 0.766666650772, 0.850000023842, + 0.766666650772, 0.866666674614, 0.766666650772, 0.883333325386, + 0.766666650772, 0.899999976158, 0.766666650772, 0.916666686535, + 0.766666650772, 0.933333337307, 0.766666650772, 0.949999988079, + 0.766666650772, 0.966666638851, 0.766666650772, 0.983333349228, + 0.766666650772, 1.000000000000, 0.783333361149, 0.000000000000, + 0.783333361149, 0.016666667536, 0.783333361149, 0.033333335072, + 0.783333361149, 0.050000000745, 0.783333361149, 0.066666670144, + 0.783333361149, 0.083333335817, 0.783333361149, 0.100000001490, + 0.783333361149, 0.116666667163, 0.783333361149, 0.133333340287, + 0.783333361149, 0.150000005960, 0.783333361149, 0.166666671634, + 0.783333361149, 0.183333337307, 0.783333361149, 0.200000002980, + 0.783333361149, 0.216666668653, 0.783333361149, 0.233333334327, + 0.783333361149, 0.250000000000, 0.783333361149, 0.266666680574, + 0.783333361149, 0.283333331347, 0.783333361149, 0.300000011921, + 0.783333361149, 0.316666662693, 0.783333361149, 0.333333343267, + 0.783333361149, 0.349999994040, 0.783333361149, 0.366666674614, + 0.783333361149, 0.383333325386, 0.783333361149, 0.400000005960, + 0.783333361149, 0.416666656733, 0.783333361149, 0.433333337307, + 0.783333361149, 0.449999988079, 0.783333361149, 0.466666668653, + 0.783333361149, 0.483333319426, 0.783333361149, 0.500000000000, + 0.783333361149, 0.516666650772, 0.783333361149, 0.533333361149, + 0.783333361149, 0.550000011921, 0.783333361149, 0.566666662693, + 0.783333361149, 0.583333313465, 0.783333361149, 0.600000023842, + 0.783333361149, 0.616666674614, 0.783333361149, 0.633333325386, + 0.783333361149, 0.649999976158, 0.783333361149, 0.666666686535, + 0.783333361149, 0.683333337307, 0.783333361149, 0.699999988079, + 0.783333361149, 0.716666638851, 0.783333361149, 0.733333349228, + 0.783333361149, 0.750000000000, 0.783333361149, 0.766666650772, + 0.783333361149, 0.783333361149, 0.783333361149, 0.800000011921, + 0.783333361149, 0.816666662693, 0.783333361149, 0.833333313465, + 0.783333361149, 0.850000023842, 0.783333361149, 0.866666674614, + 0.783333361149, 0.883333325386, 0.783333361149, 0.899999976158, + 0.783333361149, 0.916666686535, 0.783333361149, 0.933333337307, + 0.783333361149, 0.949999988079, 0.783333361149, 0.966666638851, + 0.783333361149, 0.983333349228, 0.783333361149, 1.000000000000, + 0.800000011921, 0.000000000000, 0.800000011921, 0.016666667536, + 0.800000011921, 0.033333335072, 0.800000011921, 0.050000000745, + 0.800000011921, 0.066666670144, 0.800000011921, 0.083333335817, + 0.800000011921, 0.100000001490, 0.800000011921, 0.116666667163, + 0.800000011921, 0.133333340287, 0.800000011921, 0.150000005960, + 0.800000011921, 0.166666671634, 0.800000011921, 0.183333337307, + 0.800000011921, 0.200000002980, 0.800000011921, 0.216666668653, + 0.800000011921, 0.233333334327, 0.800000011921, 0.250000000000, + 0.800000011921, 0.266666680574, 0.800000011921, 0.283333331347, + 0.800000011921, 0.300000011921, 0.800000011921, 0.316666662693, + 0.800000011921, 0.333333343267, 0.800000011921, 0.349999994040, + 0.800000011921, 0.366666674614, 0.800000011921, 0.383333325386, + 0.800000011921, 0.400000005960, 0.800000011921, 0.416666656733, + 0.800000011921, 0.433333337307, 0.800000011921, 0.449999988079, + 0.800000011921, 0.466666668653, 0.800000011921, 0.483333319426, + 0.800000011921, 0.500000000000, 0.800000011921, 0.516666650772, + 0.800000011921, 0.533333361149, 0.800000011921, 0.550000011921, + 0.800000011921, 0.566666662693, 0.800000011921, 0.583333313465, + 0.800000011921, 0.600000023842, 0.800000011921, 0.616666674614, + 0.800000011921, 0.633333325386, 0.800000011921, 0.649999976158, + 0.800000011921, 0.666666686535, 0.800000011921, 0.683333337307, + 0.800000011921, 0.699999988079, 0.800000011921, 0.716666638851, + 0.800000011921, 0.733333349228, 0.800000011921, 0.750000000000, + 0.800000011921, 0.766666650772, 0.800000011921, 0.783333361149, + 0.800000011921, 0.800000011921, 0.800000011921, 0.816666662693, + 0.800000011921, 0.833333313465, 0.800000011921, 0.850000023842, + 0.800000011921, 0.866666674614, 0.800000011921, 0.883333325386, + 0.800000011921, 0.899999976158, 0.800000011921, 0.916666686535, + 0.800000011921, 0.933333337307, 0.800000011921, 0.949999988079, + 0.800000011921, 0.966666638851, 0.800000011921, 0.983333349228, + 0.800000011921, 1.000000000000, 0.816666662693, 0.000000000000, + 0.816666662693, 0.016666667536, 0.816666662693, 0.033333335072, + 0.816666662693, 0.050000000745, 0.816666662693, 0.066666670144, + 0.816666662693, 0.083333335817, 0.816666662693, 0.100000001490, + 0.816666662693, 0.116666667163, 0.816666662693, 0.133333340287, + 0.816666662693, 0.150000005960, 0.816666662693, 0.166666671634, + 0.816666662693, 0.183333337307, 0.816666662693, 0.200000002980, + 0.816666662693, 0.216666668653, 0.816666662693, 0.233333334327, + 0.816666662693, 0.250000000000, 0.816666662693, 0.266666680574, + 0.816666662693, 0.283333331347, 0.816666662693, 0.300000011921, + 0.816666662693, 0.316666662693, 0.816666662693, 0.333333343267, + 0.816666662693, 0.349999994040, 0.816666662693, 0.366666674614, + 0.816666662693, 0.383333325386, 0.816666662693, 0.400000005960, + 0.816666662693, 0.416666656733, 0.816666662693, 0.433333337307, + 0.816666662693, 0.449999988079, 0.816666662693, 0.466666668653, + 0.816666662693, 0.483333319426, 0.816666662693, 0.500000000000, + 0.816666662693, 0.516666650772, 0.816666662693, 0.533333361149, + 0.816666662693, 0.550000011921, 0.816666662693, 0.566666662693, + 0.816666662693, 0.583333313465, 0.816666662693, 0.600000023842, + 0.816666662693, 0.616666674614, 0.816666662693, 0.633333325386, + 0.816666662693, 0.649999976158, 0.816666662693, 0.666666686535, + 0.816666662693, 0.683333337307, 0.816666662693, 0.699999988079, + 0.816666662693, 0.716666638851, 0.816666662693, 0.733333349228, + 0.816666662693, 0.750000000000, 0.816666662693, 0.766666650772, + 0.816666662693, 0.783333361149, 0.816666662693, 0.800000011921, + 0.816666662693, 0.816666662693, 0.816666662693, 0.833333313465, + 0.816666662693, 0.850000023842, 0.816666662693, 0.866666674614, + 0.816666662693, 0.883333325386, 0.816666662693, 0.899999976158, + 0.816666662693, 0.916666686535, 0.816666662693, 0.933333337307, + 0.816666662693, 0.949999988079, 0.816666662693, 0.966666638851, + 0.816666662693, 0.983333349228, 0.816666662693, 1.000000000000, + 0.833333313465, 0.000000000000, 0.833333313465, 0.016666667536, + 0.833333313465, 0.033333335072, 0.833333313465, 0.050000000745, + 0.833333313465, 0.066666670144, 0.833333313465, 0.083333335817, + 0.833333313465, 0.100000001490, 0.833333313465, 0.116666667163, + 0.833333313465, 0.133333340287, 0.833333313465, 0.150000005960, + 0.833333313465, 0.166666671634, 0.833333313465, 0.183333337307, + 0.833333313465, 0.200000002980, 0.833333313465, 0.216666668653, + 0.833333313465, 0.233333334327, 0.833333313465, 0.250000000000, + 0.833333313465, 0.266666680574, 0.833333313465, 0.283333331347, + 0.833333313465, 0.300000011921, 0.833333313465, 0.316666662693, + 0.833333313465, 0.333333343267, 0.833333313465, 0.349999994040, + 0.833333313465, 0.366666674614, 0.833333313465, 0.383333325386, + 0.833333313465, 0.400000005960, 0.833333313465, 0.416666656733, + 0.833333313465, 0.433333337307, 0.833333313465, 0.449999988079, + 0.833333313465, 0.466666668653, 0.833333313465, 0.483333319426, + 0.833333313465, 0.500000000000, 0.833333313465, 0.516666650772, + 0.833333313465, 0.533333361149, 0.833333313465, 0.550000011921, + 0.833333313465, 0.566666662693, 0.833333313465, 0.583333313465, + 0.833333313465, 0.600000023842, 0.833333313465, 0.616666674614, + 0.833333313465, 0.633333325386, 0.833333313465, 0.649999976158, + 0.833333313465, 0.666666686535, 0.833333313465, 0.683333337307, + 0.833333313465, 0.699999988079, 0.833333313465, 0.716666638851, + 0.833333313465, 0.733333349228, 0.833333313465, 0.750000000000, + 0.833333313465, 0.766666650772, 0.833333313465, 0.783333361149, + 0.833333313465, 0.800000011921, 0.833333313465, 0.816666662693, + 0.833333313465, 0.833333313465, 0.833333313465, 0.850000023842, + 0.833333313465, 0.866666674614, 0.833333313465, 0.883333325386, + 0.833333313465, 0.899999976158, 0.833333313465, 0.916666686535, + 0.833333313465, 0.933333337307, 0.833333313465, 0.949999988079, + 0.833333313465, 0.966666638851, 0.833333313465, 0.983333349228, + 0.833333313465, 1.000000000000, 0.850000023842, 0.000000000000, + 0.850000023842, 0.016666667536, 0.850000023842, 0.033333335072, + 0.850000023842, 0.050000000745, 0.850000023842, 0.066666670144, + 0.850000023842, 0.083333335817, 0.850000023842, 0.100000001490, + 0.850000023842, 0.116666667163, 0.850000023842, 0.133333340287, + 0.850000023842, 0.150000005960, 0.850000023842, 0.166666671634, + 0.850000023842, 0.183333337307, 0.850000023842, 0.200000002980, + 0.850000023842, 0.216666668653, 0.850000023842, 0.233333334327, + 0.850000023842, 0.250000000000, 0.850000023842, 0.266666680574, + 0.850000023842, 0.283333331347, 0.850000023842, 0.300000011921, + 0.850000023842, 0.316666662693, 0.850000023842, 0.333333343267, + 0.850000023842, 0.349999994040, 0.850000023842, 0.366666674614, + 0.850000023842, 0.383333325386, 0.850000023842, 0.400000005960, + 0.850000023842, 0.416666656733, 0.850000023842, 0.433333337307, + 0.850000023842, 0.449999988079, 0.850000023842, 0.466666668653, + 0.850000023842, 0.483333319426, 0.850000023842, 0.500000000000, + 0.850000023842, 0.516666650772, 0.850000023842, 0.533333361149, + 0.850000023842, 0.550000011921, 0.850000023842, 0.566666662693, + 0.850000023842, 0.583333313465, 0.850000023842, 0.600000023842, + 0.850000023842, 0.616666674614, 0.850000023842, 0.633333325386, + 0.850000023842, 0.649999976158, 0.850000023842, 0.666666686535, + 0.850000023842, 0.683333337307, 0.850000023842, 0.699999988079, + 0.850000023842, 0.716666638851, 0.850000023842, 0.733333349228, + 0.850000023842, 0.750000000000, 0.850000023842, 0.766666650772, + 0.850000023842, 0.783333361149, 0.850000023842, 0.800000011921, + 0.850000023842, 0.816666662693, 0.850000023842, 0.833333313465, + 0.850000023842, 0.850000023842, 0.850000023842, 0.866666674614, + 0.850000023842, 0.883333325386, 0.850000023842, 0.899999976158, + 0.850000023842, 0.916666686535, 0.850000023842, 0.933333337307, + 0.850000023842, 0.949999988079, 0.850000023842, 0.966666638851, + 0.850000023842, 0.983333349228, 0.850000023842, 1.000000000000, + 0.866666674614, 0.000000000000, 0.866666674614, 0.016666667536, + 0.866666674614, 0.033333335072, 0.866666674614, 0.050000000745, + 0.866666674614, 0.066666670144, 0.866666674614, 0.083333335817, + 0.866666674614, 0.100000001490, 0.866666674614, 0.116666667163, + 0.866666674614, 0.133333340287, 0.866666674614, 0.150000005960, + 0.866666674614, 0.166666671634, 0.866666674614, 0.183333337307, + 0.866666674614, 0.200000002980, 0.866666674614, 0.216666668653, + 0.866666674614, 0.233333334327, 0.866666674614, 0.250000000000, + 0.866666674614, 0.266666680574, 0.866666674614, 0.283333331347, + 0.866666674614, 0.300000011921, 0.866666674614, 0.316666662693, + 0.866666674614, 0.333333343267, 0.866666674614, 0.349999994040, + 0.866666674614, 0.366666674614, 0.866666674614, 0.383333325386, + 0.866666674614, 0.400000005960, 0.866666674614, 0.416666656733, + 0.866666674614, 0.433333337307, 0.866666674614, 0.449999988079, + 0.866666674614, 0.466666668653, 0.866666674614, 0.483333319426, + 0.866666674614, 0.500000000000, 0.866666674614, 0.516666650772, + 0.866666674614, 0.533333361149, 0.866666674614, 0.550000011921, + 0.866666674614, 0.566666662693, 0.866666674614, 0.583333313465, + 0.866666674614, 0.600000023842, 0.866666674614, 0.616666674614, + 0.866666674614, 0.633333325386, 0.866666674614, 0.649999976158, + 0.866666674614, 0.666666686535, 0.866666674614, 0.683333337307, + 0.866666674614, 0.699999988079, 0.866666674614, 0.716666638851, + 0.866666674614, 0.733333349228, 0.866666674614, 0.750000000000, + 0.866666674614, 0.766666650772, 0.866666674614, 0.783333361149, + 0.866666674614, 0.800000011921, 0.866666674614, 0.816666662693, + 0.866666674614, 0.833333313465, 0.866666674614, 0.850000023842, + 0.866666674614, 0.866666674614, 0.866666674614, 0.883333325386, + 0.866666674614, 0.899999976158, 0.866666674614, 0.916666686535, + 0.866666674614, 0.933333337307, 0.866666674614, 0.949999988079, + 0.866666674614, 0.966666638851, 0.866666674614, 0.983333349228, + 0.866666674614, 1.000000000000, 0.883333325386, 0.000000000000, + 0.883333325386, 0.016666667536, 0.883333325386, 0.033333335072, + 0.883333325386, 0.050000000745, 0.883333325386, 0.066666670144, + 0.883333325386, 0.083333335817, 0.883333325386, 0.100000001490, + 0.883333325386, 0.116666667163, 0.883333325386, 0.133333340287, + 0.883333325386, 0.150000005960, 0.883333325386, 0.166666671634, + 0.883333325386, 0.183333337307, 0.883333325386, 0.200000002980, + 0.883333325386, 0.216666668653, 0.883333325386, 0.233333334327, + 0.883333325386, 0.250000000000, 0.883333325386, 0.266666680574, + 0.883333325386, 0.283333331347, 0.883333325386, 0.300000011921, + 0.883333325386, 0.316666662693, 0.883333325386, 0.333333343267, + 0.883333325386, 0.349999994040, 0.883333325386, 0.366666674614, + 0.883333325386, 0.383333325386, 0.883333325386, 0.400000005960, + 0.883333325386, 0.416666656733, 0.883333325386, 0.433333337307, + 0.883333325386, 0.449999988079, 0.883333325386, 0.466666668653, + 0.883333325386, 0.483333319426, 0.883333325386, 0.500000000000, + 0.883333325386, 0.516666650772, 0.883333325386, 0.533333361149, + 0.883333325386, 0.550000011921, 0.883333325386, 0.566666662693, + 0.883333325386, 0.583333313465, 0.883333325386, 0.600000023842, + 0.883333325386, 0.616666674614, 0.883333325386, 0.633333325386, + 0.883333325386, 0.649999976158, 0.883333325386, 0.666666686535, + 0.883333325386, 0.683333337307, 0.883333325386, 0.699999988079, + 0.883333325386, 0.716666638851, 0.883333325386, 0.733333349228, + 0.883333325386, 0.750000000000, 0.883333325386, 0.766666650772, + 0.883333325386, 0.783333361149, 0.883333325386, 0.800000011921, + 0.883333325386, 0.816666662693, 0.883333325386, 0.833333313465, + 0.883333325386, 0.850000023842, 0.883333325386, 0.866666674614, + 0.883333325386, 0.883333325386, 0.883333325386, 0.899999976158, + 0.883333325386, 0.916666686535, 0.883333325386, 0.933333337307, + 0.883333325386, 0.949999988079, 0.883333325386, 0.966666638851, + 0.883333325386, 0.983333349228, 0.883333325386, 1.000000000000, + 0.899999976158, 0.000000000000, 0.899999976158, 0.016666667536, + 0.899999976158, 0.033333335072, 0.899999976158, 0.050000000745, + 0.899999976158, 0.066666670144, 0.899999976158, 0.083333335817, + 0.899999976158, 0.100000001490, 0.899999976158, 0.116666667163, + 0.899999976158, 0.133333340287, 0.899999976158, 0.150000005960, + 0.899999976158, 0.166666671634, 0.899999976158, 0.183333337307, + 0.899999976158, 0.200000002980, 0.899999976158, 0.216666668653, + 0.899999976158, 0.233333334327, 0.899999976158, 0.250000000000, + 0.899999976158, 0.266666680574, 0.899999976158, 0.283333331347, + 0.899999976158, 0.300000011921, 0.899999976158, 0.316666662693, + 0.899999976158, 0.333333343267, 0.899999976158, 0.349999994040, + 0.899999976158, 0.366666674614, 0.899999976158, 0.383333325386, + 0.899999976158, 0.400000005960, 0.899999976158, 0.416666656733, + 0.899999976158, 0.433333337307, 0.899999976158, 0.449999988079, + 0.899999976158, 0.466666668653, 0.899999976158, 0.483333319426, + 0.899999976158, 0.500000000000, 0.899999976158, 0.516666650772, + 0.899999976158, 0.533333361149, 0.899999976158, 0.550000011921, + 0.899999976158, 0.566666662693, 0.899999976158, 0.583333313465, + 0.899999976158, 0.600000023842, 0.899999976158, 0.616666674614, + 0.899999976158, 0.633333325386, 0.899999976158, 0.649999976158, + 0.899999976158, 0.666666686535, 0.899999976158, 0.683333337307, + 0.899999976158, 0.699999988079, 0.899999976158, 0.716666638851, + 0.899999976158, 0.733333349228, 0.899999976158, 0.750000000000, + 0.899999976158, 0.766666650772, 0.899999976158, 0.783333361149, + 0.899999976158, 0.800000011921, 0.899999976158, 0.816666662693, + 0.899999976158, 0.833333313465, 0.899999976158, 0.850000023842, + 0.899999976158, 0.866666674614, 0.899999976158, 0.883333325386, + 0.899999976158, 0.899999976158, 0.899999976158, 0.916666686535, + 0.899999976158, 0.933333337307, 0.899999976158, 0.949999988079, + 0.899999976158, 0.966666638851, 0.899999976158, 0.983333349228, + 0.899999976158, 1.000000000000, 0.916666686535, 0.000000000000, + 0.916666686535, 0.016666667536, 0.916666686535, 0.033333335072, + 0.916666686535, 0.050000000745, 0.916666686535, 0.066666670144, + 0.916666686535, 0.083333335817, 0.916666686535, 0.100000001490, + 0.916666686535, 0.116666667163, 0.916666686535, 0.133333340287, + 0.916666686535, 0.150000005960, 0.916666686535, 0.166666671634, + 0.916666686535, 0.183333337307, 0.916666686535, 0.200000002980, + 0.916666686535, 0.216666668653, 0.916666686535, 0.233333334327, + 0.916666686535, 0.250000000000, 0.916666686535, 0.266666680574, + 0.916666686535, 0.283333331347, 0.916666686535, 0.300000011921, + 0.916666686535, 0.316666662693, 0.916666686535, 0.333333343267, + 0.916666686535, 0.349999994040, 0.916666686535, 0.366666674614, + 0.916666686535, 0.383333325386, 0.916666686535, 0.400000005960, + 0.916666686535, 0.416666656733, 0.916666686535, 0.433333337307, + 0.916666686535, 0.449999988079, 0.916666686535, 0.466666668653, + 0.916666686535, 0.483333319426, 0.916666686535, 0.500000000000, + 0.916666686535, 0.516666650772, 0.916666686535, 0.533333361149, + 0.916666686535, 0.550000011921, 0.916666686535, 0.566666662693, + 0.916666686535, 0.583333313465, 0.916666686535, 0.600000023842, + 0.916666686535, 0.616666674614, 0.916666686535, 0.633333325386, + 0.916666686535, 0.649999976158, 0.916666686535, 0.666666686535, + 0.916666686535, 0.683333337307, 0.916666686535, 0.699999988079, + 0.916666686535, 0.716666638851, 0.916666686535, 0.733333349228, + 0.916666686535, 0.750000000000, 0.916666686535, 0.766666650772, + 0.916666686535, 0.783333361149, 0.916666686535, 0.800000011921, + 0.916666686535, 0.816666662693, 0.916666686535, 0.833333313465, + 0.916666686535, 0.850000023842, 0.916666686535, 0.866666674614, + 0.916666686535, 0.883333325386, 0.916666686535, 0.899999976158, + 0.916666686535, 0.916666686535, 0.916666686535, 0.933333337307, + 0.916666686535, 0.949999988079, 0.916666686535, 0.966666638851, + 0.916666686535, 0.983333349228, 0.916666686535, 1.000000000000, + 0.933333337307, 0.000000000000, 0.933333337307, 0.016666667536, + 0.933333337307, 0.033333335072, 0.933333337307, 0.050000000745, + 0.933333337307, 0.066666670144, 0.933333337307, 0.083333335817, + 0.933333337307, 0.100000001490, 0.933333337307, 0.116666667163, + 0.933333337307, 0.133333340287, 0.933333337307, 0.150000005960, + 0.933333337307, 0.166666671634, 0.933333337307, 0.183333337307, + 0.933333337307, 0.200000002980, 0.933333337307, 0.216666668653, + 0.933333337307, 0.233333334327, 0.933333337307, 0.250000000000, + 0.933333337307, 0.266666680574, 0.933333337307, 0.283333331347, + 0.933333337307, 0.300000011921, 0.933333337307, 0.316666662693, + 0.933333337307, 0.333333343267, 0.933333337307, 0.349999994040, + 0.933333337307, 0.366666674614, 0.933333337307, 0.383333325386, + 0.933333337307, 0.400000005960, 0.933333337307, 0.416666656733, + 0.933333337307, 0.433333337307, 0.933333337307, 0.449999988079, + 0.933333337307, 0.466666668653, 0.933333337307, 0.483333319426, + 0.933333337307, 0.500000000000, 0.933333337307, 0.516666650772, + 0.933333337307, 0.533333361149, 0.933333337307, 0.550000011921, + 0.933333337307, 0.566666662693, 0.933333337307, 0.583333313465, + 0.933333337307, 0.600000023842, 0.933333337307, 0.616666674614, + 0.933333337307, 0.633333325386, 0.933333337307, 0.649999976158, + 0.933333337307, 0.666666686535, 0.933333337307, 0.683333337307, + 0.933333337307, 0.699999988079, 0.933333337307, 0.716666638851, + 0.933333337307, 0.733333349228, 0.933333337307, 0.750000000000, + 0.933333337307, 0.766666650772, 0.933333337307, 0.783333361149, + 0.933333337307, 0.800000011921, 0.933333337307, 0.816666662693, + 0.933333337307, 0.833333313465, 0.933333337307, 0.850000023842, + 0.933333337307, 0.866666674614, 0.933333337307, 0.883333325386, + 0.933333337307, 0.899999976158, 0.933333337307, 0.916666686535, + 0.933333337307, 0.933333337307, 0.933333337307, 0.949999988079, + 0.933333337307, 0.966666638851, 0.933333337307, 0.983333349228, + 0.933333337307, 1.000000000000, 0.949999988079, 0.000000000000, + 0.949999988079, 0.016666667536, 0.949999988079, 0.033333335072, + 0.949999988079, 0.050000000745, 0.949999988079, 0.066666670144, + 0.949999988079, 0.083333335817, 0.949999988079, 0.100000001490, + 0.949999988079, 0.116666667163, 0.949999988079, 0.133333340287, + 0.949999988079, 0.150000005960, 0.949999988079, 0.166666671634, + 0.949999988079, 0.183333337307, 0.949999988079, 0.200000002980, + 0.949999988079, 0.216666668653, 0.949999988079, 0.233333334327, + 0.949999988079, 0.250000000000, 0.949999988079, 0.266666680574, + 0.949999988079, 0.283333331347, 0.949999988079, 0.300000011921, + 0.949999988079, 0.316666662693, 0.949999988079, 0.333333343267, + 0.949999988079, 0.349999994040, 0.949999988079, 0.366666674614, + 0.949999988079, 0.383333325386, 0.949999988079, 0.400000005960, + 0.949999988079, 0.416666656733, 0.949999988079, 0.433333337307, + 0.949999988079, 0.449999988079, 0.949999988079, 0.466666668653, + 0.949999988079, 0.483333319426, 0.949999988079, 0.500000000000, + 0.949999988079, 0.516666650772, 0.949999988079, 0.533333361149, + 0.949999988079, 0.550000011921, 0.949999988079, 0.566666662693, + 0.949999988079, 0.583333313465, 0.949999988079, 0.600000023842, + 0.949999988079, 0.616666674614, 0.949999988079, 0.633333325386, + 0.949999988079, 0.649999976158, 0.949999988079, 0.666666686535, + 0.949999988079, 0.683333337307, 0.949999988079, 0.699999988079, + 0.949999988079, 0.716666638851, 0.949999988079, 0.733333349228, + 0.949999988079, 0.750000000000, 0.949999988079, 0.766666650772, + 0.949999988079, 0.783333361149, 0.949999988079, 0.800000011921, + 0.949999988079, 0.816666662693, 0.949999988079, 0.833333313465, + 0.949999988079, 0.850000023842, 0.949999988079, 0.866666674614, + 0.949999988079, 0.883333325386, 0.949999988079, 0.899999976158, + 0.949999988079, 0.916666686535, 0.949999988079, 0.933333337307, + 0.949999988079, 0.949999988079, 0.949999988079, 0.966666638851, + 0.949999988079, 0.983333349228, 0.949999988079, 1.000000000000, + 0.966666638851, 0.000000000000, 0.966666638851, 0.016666667536, + 0.966666638851, 0.033333335072, 0.966666638851, 0.050000000745, + 0.966666638851, 0.066666670144, 0.966666638851, 0.083333335817, + 0.966666638851, 0.100000001490, 0.966666638851, 0.116666667163, + 0.966666638851, 0.133333340287, 0.966666638851, 0.150000005960, + 0.966666638851, 0.166666671634, 0.966666638851, 0.183333337307, + 0.966666638851, 0.200000002980, 0.966666638851, 0.216666668653, + 0.966666638851, 0.233333334327, 0.966666638851, 0.250000000000, + 0.966666638851, 0.266666680574, 0.966666638851, 0.283333331347, + 0.966666638851, 0.300000011921, 0.966666638851, 0.316666662693, + 0.966666638851, 0.333333343267, 0.966666638851, 0.349999994040, + 0.966666638851, 0.366666674614, 0.966666638851, 0.383333325386, + 0.966666638851, 0.400000005960, 0.966666638851, 0.416666656733, + 0.966666638851, 0.433333337307, 0.966666638851, 0.449999988079, + 0.966666638851, 0.466666668653, 0.966666638851, 0.483333319426, + 0.966666638851, 0.500000000000, 0.966666638851, 0.516666650772, + 0.966666638851, 0.533333361149, 0.966666638851, 0.550000011921, + 0.966666638851, 0.566666662693, 0.966666638851, 0.583333313465, + 0.966666638851, 0.600000023842, 0.966666638851, 0.616666674614, + 0.966666638851, 0.633333325386, 0.966666638851, 0.649999976158, + 0.966666638851, 0.666666686535, 0.966666638851, 0.683333337307, + 0.966666638851, 0.699999988079, 0.966666638851, 0.716666638851, + 0.966666638851, 0.733333349228, 0.966666638851, 0.750000000000, + 0.966666638851, 0.766666650772, 0.966666638851, 0.783333361149, + 0.966666638851, 0.800000011921, 0.966666638851, 0.816666662693, + 0.966666638851, 0.833333313465, 0.966666638851, 0.850000023842, + 0.966666638851, 0.866666674614, 0.966666638851, 0.883333325386, + 0.966666638851, 0.899999976158, 0.966666638851, 0.916666686535, + 0.966666638851, 0.933333337307, 0.966666638851, 0.949999988079, + 0.966666638851, 0.966666638851, 0.966666638851, 0.983333349228, + 0.966666638851, 1.000000000000, 0.983333349228, 0.000000000000, + 0.983333349228, 0.016666667536, 0.983333349228, 0.033333335072, + 0.983333349228, 0.050000000745, 0.983333349228, 0.066666670144, + 0.983333349228, 0.083333335817, 0.983333349228, 0.100000001490, + 0.983333349228, 0.116666667163, 0.983333349228, 0.133333340287, + 0.983333349228, 0.150000005960, 0.983333349228, 0.166666671634, + 0.983333349228, 0.183333337307, 0.983333349228, 0.200000002980, + 0.983333349228, 0.216666668653, 0.983333349228, 0.233333334327, + 0.983333349228, 0.250000000000, 0.983333349228, 0.266666680574, + 0.983333349228, 0.283333331347, 0.983333349228, 0.300000011921, + 0.983333349228, 0.316666662693, 0.983333349228, 0.333333343267, + 0.983333349228, 0.349999994040, 0.983333349228, 0.366666674614, + 0.983333349228, 0.383333325386, 0.983333349228, 0.400000005960, + 0.983333349228, 0.416666656733, 0.983333349228, 0.433333337307, + 0.983333349228, 0.449999988079, 0.983333349228, 0.466666668653, + 0.983333349228, 0.483333319426, 0.983333349228, 0.500000000000, + 0.983333349228, 0.516666650772, 0.983333349228, 0.533333361149, + 0.983333349228, 0.550000011921, 0.983333349228, 0.566666662693, + 0.983333349228, 0.583333313465, 0.983333349228, 0.600000023842, + 0.983333349228, 0.616666674614, 0.983333349228, 0.633333325386, + 0.983333349228, 0.649999976158, 0.983333349228, 0.666666686535, + 0.983333349228, 0.683333337307, 0.983333349228, 0.699999988079, + 0.983333349228, 0.716666638851, 0.983333349228, 0.733333349228, + 0.983333349228, 0.750000000000, 0.983333349228, 0.766666650772, + 0.983333349228, 0.783333361149, 0.983333349228, 0.800000011921, + 0.983333349228, 0.816666662693, 0.983333349228, 0.833333313465, + 0.983333349228, 0.850000023842, 0.983333349228, 0.866666674614, + 0.983333349228, 0.883333325386, 0.983333349228, 0.899999976158, + 0.983333349228, 0.916666686535, 0.983333349228, 0.933333337307, + 0.983333349228, 0.949999988079, 0.983333349228, 0.966666638851, + 0.983333349228, 0.983333349228, 0.983333349228, 1.000000000000, + 1.000000000000, 0.000000000000, 1.000000000000, 0.016666667536, + 1.000000000000, 0.033333335072, 1.000000000000, 0.050000000745, + 1.000000000000, 0.066666670144, 1.000000000000, 0.083333335817, + 1.000000000000, 0.100000001490, 1.000000000000, 0.116666667163, + 1.000000000000, 0.133333340287, 1.000000000000, 0.150000005960, + 1.000000000000, 0.166666671634, 1.000000000000, 0.183333337307, + 1.000000000000, 0.200000002980, 1.000000000000, 0.216666668653, + 1.000000000000, 0.233333334327, 1.000000000000, 0.250000000000, + 1.000000000000, 0.266666680574, 1.000000000000, 0.283333331347, + 1.000000000000, 0.300000011921, 1.000000000000, 0.316666662693, + 1.000000000000, 0.333333343267, 1.000000000000, 0.349999994040, + 1.000000000000, 0.366666674614, 1.000000000000, 0.383333325386, + 1.000000000000, 0.400000005960, 1.000000000000, 0.416666656733, + 1.000000000000, 0.433333337307, 1.000000000000, 0.449999988079, + 1.000000000000, 0.466666668653, 1.000000000000, 0.483333319426, + 1.000000000000, 0.500000000000, 1.000000000000, 0.516666650772, + 1.000000000000, 0.533333361149, 1.000000000000, 0.550000011921, + 1.000000000000, 0.566666662693, 1.000000000000, 0.583333313465, + 1.000000000000, 0.600000023842, 1.000000000000, 0.616666674614, + 1.000000000000, 0.633333325386, 1.000000000000, 0.649999976158, + 1.000000000000, 0.666666686535, 1.000000000000, 0.683333337307, + 1.000000000000, 0.699999988079, 1.000000000000, 0.716666638851, + 1.000000000000, 0.733333349228, 1.000000000000, 0.750000000000, + 1.000000000000, 0.766666650772, 1.000000000000, 0.783333361149, + 1.000000000000, 0.800000011921, 1.000000000000, 0.816666662693, + 1.000000000000, 0.833333313465, 1.000000000000, 0.850000023842, + 1.000000000000, 0.866666674614, 1.000000000000, 0.883333325386, + 1.000000000000, 0.899999976158, 1.000000000000, 0.916666686535, + 1.000000000000, 0.933333337307, 1.000000000000, 0.949999988079, + 1.000000000000, 0.966666638851, 1.000000000000, 0.983333349228, + 1.000000000000, 1.000000000000, +}; + +} /* namespace Pdraw */ + +#endif /* USE_GLES2 */ diff --git a/libpdraw/src/pdraw_gles2_hmd_texcoords_cockpitglasses_blue.cpp b/libpdraw/src/pdraw_gles2_hmd_texcoords_cockpitglasses_blue.cpp index de1e4dc..06d47c1 100644 --- a/libpdraw/src/pdraw_gles2_hmd_texcoords_cockpitglasses_blue.cpp +++ b/libpdraw/src/pdraw_gles2_hmd_texcoords_cockpitglasses_blue.cpp @@ -1,1907 +1,1907 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * OpenGL ES 2.0 HMD distortion correction - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * Extracted from FPVToolbox by Frédéric Bertolus - * https://github.com/niavok/fpvtoolbox - * and translated from Java to C++ - */ - -#ifdef USE_GLES2 - -namespace Pdraw { - -extern const float pdraw_gles2HmdTexCoordsCockpitglassesBlue[7442] = { - -0.007770000026, -0.008129999973, -0.007770000026, 0.008803333156, - -0.007770000026, 0.025736667216, -0.007770000026, 0.042670000345, - -0.007770000026, 0.059603333473, -0.007770000026, 0.076536670327, - -0.007770000026, 0.093469999731, -0.007770000026, 0.110403336585, - -0.007770000026, 0.127336665988, -0.007770000026, 0.144270002842, - -0.007770000026, 0.161203339696, -0.007770000026, 0.178136661649, - -0.007770000026, 0.195069998503, -0.007770000026, 0.212003335357, - -0.007770000026, 0.228936672211, -0.007770000026, 0.245869994164, - -0.007770000026, 0.262803345919, -0.007770000026, 0.279736667871, - -0.007770000026, 0.296669989824, -0.007770000026, 0.313603341579, - -0.007770000026, 0.330536663532, -0.007770000026, 0.347469985485, - -0.007770000026, 0.364403337240, -0.007770000026, 0.381336659193, - -0.007770000026, 0.398270010948, -0.007770000026, 0.415203332901, - -0.007770000026, 0.432136654854, -0.007770000026, 0.449070006609, - -0.007770000026, 0.466003328562, -0.007770000026, 0.482936680317, - -0.007770000026, 0.499870002270, -0.007770000026, 0.516803324223, - -0.007770000026, 0.533736646175, -0.007770000026, 0.550670027733, - -0.007770000026, 0.567603349686, -0.007770000026, 0.584536671638, - -0.007770000026, 0.601469993591, -0.007770000026, 0.618403315544, - -0.007770000026, 0.635336637497, -0.007770000026, 0.652270019054, - -0.007770000026, 0.669203341007, -0.007770000026, 0.686136662960, - -0.007770000026, 0.703069984913, -0.007770000026, 0.720003306866, - -0.007770000026, 0.736936688423, -0.007770000026, 0.753870010376, - -0.007770000026, 0.770803332329, -0.007770000026, 0.787736654282, - -0.007770000026, 0.804669976234, -0.007770000026, 0.821603357792, - -0.007770000026, 0.838536679745, -0.007770000026, 0.855470001698, - -0.007770000026, 0.872403323650, -0.007770000026, 0.889336645603, - -0.007770000026, 0.906270027161, -0.007770000026, 0.923203349113, - -0.007770000026, 0.940136671066, -0.007770000026, 0.957069993019, - -0.007770000026, 0.974003314972, -0.007770000026, 0.990936636925, - -0.007770000026, 1.007869958878, 0.009163333103, -0.008129999973, - 0.009163333103, 0.008803333156, 0.009163333103, 0.025736667216, - 0.009163333103, 0.042670000345, 0.009163333103, 0.059603333473, - 0.009163333103, 0.076536670327, 0.009163333103, 0.093469999731, - 0.009163333103, 0.110403336585, 0.009163333103, 0.127336665988, - 0.009163333103, 0.144270002842, 0.009163333103, 0.161203339696, - 0.009163333103, 0.178136661649, 0.009163333103, 0.195069998503, - 0.009163333103, 0.212003335357, 0.009163333103, 0.228936672211, - 0.009163333103, 0.245869994164, 0.009163333103, 0.262803345919, - 0.009163333103, 0.279736667871, 0.009163333103, 0.296669989824, - 0.009163333103, 0.313603341579, 0.009163333103, 0.330536663532, - 0.009163333103, 0.347469985485, 0.009163333103, 0.364403337240, - 0.009163333103, 0.381336659193, 0.009163333103, 0.398270010948, - 0.009163333103, 0.415203332901, 0.009163333103, 0.432136654854, - 0.009163333103, 0.449070006609, 0.009163333103, 0.466003328562, - 0.009163333103, 0.482936680317, 0.009163333103, 0.499870002270, - 0.009163333103, 0.516803324223, 0.009163333103, 0.533736646175, - 0.009163333103, 0.550670027733, 0.009163333103, 0.567603349686, - 0.009163333103, 0.584536671638, 0.009163333103, 0.601469993591, - 0.009163333103, 0.618403315544, 0.009163333103, 0.635336637497, - 0.009163333103, 0.652270019054, 0.009163333103, 0.669203341007, - 0.009163333103, 0.686136662960, 0.009163333103, 0.703069984913, - 0.009163333103, 0.720003306866, 0.009163333103, 0.736936688423, - 0.009163333103, 0.753870010376, 0.009163333103, 0.770803332329, - 0.009163333103, 0.787736654282, 0.009163333103, 0.804669976234, - 0.009163333103, 0.821603357792, 0.009163333103, 0.838536679745, - 0.009163333103, 0.855470001698, 0.009163333103, 0.872403323650, - 0.009163333103, 0.889336645603, 0.009163333103, 0.906270027161, - 0.009163333103, 0.923203349113, 0.009163333103, 0.940136671066, - 0.009163333103, 0.957069993019, 0.009163333103, 0.974003314972, - 0.009163333103, 0.990936636925, 0.009163333103, 1.007869958878, - 0.026096666232, -0.008129999973, 0.026096666232, 0.008803333156, - 0.026096666232, 0.025736667216, 0.026096666232, 0.042670000345, - 0.026096666232, 0.059603333473, 0.026096666232, 0.076536670327, - 0.026096666232, 0.093469999731, 0.026096666232, 0.110403336585, - 0.026096666232, 0.127336665988, 0.026096666232, 0.144270002842, - 0.026096666232, 0.161203339696, 0.026096666232, 0.178136661649, - 0.026096666232, 0.195069998503, 0.026096666232, 0.212003335357, - 0.026096666232, 0.228936672211, 0.026096666232, 0.245869994164, - 0.026096666232, 0.262803345919, 0.026096666232, 0.279736667871, - 0.026096666232, 0.296669989824, 0.026096666232, 0.313603341579, - 0.026096666232, 0.330536663532, 0.026096666232, 0.347469985485, - 0.026096666232, 0.364403337240, 0.026096666232, 0.381336659193, - 0.026096666232, 0.398270010948, 0.026096666232, 0.415203332901, - 0.026096666232, 0.432136654854, 0.026096666232, 0.449070006609, - 0.026096666232, 0.466003328562, 0.026096666232, 0.482936680317, - 0.026096666232, 0.499870002270, 0.026096666232, 0.516803324223, - 0.026096666232, 0.533736646175, 0.026096666232, 0.550670027733, - 0.026096666232, 0.567603349686, 0.026096666232, 0.584536671638, - 0.026096666232, 0.601469993591, 0.026096666232, 0.618403315544, - 0.026096666232, 0.635336637497, 0.026096666232, 0.652270019054, - 0.026096666232, 0.669203341007, 0.026096666232, 0.686136662960, - 0.026096666232, 0.703069984913, 0.026096666232, 0.720003306866, - 0.026096666232, 0.736936688423, 0.026096666232, 0.753870010376, - 0.026096666232, 0.770803332329, 0.026096666232, 0.787736654282, - 0.026096666232, 0.804669976234, 0.026096666232, 0.821603357792, - 0.026096666232, 0.838536679745, 0.026096666232, 0.855470001698, - 0.026096666232, 0.872403323650, 0.026096666232, 0.889336645603, - 0.026096666232, 0.906270027161, 0.026096666232, 0.923203349113, - 0.026096666232, 0.940136671066, 0.026096666232, 0.957069993019, - 0.026096666232, 0.974003314972, 0.026096666232, 0.990936636925, - 0.026096666232, 1.007869958878, 0.043030001223, -0.008129999973, - 0.043030001223, 0.008803333156, 0.043030001223, 0.025736667216, - 0.043030001223, 0.042670000345, 0.043030001223, 0.059603333473, - 0.043030001223, 0.076536670327, 0.043030001223, 0.093469999731, - 0.043030001223, 0.110403336585, 0.043030001223, 0.127336665988, - 0.043030001223, 0.144270002842, 0.043030001223, 0.161203339696, - 0.043030001223, 0.178136661649, 0.043030001223, 0.195069998503, - 0.043030001223, 0.212003335357, 0.043030001223, 0.228936672211, - 0.043030001223, 0.245869994164, 0.043030001223, 0.262803345919, - 0.043030001223, 0.279736667871, 0.043030001223, 0.296669989824, - 0.043030001223, 0.313603341579, 0.043030001223, 0.330536663532, - 0.043030001223, 0.347469985485, 0.043030001223, 0.364403337240, - 0.043030001223, 0.381336659193, 0.043030001223, 0.398270010948, - 0.043030001223, 0.415203332901, 0.043030001223, 0.432136654854, - 0.043030001223, 0.449070006609, 0.043030001223, 0.466003328562, - 0.043030001223, 0.482936680317, 0.043030001223, 0.499870002270, - 0.043030001223, 0.516803324223, 0.043030001223, 0.533736646175, - 0.043030001223, 0.550670027733, 0.043030001223, 0.567603349686, - 0.043030001223, 0.584536671638, 0.043030001223, 0.601469993591, - 0.043030001223, 0.618403315544, 0.043030001223, 0.635336637497, - 0.043030001223, 0.652270019054, 0.043030001223, 0.669203341007, - 0.043030001223, 0.686136662960, 0.043030001223, 0.703069984913, - 0.043030001223, 0.720003306866, 0.043030001223, 0.736936688423, - 0.043030001223, 0.753870010376, 0.043030001223, 0.770803332329, - 0.043030001223, 0.787736654282, 0.043030001223, 0.804669976234, - 0.043030001223, 0.821603357792, 0.043030001223, 0.838536679745, - 0.043030001223, 0.855470001698, 0.043030001223, 0.872403323650, - 0.043030001223, 0.889336645603, 0.043030001223, 0.906270027161, - 0.043030001223, 0.923203349113, 0.043030001223, 0.940136671066, - 0.043030001223, 0.957069993019, 0.043030001223, 0.974003314972, - 0.043030001223, 0.990936636925, 0.043030001223, 1.007869958878, - 0.059963334352, -0.008129999973, 0.059963334352, 0.008803333156, - 0.059963334352, 0.025736667216, 0.059963334352, 0.042670000345, - 0.059963334352, 0.059603333473, 0.059963334352, 0.076536670327, - 0.059963334352, 0.093469999731, 0.059963334352, 0.110403336585, - 0.059963334352, 0.127336665988, 0.059963334352, 0.144270002842, - 0.059963334352, 0.161203339696, 0.059963334352, 0.178136661649, - 0.059963334352, 0.195069998503, 0.059963334352, 0.212003335357, - 0.059963334352, 0.228936672211, 0.059963334352, 0.245869994164, - 0.059963334352, 0.262803345919, 0.059963334352, 0.279736667871, - 0.059963334352, 0.296669989824, 0.059963334352, 0.313603341579, - 0.059963334352, 0.330536663532, 0.059963334352, 0.347469985485, - 0.059963334352, 0.364403337240, 0.059963334352, 0.381336659193, - 0.059963334352, 0.398270010948, 0.059963334352, 0.415203332901, - 0.059963334352, 0.432136654854, 0.059963334352, 0.449070006609, - 0.059963334352, 0.466003328562, 0.059963334352, 0.482936680317, - 0.059963334352, 0.499870002270, 0.059963334352, 0.516803324223, - 0.059963334352, 0.533736646175, 0.059963334352, 0.550670027733, - 0.059963334352, 0.567603349686, 0.059963334352, 0.584536671638, - 0.059963334352, 0.601469993591, 0.059963334352, 0.618403315544, - 0.059963334352, 0.635336637497, 0.059963334352, 0.652270019054, - 0.059963334352, 0.669203341007, 0.059963334352, 0.686136662960, - 0.059963334352, 0.703069984913, 0.059963334352, 0.720003306866, - 0.059963334352, 0.736936688423, 0.059963334352, 0.753870010376, - 0.059963334352, 0.770803332329, 0.059963334352, 0.787736654282, - 0.059963334352, 0.804669976234, 0.059963334352, 0.821603357792, - 0.059963334352, 0.838536679745, 0.059963334352, 0.855470001698, - 0.059963334352, 0.872403323650, 0.059963334352, 0.889336645603, - 0.059963334352, 0.906270027161, 0.059963334352, 0.923203349113, - 0.059963334352, 0.940136671066, 0.059963334352, 0.957069993019, - 0.059963334352, 0.974003314972, 0.059963334352, 0.990936636925, - 0.059963334352, 1.007869958878, 0.076896667480, -0.008129999973, - 0.076896667480, 0.008803333156, 0.076896667480, 0.025736667216, - 0.076896667480, 0.042670000345, 0.076896667480, 0.059603333473, - 0.076896667480, 0.076536670327, 0.076896667480, 0.093469999731, - 0.076896667480, 0.110403336585, 0.076896667480, 0.127336665988, - 0.076896667480, 0.144270002842, 0.076896667480, 0.161203339696, - 0.076896667480, 0.178136661649, 0.076896667480, 0.195069998503, - 0.076896667480, 0.212003335357, 0.076896667480, 0.228936672211, - 0.076896667480, 0.245869994164, 0.076896667480, 0.262803345919, - 0.076896667480, 0.279736667871, 0.076896667480, 0.296669989824, - 0.076896667480, 0.313603341579, 0.076896667480, 0.330536663532, - 0.076896667480, 0.347469985485, 0.076896667480, 0.364403337240, - 0.076896667480, 0.381336659193, 0.076896667480, 0.398270010948, - 0.076896667480, 0.415203332901, 0.076896667480, 0.432136654854, - 0.076896667480, 0.449070006609, 0.076896667480, 0.466003328562, - 0.076896667480, 0.482936680317, 0.076896667480, 0.499870002270, - 0.076896667480, 0.516803324223, 0.076896667480, 0.533736646175, - 0.076896667480, 0.550670027733, 0.076896667480, 0.567603349686, - 0.076896667480, 0.584536671638, 0.076896667480, 0.601469993591, - 0.076896667480, 0.618403315544, 0.076896667480, 0.635336637497, - 0.076896667480, 0.652270019054, 0.076896667480, 0.669203341007, - 0.076896667480, 0.686136662960, 0.076896667480, 0.703069984913, - 0.076896667480, 0.720003306866, 0.076896667480, 0.736936688423, - 0.076896667480, 0.753870010376, 0.076896667480, 0.770803332329, - 0.076896667480, 0.787736654282, 0.076896667480, 0.804669976234, - 0.076896667480, 0.821603357792, 0.076896667480, 0.838536679745, - 0.076896667480, 0.855470001698, 0.076896667480, 0.872403323650, - 0.076896667480, 0.889336645603, 0.076896667480, 0.906270027161, - 0.076896667480, 0.923203349113, 0.076896667480, 0.940136671066, - 0.076896667480, 0.957069993019, 0.076896667480, 0.974003314972, - 0.076896667480, 0.990936636925, 0.076896667480, 1.007869958878, - 0.093829996884, -0.008129999973, 0.093829996884, 0.008803333156, - 0.093829996884, 0.025736667216, 0.093829996884, 0.042670000345, - 0.093829996884, 0.059603333473, 0.093829996884, 0.076536670327, - 0.093829996884, 0.093469999731, 0.093829996884, 0.110403336585, - 0.093829996884, 0.127336665988, 0.093829996884, 0.144270002842, - 0.093829996884, 0.161203339696, 0.093829996884, 0.178136661649, - 0.093829996884, 0.195069998503, 0.093829996884, 0.212003335357, - 0.093829996884, 0.228936672211, 0.093829996884, 0.245869994164, - 0.093829996884, 0.262803345919, 0.093829996884, 0.279736667871, - 0.093829996884, 0.296669989824, 0.093829996884, 0.313603341579, - 0.093829996884, 0.330536663532, 0.093829996884, 0.347469985485, - 0.093829996884, 0.364403337240, 0.093829996884, 0.381336659193, - 0.093829996884, 0.398270010948, 0.093829996884, 0.415203332901, - 0.093829996884, 0.432136654854, 0.093829996884, 0.449070006609, - 0.093829996884, 0.466003328562, 0.093829996884, 0.482936680317, - 0.093829996884, 0.499870002270, 0.093829996884, 0.516803324223, - 0.093829996884, 0.533736646175, 0.093829996884, 0.550670027733, - 0.093829996884, 0.567603349686, 0.093829996884, 0.584536671638, - 0.093829996884, 0.601469993591, 0.093829996884, 0.618403315544, - 0.093829996884, 0.635336637497, 0.093829996884, 0.652270019054, - 0.093829996884, 0.669203341007, 0.093829996884, 0.686136662960, - 0.093829996884, 0.703069984913, 0.093829996884, 0.720003306866, - 0.093829996884, 0.736936688423, 0.093829996884, 0.753870010376, - 0.093829996884, 0.770803332329, 0.093829996884, 0.787736654282, - 0.093829996884, 0.804669976234, 0.093829996884, 0.821603357792, - 0.093829996884, 0.838536679745, 0.093829996884, 0.855470001698, - 0.093829996884, 0.872403323650, 0.093829996884, 0.889336645603, - 0.093829996884, 0.906270027161, 0.093829996884, 0.923203349113, - 0.093829996884, 0.940136671066, 0.093829996884, 0.957069993019, - 0.093829996884, 0.974003314972, 0.093829996884, 0.990936636925, - 0.093829996884, 1.007869958878, 0.110763333738, -0.008129999973, - 0.110763333738, 0.008803333156, 0.110763333738, 0.025736667216, - 0.110763333738, 0.042670000345, 0.110763333738, 0.059603333473, - 0.110763333738, 0.076536670327, 0.110763333738, 0.093469999731, - 0.110763333738, 0.110403336585, 0.110763333738, 0.127336665988, - 0.110763333738, 0.144270002842, 0.110763333738, 0.161203339696, - 0.110763333738, 0.178136661649, 0.110763333738, 0.195069998503, - 0.110763333738, 0.212003335357, 0.110763333738, 0.228936672211, - 0.110763333738, 0.245869994164, 0.110763333738, 0.262803345919, - 0.110763333738, 0.279736667871, 0.110763333738, 0.296669989824, - 0.110763333738, 0.313603341579, 0.110763333738, 0.330536663532, - 0.110763333738, 0.347469985485, 0.110763333738, 0.364403337240, - 0.110763333738, 0.381336659193, 0.110763333738, 0.398270010948, - 0.110763333738, 0.415203332901, 0.110763333738, 0.432136654854, - 0.110763333738, 0.449070006609, 0.110763333738, 0.466003328562, - 0.110763333738, 0.482936680317, 0.110763333738, 0.499870002270, - 0.110763333738, 0.516803324223, 0.110763333738, 0.533736646175, - 0.110763333738, 0.550670027733, 0.110763333738, 0.567603349686, - 0.110763333738, 0.584536671638, 0.110763333738, 0.601469993591, - 0.110763333738, 0.618403315544, 0.110763333738, 0.635336637497, - 0.110763333738, 0.652270019054, 0.110763333738, 0.669203341007, - 0.110763333738, 0.686136662960, 0.110763333738, 0.703069984913, - 0.110763333738, 0.720003306866, 0.110763333738, 0.736936688423, - 0.110763333738, 0.753870010376, 0.110763333738, 0.770803332329, - 0.110763333738, 0.787736654282, 0.110763333738, 0.804669976234, - 0.110763333738, 0.821603357792, 0.110763333738, 0.838536679745, - 0.110763333738, 0.855470001698, 0.110763333738, 0.872403323650, - 0.110763333738, 0.889336645603, 0.110763333738, 0.906270027161, - 0.110763333738, 0.923203349113, 0.110763333738, 0.940136671066, - 0.110763333738, 0.957069993019, 0.110763333738, 0.974003314972, - 0.110763333738, 0.990936636925, 0.110763333738, 1.007869958878, - 0.127696663141, -0.008129999973, 0.127696663141, 0.008803333156, - 0.127696663141, 0.025736667216, 0.127696663141, 0.042670000345, - 0.127696663141, 0.059603333473, 0.127696663141, 0.076536670327, - 0.127696663141, 0.093469999731, 0.127696663141, 0.110403336585, - 0.127696663141, 0.127336665988, 0.127696663141, 0.144270002842, - 0.127696663141, 0.161203339696, 0.127696663141, 0.178136661649, - 0.127696663141, 0.195069998503, 0.127696663141, 0.212003335357, - 0.127696663141, 0.228936672211, 0.127696663141, 0.245869994164, - 0.127696663141, 0.262803345919, 0.127696663141, 0.279736667871, - 0.127696663141, 0.296669989824, 0.127696663141, 0.313603341579, - 0.127696663141, 0.330536663532, 0.127696663141, 0.347469985485, - 0.127696663141, 0.364403337240, 0.127696663141, 0.381336659193, - 0.127696663141, 0.398270010948, 0.127696663141, 0.415203332901, - 0.127696663141, 0.432136654854, 0.127696663141, 0.449070006609, - 0.127696663141, 0.466003328562, 0.127696663141, 0.482936680317, - 0.127696663141, 0.499870002270, 0.127696663141, 0.516803324223, - 0.127696663141, 0.533736646175, 0.127696663141, 0.550670027733, - 0.127696663141, 0.567603349686, 0.127696663141, 0.584536671638, - 0.127696663141, 0.601469993591, 0.127696663141, 0.618403315544, - 0.127696663141, 0.635336637497, 0.127696663141, 0.652270019054, - 0.127696663141, 0.669203341007, 0.127696663141, 0.686136662960, - 0.127696663141, 0.703069984913, 0.127696663141, 0.720003306866, - 0.127696663141, 0.736936688423, 0.127696663141, 0.753870010376, - 0.127696663141, 0.770803332329, 0.127696663141, 0.787736654282, - 0.127696663141, 0.804669976234, 0.127696663141, 0.821603357792, - 0.127696663141, 0.838536679745, 0.127696663141, 0.855470001698, - 0.127696663141, 0.872403323650, 0.127696663141, 0.889336645603, - 0.127696663141, 0.906270027161, 0.127696663141, 0.923203349113, - 0.127696663141, 0.940136671066, 0.127696663141, 0.957069993019, - 0.127696663141, 0.974003314972, 0.127696663141, 0.990936636925, - 0.127696663141, 1.007869958878, 0.144629999995, -0.008129999973, - 0.144629999995, 0.008803333156, 0.144629999995, 0.025736667216, - 0.144629999995, 0.042670000345, 0.144629999995, 0.059603333473, - 0.144629999995, 0.076536670327, 0.144629999995, 0.093469999731, - 0.144629999995, 0.110403336585, 0.144629999995, 0.127336665988, - 0.144629999995, 0.144270002842, 0.144629999995, 0.161203339696, - 0.144629999995, 0.178136661649, 0.144629999995, 0.195069998503, - 0.144629999995, 0.212003335357, 0.144629999995, 0.228936672211, - 0.144629999995, 0.245869994164, 0.144629999995, 0.262803345919, - 0.144629999995, 0.279736667871, 0.144629999995, 0.296669989824, - 0.144629999995, 0.313603341579, 0.144629999995, 0.330536663532, - 0.144629999995, 0.347469985485, 0.144629999995, 0.364403337240, - 0.144629999995, 0.381336659193, 0.144629999995, 0.398270010948, - 0.144629999995, 0.415203332901, 0.144629999995, 0.432136654854, - 0.144629999995, 0.449070006609, 0.144629999995, 0.466003328562, - 0.144813701510, 0.482945412397, 0.144816666842, 0.499870002270, - 0.144813701510, 0.516794562340, 0.144629999995, 0.533736646175, - 0.144629999995, 0.550670027733, 0.144629999995, 0.567603349686, - 0.144629999995, 0.584536671638, 0.144629999995, 0.601469993591, - 0.144629999995, 0.618403315544, 0.144629999995, 0.635336637497, - 0.144629999995, 0.652270019054, 0.144629999995, 0.669203341007, - 0.144629999995, 0.686136662960, 0.144629999995, 0.703069984913, - 0.144629999995, 0.720003306866, 0.144629999995, 0.736936688423, - 0.144629999995, 0.753870010376, 0.144629999995, 0.770803332329, - 0.144629999995, 0.787736654282, 0.144629999995, 0.804669976234, - 0.144629999995, 0.821603357792, 0.144629999995, 0.838536679745, - 0.144629999995, 0.855470001698, 0.144629999995, 0.872403323650, - 0.144629999995, 0.889336645603, 0.144629999995, 0.906270027161, - 0.144629999995, 0.923203349113, 0.144629999995, 0.940136671066, - 0.144629999995, 0.957069993019, 0.144629999995, 0.974003314972, - 0.144629999995, 0.990936636925, 0.144629999995, 1.007869958878, - 0.161563336849, -0.008129999973, 0.161563336849, 0.008803333156, - 0.161563336849, 0.025736667216, 0.161563336849, 0.042670000345, - 0.161563336849, 0.059603333473, 0.161563336849, 0.076536670327, - 0.161563336849, 0.093469999731, 0.161563336849, 0.110403336585, - 0.161563336849, 0.127336665988, 0.161563336849, 0.144270002842, - 0.161563336849, 0.161203339696, 0.161563336849, 0.178136661649, - 0.161563336849, 0.195069998503, 0.161563336849, 0.212003335357, - 0.161563336849, 0.228936672211, 0.161563336849, 0.245869994164, - 0.161563336849, 0.262803345919, 0.161563336849, 0.279736667871, - 0.161563336849, 0.296669989824, 0.161563336849, 0.313603341579, - 0.161563336849, 0.330536663532, 0.161563336849, 0.347469985485, - 0.161563336849, 0.364403337240, 0.161563336849, 0.381336659193, - 0.161755263805, 0.398327589035, 0.161786675453, 0.415259182453, - 0.161812692881, 0.432186543941, 0.161833107471, 0.449110478163, - 0.161847800016, 0.466031789780, 0.161856666207, 0.482951343060, - 0.161859631538, 0.499870002270, 0.161856666207, 0.516788661480, - 0.161847800016, 0.533708214760, 0.161833107471, 0.550629556179, - 0.161812692881, 0.567553460598, 0.161786675453, 0.584480822086, - 0.161755263805, 0.601412415504, 0.161563336849, 0.618403315544, - 0.161563336849, 0.635336637497, 0.161563336849, 0.652270019054, - 0.161563336849, 0.669203341007, 0.161563336849, 0.686136662960, - 0.161563336849, 0.703069984913, 0.161563336849, 0.720003306866, - 0.161563336849, 0.736936688423, 0.161563336849, 0.753870010376, - 0.161563336849, 0.770803332329, 0.161563336849, 0.787736654282, - 0.161563336849, 0.804669976234, 0.161563336849, 0.821603357792, - 0.161563336849, 0.838536679745, 0.161563336849, 0.855470001698, - 0.161563336849, 0.872403323650, 0.161563336849, 0.889336645603, - 0.161563336849, 0.906270027161, 0.161563336849, 0.923203349113, - 0.161563336849, 0.940136671066, 0.161563336849, 0.957069993019, - 0.161563336849, 0.974003314972, 0.161563336849, 0.990936636925, - 0.161563336849, 1.007869958878, 0.178496673703, -0.008129999973, - 0.178496673703, 0.008803333156, 0.178496673703, 0.025736667216, - 0.178496673703, 0.042670000345, 0.178496673703, 0.059603333473, - 0.178496673703, 0.076536670327, 0.178496673703, 0.093469999731, - 0.178496673703, 0.110403336585, 0.178496673703, 0.127336665988, - 0.178496673703, 0.144270002842, 0.178496673703, 0.161203339696, - 0.178496673703, 0.178136661649, 0.178496673703, 0.195069998503, - 0.178496673703, 0.212003335357, 0.178496673703, 0.228936672211, - 0.178496673703, 0.245869994164, 0.178496673703, 0.262803345919, - 0.178496673703, 0.279736667871, 0.178496673703, 0.296669989824, - 0.178496673703, 0.313603341579, 0.178496673703, 0.330536663532, - 0.178662881255, 0.347548723221, 0.178708851337, 0.364492684603, - 0.178750172257, 0.381430059671, 0.178786605597, 0.398361563683, - 0.178817912936, 0.415287882090, 0.178843840957, 0.432209759951, - 0.178864240646, 0.449128031731, 0.178878918290, 0.466043561697, - 0.178887784481, 0.482957243919, 0.178890734911, 0.499870002270, - 0.178887784481, 0.516782760620, 0.178878918290, 0.533696413040, - 0.178864240646, 0.550611972809, 0.178843840957, 0.567530214787, - 0.178817912936, 0.584452152252, 0.178786605597, 0.601378440857, - 0.178750172257, 0.618309915066, 0.178708851337, 0.635247349739, - 0.178662881255, 0.652191281319, 0.178496673703, 0.669203341007, - 0.178496673703, 0.686136662960, 0.178496673703, 0.703069984913, - 0.178496673703, 0.720003306866, 0.178496673703, 0.736936688423, - 0.178496673703, 0.753870010376, 0.178496673703, 0.770803332329, - 0.178496673703, 0.787736654282, 0.178496673703, 0.804669976234, - 0.178496673703, 0.821603357792, 0.178496673703, 0.838536679745, - 0.178496673703, 0.855470001698, 0.178496673703, 0.872403323650, - 0.178496673703, 0.889336645603, 0.178496673703, 0.906270027161, - 0.178496673703, 0.923203349113, 0.178496673703, 0.940136671066, - 0.178496673703, 0.957069993019, 0.178496673703, 0.974003314972, - 0.178496673703, 0.990936636925, 0.178496673703, 1.007869958878, - 0.195429995656, -0.008129999973, 0.195429995656, 0.008803333156, - 0.195429995656, 0.025736667216, 0.195429995656, 0.042670000345, - 0.195429995656, 0.059603333473, 0.195429995656, 0.076536670327, - 0.195429995656, 0.093469999731, 0.195429995656, 0.110403336585, - 0.195429995656, 0.127336665988, 0.195429995656, 0.144270002842, - 0.195429995656, 0.161203339696, 0.195429995656, 0.178136661649, - 0.195429995656, 0.195069998503, 0.195429995656, 0.212003335357, - 0.195429995656, 0.228936672211, 0.195429995656, 0.245869994164, - 0.195429995656, 0.262803345919, 0.195429995656, 0.279736667871, - 0.195429995656, 0.296669989824, 0.195429995656, 0.313603341579, - 0.195633605123, 0.330649763346, 0.195683375001, 0.347596675158, - 0.195728912950, 0.364536195993, 0.195769920945, 0.381468862295, - 0.195806145668, 0.398395389318, 0.195837303996, 0.415316462517, - 0.195863157511, 0.432232916355, 0.195975631475, 0.449160933495, - 0.195999100804, 0.466066569090, 0.196013256907, 0.482969075441, - 0.196017995477, 0.499870002270, 0.196013256907, 0.516770958900, - 0.195999100804, 0.533673405647, 0.195975631475, 0.550579071045, - 0.195863157511, 0.567507088184, 0.195837303996, 0.584423542023, - 0.195806145668, 0.601344645023, 0.195769920945, 0.618271112442, - 0.195728912950, 0.635203838348, 0.195683375001, 0.652143299580, - 0.195633605123, 0.669090211391, 0.195429995656, 0.686136662960, - 0.195429995656, 0.703069984913, 0.195429995656, 0.720003306866, - 0.195429995656, 0.736936688423, 0.195429995656, 0.753870010376, - 0.195429995656, 0.770803332329, 0.195429995656, 0.787736654282, - 0.195429995656, 0.804669976234, 0.195429995656, 0.821603357792, - 0.195429995656, 0.838536679745, 0.195429995656, 0.855470001698, - 0.195429995656, 0.872403323650, 0.195429995656, 0.889336645603, - 0.195429995656, 0.906270027161, 0.195429995656, 0.923203349113, - 0.195429995656, 0.940136671066, 0.195429995656, 0.957069993019, - 0.195429995656, 0.974003314972, 0.195429995656, 0.990936636925, - 0.195429995656, 1.007869958878, 0.212363332510, -0.008129999973, - 0.212363332510, 0.008803333156, 0.212363332510, 0.025736667216, - 0.212363332510, 0.042670000345, 0.212363332510, 0.059603333473, - 0.212363332510, 0.076536670327, 0.212363332510, 0.093469999731, - 0.212363332510, 0.110403336585, 0.212363332510, 0.127336665988, - 0.212363332510, 0.144270002842, 0.212363332510, 0.161203339696, - 0.212363332510, 0.178136661649, 0.212363332510, 0.195069998503, - 0.212363332510, 0.212003335357, 0.212363332510, 0.228936672211, - 0.212363332510, 0.245869994164, 0.212363332510, 0.262803345919, - 0.212363332510, 0.279736667871, 0.212533727288, 0.296790271997, - 0.212590157986, 0.313750088215, 0.212643086910, 0.330701231956, - 0.212692216039, 0.347644120455, 0.212737247348, 0.364579290152, - 0.212777897716, 0.381507366896, 0.212914198637, 0.398464411497, - 0.212963789701, 0.415379941463, 0.213005021214, 0.432287663221, - 0.213037505746, 0.449188977480, 0.213060960174, 0.466085404158, - 0.213075116277, 0.482978522778, 0.213079854846, 0.499870002270, - 0.213075116277, 0.516761481762, 0.213060960174, 0.533654570580, - 0.213037505746, 0.550551056862, 0.213005021214, 0.567452371120, - 0.212963789701, 0.584360063076, 0.212914198637, 0.601275563240, - 0.212777897716, 0.618232607841, 0.212737247348, 0.635160684586, - 0.212692216039, 0.652095913887, 0.212643086910, 0.669038772583, - 0.212590157986, 0.685989916325, 0.212533727288, 0.702949702740, - 0.212363332510, 0.720003306866, 0.212363332510, 0.736936688423, - 0.212363332510, 0.753870010376, 0.212363332510, 0.770803332329, - 0.212363332510, 0.787736654282, 0.212363332510, 0.804669976234, - 0.212363332510, 0.821603357792, 0.212363332510, 0.838536679745, - 0.212363332510, 0.855470001698, 0.212363332510, 0.872403323650, - 0.212363332510, 0.889336645603, 0.212363332510, 0.906270027161, - 0.212363332510, 0.923203349113, 0.212363332510, 0.940136671066, - 0.212363332510, 0.957069993019, 0.212363332510, 0.974003314972, - 0.212363332510, 0.990936636925, 0.212363332510, 1.007869958878, - 0.229296669364, -0.008129999973, 0.229296669364, 0.008803333156, - 0.229296669364, 0.025736667216, 0.229296669364, 0.042670000345, - 0.229296669364, 0.059603333473, 0.229296669364, 0.076536670327, - 0.229296669364, 0.093469999731, 0.229296669364, 0.110403336585, - 0.229296669364, 0.127336665988, 0.229296669364, 0.144270002842, - 0.229296669364, 0.161203339696, 0.229296669364, 0.178136661649, - 0.229296669364, 0.195069998503, 0.229296669364, 0.212003335357, - 0.229296669364, 0.228936672211, 0.229296669364, 0.245869994164, - 0.229296669364, 0.262803345919, 0.229475349188, 0.279881834984, - 0.229533702135, 0.296847790480, 0.229589030147, 0.313804328442, - 0.229641035199, 0.330751895905, 0.229689434171, 0.347690939903, - 0.229836240411, 0.364673107862, 0.229900613427, 0.381600886583, - 0.229957684875, 0.398517876863, 0.230122134089, 0.415461301804, - 0.230183720589, 0.432358443737, 0.230232328176, 0.449245423079, - 0.230267450213, 0.466124683619, 0.230288669467, 0.482998669147, - 0.230295777321, 0.499870002270, 0.230288669467, 0.516741335392, - 0.230267450213, 0.533615291119, 0.230232328176, 0.550494551659, - 0.230183720589, 0.567381560802, 0.230122134089, 0.584278702736, - 0.229957684875, 0.601222097874, 0.229900613427, 0.618139088154, - 0.229836240411, 0.635066866875, 0.229689434171, 0.652049064636, - 0.229641035199, 0.668988108635, 0.229589030147, 0.685935676098, - 0.229533702135, 0.702892243862, 0.229475349188, 0.719858169556, - 0.229296669364, 0.736936688423, 0.229296669364, 0.753870010376, - 0.229296669364, 0.770803332329, 0.229296669364, 0.787736654282, - 0.229296669364, 0.804669976234, 0.229296669364, 0.821603357792, - 0.229296669364, 0.838536679745, 0.229296669364, 0.855470001698, - 0.229296669364, 0.872403323650, 0.229296669364, 0.889336645603, - 0.229296669364, 0.906270027161, 0.229296669364, 0.923203349113, - 0.229296669364, 0.940136671066, 0.229296669364, 0.957069993019, - 0.229296669364, 0.974003314972, 0.229296669364, 0.990936636925, - 0.229296669364, 1.007869958878, 0.246230006218, -0.008129999973, - 0.246230006218, 0.008803333156, 0.246230006218, 0.025736667216, - 0.246230006218, 0.042670000345, 0.246230006218, 0.059603333473, - 0.246230006218, 0.076536670327, 0.246230006218, 0.093469999731, - 0.246230006218, 0.110403336585, 0.246230006218, 0.127336665988, - 0.246230006218, 0.144270002842, 0.246230006218, 0.161203339696, - 0.246230006218, 0.178136661649, 0.246230006218, 0.195069998503, - 0.246230006218, 0.212003335357, 0.246230006218, 0.228936672211, - 0.246230006218, 0.245869994164, 0.246406152844, 0.262967735529, - 0.246465608478, 0.279940843582, 0.246522501111, 0.296903997660, - 0.246576577425, 0.313857495785, 0.246716052294, 0.330860704184, - 0.246792122722, 0.347807288170, 0.246862217784, 0.364740520716, - 0.247048705816, 0.381718724966, 0.247133493423, 0.398631393909, - 0.247206896544, 0.415528953075, 0.247268170118, 0.432413518429, - 0.246979996562, 0.449220001698, 0.246979996562, 0.466103345156, - 0.246979996562, 0.482986658812, 0.246979996562, 0.499870002270, - 0.246979996562, 0.516753315926, 0.246979996562, 0.533636689186, - 0.246979996562, 0.550520002842, 0.247268170118, 0.567326486111, - 0.247206896544, 0.584211051464, 0.247133493423, 0.601108610630, - 0.247048705816, 0.618021249771, 0.246862217784, 0.634999454021, - 0.246792122722, 0.651932716370, 0.246716052294, 0.668879270554, - 0.246576577425, 0.685882508755, 0.246522501111, 0.702835977077, - 0.246465608478, 0.719799160957, 0.246406152844, 0.736772239208, - 0.246230006218, 0.753870010376, 0.246230006218, 0.770803332329, - 0.246230006218, 0.787736654282, 0.246230006218, 0.804669976234, - 0.246230006218, 0.821603357792, 0.246230006218, 0.838536679745, - 0.246230006218, 0.855470001698, 0.246230006218, 0.872403323650, - 0.246230006218, 0.889336645603, 0.246230006218, 0.906270027161, - 0.246230006218, 0.923203349113, 0.246230006218, 0.940136671066, - 0.246230006218, 0.957069993019, 0.246230006218, 0.974003314972, - 0.246230006218, 0.990936636925, 0.246230006218, 1.007869958878, - 0.263163328171, -0.008129999973, 0.263163328171, 0.008803333156, - 0.263163328171, 0.025736667216, 0.263163328171, 0.042670000345, - 0.263163328171, 0.059603333473, 0.263163328171, 0.076536670327, - 0.263163328171, 0.093469999731, 0.263163328171, 0.110403336585, - 0.263163328171, 0.127336665988, 0.263163328171, 0.144270002842, - 0.263163328171, 0.161203339696, 0.263163328171, 0.178136661649, - 0.263163328171, 0.195069998503, 0.263163328171, 0.212003335357, - 0.263163328171, 0.228936672211, 0.263327747583, 0.246046155691, - 0.263387411833, 0.263027429581, 0.263444989920, 0.279998213053, - 0.263500243425, 0.296958774328, 0.263646632433, 0.313983052969, - 0.263726234436, 0.330938756466, 0.263909459114, 0.347949653864, - 0.264012753963, 0.364888727665, 0.264106750488, 0.381808370352, - 0.263863325119, 0.398570001125, 0.263863325119, 0.415453344584, - 0.263863325119, 0.432336658239, 0.263863325119, 0.449220001698, - 0.263863325119, 0.466103345156, 0.264124900103, 0.483005344868, - 0.264131397009, 0.499870002270, 0.264124900103, 0.516734659672, - 0.263863325119, 0.533636689186, 0.263863325119, 0.550520002842, - 0.263863325119, 0.567403316498, 0.263863325119, 0.584286689758, - 0.263863325119, 0.601170003414, 0.264106750488, 0.617931604385, - 0.264012753963, 0.634851276875, 0.263909459114, 0.651790320873, - 0.263726234436, 0.668801248074, 0.263646632433, 0.685756921768, - 0.263500243425, 0.702781200409, 0.263444989920, 0.719741761684, - 0.263387411833, 0.736712574959, 0.263327747583, 0.753693819046, - 0.263163328171, 0.770803332329, 0.263163328171, 0.787736654282, - 0.263163328171, 0.804669976234, 0.263163328171, 0.821603357792, - 0.263163328171, 0.838536679745, 0.263163328171, 0.855470001698, - 0.263163328171, 0.872403323650, 0.263163328171, 0.889336645603, - 0.263163328171, 0.906270027161, 0.263163328171, 0.923203349113, - 0.263163328171, 0.940136671066, 0.263163328171, 0.957069993019, - 0.263163328171, 0.974003314972, 0.263163328171, 0.990936636925, - 0.263163328171, 1.007869958878, 0.280096679926, -0.008129999973, - 0.280096679926, 0.008803333156, 0.280096679926, 0.025736667216, - 0.280096679926, 0.042670000345, 0.280096679926, 0.059603333473, - 0.280096679926, 0.076536670327, 0.280096679926, 0.093469999731, - 0.280096679926, 0.110403336585, 0.280096679926, 0.127336665988, - 0.280096679926, 0.144270002842, 0.280096679926, 0.161203339696, - 0.280096679926, 0.178136661649, 0.280096679926, 0.195069998503, - 0.280096679926, 0.212003335357, 0.280241847038, 0.229115337133, - 0.280300855637, 0.246105611324, 0.280358195305, 0.263085007668, - 0.280413687229, 0.280053704977, 0.280559331179, 0.297097057104, - 0.280640959740, 0.314063906670, 0.280834257603, 0.331104040146, - 0.280943304300, 0.348056137562, 0.280746668577, 0.364803344011, - 0.280746668577, 0.381686657667, 0.280746668577, 0.398570001125, - 0.281007736921, 0.415553748608, 0.281063139439, 0.432434052229, - 0.281107157469, 0.449303179979, 0.281139165163, 0.466163724661, - 0.281158566475, 0.483018338680, 0.281165063381, 0.499870002270, - 0.281158566475, 0.516721665859, 0.281139165163, 0.533576309681, - 0.281107157469, 0.550436794758, 0.281063139439, 0.567305982113, - 0.281007736921, 0.584186255932, 0.280746668577, 0.601170003414, - 0.280746668577, 0.618053317070, 0.280746668577, 0.634936690331, - 0.280943304300, 0.651683866978, 0.280834257603, 0.668635964394, - 0.280640959740, 0.685676097870, 0.280559331179, 0.702642917633, - 0.280413687229, 0.719686329365, 0.280358195305, 0.736654996872, - 0.280300855637, 0.753634393215, 0.280241847038, 0.770624637604, - 0.280096679926, 0.787736654282, 0.280096679926, 0.804669976234, - 0.280096679926, 0.821603357792, 0.280096679926, 0.838536679745, - 0.280096679926, 0.855470001698, 0.280096679926, 0.872403323650, - 0.280096679926, 0.889336645603, 0.280096679926, 0.906270027161, - 0.280096679926, 0.923203349113, 0.280096679926, 0.940136671066, - 0.280096679926, 0.957069993019, 0.280096679926, 0.974003314972, - 0.280096679926, 0.990936636925, 0.280096679926, 1.007869958878, - 0.297030001879, -0.008129999973, 0.297030001879, 0.008803333156, - 0.297030001879, 0.025736667216, 0.297030001879, 0.042670000345, - 0.297030001879, 0.059603333473, 0.297030001879, 0.076536670327, - 0.297030001879, 0.093469999731, 0.297030001879, 0.110403336585, - 0.297030001879, 0.127336665988, 0.297030001879, 0.144270002842, - 0.297030001879, 0.161203339696, 0.297030001879, 0.178136661649, - 0.297030001879, 0.195069998503, 0.297150284052, 0.212173715234, - 0.297207772732, 0.229173704982, 0.297264009714, 0.246162503958, - 0.297318786383, 0.263140231371, 0.297457069159, 0.280199319124, - 0.297539114952, 0.297179132700, 0.297731757164, 0.314246594906, - 0.297844111919, 0.331215083599, 0.297630012035, 0.347920000553, - 0.297630012035, 0.364803344011, 0.297876596451, 0.381830513477, - 0.297951072454, 0.398730546236, 0.298016220331, 0.415614247322, - 0.298051029444, 0.432476997375, 0.298090815544, 0.449335187674, - 0.298119783401, 0.466184973717, 0.298137426376, 0.483028948307, - 0.298143327236, 0.499870002270, 0.298137426376, 0.516711056232, - 0.298119783401, 0.533555030823, 0.298090815544, 0.550404787064, - 0.298051029444, 0.567263007164, 0.298016220331, 0.584125757217, - 0.297951072454, 0.601009488106, 0.297876596451, 0.617909491062, - 0.297630012035, 0.634936690331, 0.297630012035, 0.651820003986, - 0.297844111919, 0.668524920940, 0.297731757164, 0.685493409634, - 0.297539114952, 0.702560901642, 0.297457069159, 0.719540655613, - 0.297318786383, 0.736599743366, 0.297264009714, 0.753577470779, - 0.297207772732, 0.770566284657, 0.297150284052, 0.787566304207, - 0.297030001879, 0.804669976234, 0.297030001879, 0.821603357792, - 0.297030001879, 0.838536679745, 0.297030001879, 0.855470001698, - 0.297030001879, 0.872403323650, 0.297030001879, 0.889336645603, - 0.297030001879, 0.906270027161, 0.297030001879, 0.923203349113, - 0.297030001879, 0.940136671066, 0.297030001879, 0.957069993019, - 0.297030001879, 0.974003314972, 0.297030001879, 0.990936636925, - 0.297030001879, 1.007869958878, 0.313963323832, -0.008129999973, - 0.313963323832, 0.008803333156, 0.313963323832, 0.025736667216, - 0.313963323832, 0.042670000345, 0.313963323832, 0.059603333473, - 0.313963323832, 0.076536670327, 0.313963323832, 0.093469999731, - 0.313963323832, 0.110403336585, 0.313963323832, 0.127336665988, - 0.313963323832, 0.144270002842, 0.313963323832, 0.161203339696, - 0.313963323832, 0.178136661649, 0.313963323832, 0.195069998503, - 0.314110100269, 0.212230160832, 0.314164340496, 0.229229032993, - 0.314217478037, 0.246216565371, 0.314343065023, 0.263286620378, - 0.314423888922, 0.280280977488, 0.314606606960, 0.297371745110, - 0.314719617367, 0.314359635115, 0.314513325691, 0.331036657095, - 0.314513325691, 0.347920000553, 0.314781129360, 0.364998072386, - 0.314861863852, 0.381908446550, 0.314914792776, 0.398788988590, - 0.314973056316, 0.415662288666, 0.315022379160, 0.432521790266, - 0.315061897039, 0.449369609356, 0.314936816692, 0.466180324554, - 0.314940333366, 0.483025491238, 0.314941525459, 0.499870002270, - 0.314940333366, 0.516714513302, 0.314936816692, 0.533559679985, - 0.315061897039, 0.550370395184, 0.315022379160, 0.567218244076, - 0.314973056316, 0.584077715874, 0.314914792776, 0.600951015949, - 0.314861863852, 0.617831528187, 0.314781129360, 0.634741902351, - 0.314513325691, 0.651820003986, 0.314513325691, 0.668703317642, - 0.314719617367, 0.685380399227, 0.314606606960, 0.702368259430, - 0.314423888922, 0.719459056854, 0.314343065023, 0.736453354359, - 0.314217478037, 0.753523409367, 0.314164340496, 0.770510971546, - 0.314110100269, 0.787509858608, 0.313963323832, 0.804669976234, - 0.313963323832, 0.821603357792, 0.313963323832, 0.838536679745, - 0.313963323832, 0.855470001698, 0.313963323832, 0.872403323650, - 0.313963323832, 0.889336645603, 0.313963323832, 0.906270027161, - 0.313963323832, 0.923203349113, 0.313963323832, 0.940136671066, - 0.313963323832, 0.957069993019, 0.313963323832, 0.974003314972, - 0.313963323832, 0.990936636925, 0.313963323832, 1.007869958878, - 0.330896675587, -0.008129999973, 0.330896675587, 0.008803333156, - 0.330896675587, 0.025736667216, 0.330896675587, 0.042670000345, - 0.330896675587, 0.059603333473, 0.330896675587, 0.076536670327, - 0.330896675587, 0.093469999731, 0.330896675587, 0.110403336585, - 0.330896675587, 0.127336665988, 0.330896675587, 0.144270002842, - 0.330896675587, 0.161203339696, 0.330896675587, 0.178136661649, - 0.331009775400, 0.195273593068, 0.331061214209, 0.212283074856, - 0.331111907959, 0.229281038046, 0.331220716238, 0.246356055140, - 0.331298738718, 0.263366252184, 0.331464052200, 0.280474275351, - 0.331575095654, 0.297484099865, 0.331396669149, 0.314153343439, - 0.331396669149, 0.331036657095, 0.331659376621, 0.348156452179, - 0.331743776798, 0.365081012249, 0.331799954176, 0.381968975067, - 0.331864506006, 0.398850709200, 0.331781655550, 0.415645837784, - 0.331791371107, 0.432494550943, 0.331799179316, 0.449340760708, - 0.331804931164, 0.466184973717, 0.331808447838, 0.483027845621, - 0.331809639931, 0.499870002270, 0.331808447838, 0.516712129116, - 0.331804931164, 0.533555030823, 0.331799179316, 0.550399243832, - 0.331791371107, 0.567245423794, 0.331781655550, 0.584094166756, - 0.331864506006, 0.600889265537, 0.331799954176, 0.617771029472, - 0.331743776798, 0.634658992290, 0.331659376621, 0.651583552361, - 0.331396669149, 0.668703317642, 0.331396669149, 0.685586690903, - 0.331575095654, 0.702255904675, 0.331464052200, 0.719265758991, - 0.331298738718, 0.736373782158, 0.331220716238, 0.753383934498, - 0.331111907959, 0.770458936691, 0.331061214209, 0.787456929684, - 0.331009775400, 0.804466426373, 0.330896675587, 0.821603357792, - 0.330896675587, 0.838536679745, 0.330896675587, 0.855470001698, - 0.330896675587, 0.872403323650, 0.330896675587, 0.889336645603, - 0.330896675587, 0.906270027161, 0.330896675587, 0.923203349113, - 0.330896675587, 0.940136671066, 0.330896675587, 0.957069993019, - 0.330896675587, 0.974003314972, 0.330896675587, 0.990936636925, - 0.330896675587, 1.007869958878, 0.347829997540, -0.008129999973, - 0.347829997540, 0.008803333156, 0.347829997540, 0.025736667216, - 0.347829997540, 0.042670000345, 0.347829997540, 0.059603333473, - 0.347829997540, 0.076536670327, 0.347829997540, 0.093469999731, - 0.347829997540, 0.110403336585, 0.347829997540, 0.127336665988, - 0.347829997540, 0.144270002842, 0.347829997540, 0.161203339696, - 0.347908735275, 0.178302869201, 0.347956687212, 0.195323377848, - 0.348004102707, 0.212332218885, 0.348050922155, 0.229329437017, - 0.348167270422, 0.246432125568, 0.348309665918, 0.263549476862, - 0.348416149616, 0.280583322048, 0.348280012608, 0.297269999981, - 0.348280012608, 0.314153343439, 0.348516434431, 0.331299364567, - 0.348601579666, 0.348241597414, 0.348660558462, 0.365141600370, - 0.348728805780, 0.382035732269, 0.348634243011, 0.398806154728, - 0.348645359278, 0.415656298399, 0.348654896021, 0.432503283024, - 0.348756641150, 0.449378877878, 0.348776608706, 0.466213703156, - 0.348788857460, 0.483043193817, 0.348792999983, 0.499870002270, - 0.348788857460, 0.516696810722, 0.348776608706, 0.533526301384, - 0.348756641150, 0.550361096859, 0.348654896021, 0.567236721516, - 0.348645359278, 0.584083676338, 0.348634243011, 0.600933849812, - 0.348728805780, 0.617704272270, 0.348660558462, 0.634598374367, - 0.348601579666, 0.651498436928, 0.348516434431, 0.668440639973, - 0.348280012608, 0.685586690903, 0.348280012608, 0.702470004559, - 0.348416149616, 0.719156682491, 0.348309665918, 0.736190557480, - 0.348167270422, 0.753307878971, 0.348050922155, 0.770410597324, - 0.348004102707, 0.787407815456, 0.347956687212, 0.804416596889, - 0.347908735275, 0.821437120438, 0.347829997540, 0.838536679745, - 0.347829997540, 0.855470001698, 0.347829997540, 0.872403323650, - 0.347829997540, 0.889336645603, 0.347829997540, 0.906270027161, - 0.347829997540, 0.923203349113, 0.347829997540, 0.940136671066, - 0.347829997540, 0.957069993019, 0.347829997540, 0.974003314972, - 0.347829997540, 0.990936636925, 0.347829997540, 1.007869958878, - 0.364763319492, -0.008129999973, 0.364763319492, 0.008803333156, - 0.364763319492, 0.025736667216, 0.364763319492, 0.042670000345, - 0.364763319492, 0.059603333473, 0.364763319492, 0.076536670327, - 0.364763319492, 0.093469999731, 0.364763319492, 0.110403336585, - 0.364763319492, 0.127336665988, 0.364763319492, 0.144270002842, - 0.364763319492, 0.161203339696, 0.364852666855, 0.178348839283, - 0.364896178246, 0.195368915796, 0.364939302206, 0.212377250195, - 0.365033119917, 0.229476243258, 0.365100532770, 0.246502220631, - 0.365248709917, 0.263652771711, 0.365163326263, 0.280386656523, - 0.365163326263, 0.297269999981, 0.365358084440, 0.314421117306, - 0.365441024303, 0.331383764744, 0.365501612425, 0.348300576210, - 0.365570634604, 0.365210622549, 0.365481764078, 0.381965279579, - 0.365493714809, 0.398817777634, 0.365590542555, 0.415720343590, - 0.365623027086, 0.432566523552, 0.365649610758, 0.449402362108, - 0.365396708250, 0.466161668301, 0.365377515554, 0.483013451099, - 0.365371048450, 0.499870002270, 0.365377515554, 0.516726553440, - 0.365396708250, 0.533578336239, 0.365649610758, 0.550337672234, - 0.365623027086, 0.567173480988, 0.365590542555, 0.584019660950, - 0.365493714809, 0.600922226906, 0.365481764078, 0.617774724960, - 0.365570634604, 0.634529352188, 0.365501612425, 0.651439428329, - 0.365441024303, 0.668356239796, 0.365358084440, 0.685318887234, - 0.365163326263, 0.702470004559, 0.365163326263, 0.719353318214, - 0.365248709917, 0.736087262630, 0.365100532770, 0.753237783909, - 0.365033119917, 0.770263731480, 0.364939302206, 0.787362754345, - 0.364896178246, 0.804371118546, 0.364852666855, 0.821391165257, - 0.364763319492, 0.838536679745, 0.364763319492, 0.855470001698, - 0.364763319492, 0.872403323650, 0.364763319492, 0.889336645603, - 0.364763319492, 0.906270027161, 0.364763319492, 0.923203349113, - 0.364763319492, 0.940136671066, 0.364763319492, 0.957069993019, - 0.364763319492, 0.974003314972, 0.364763319492, 0.990936636925, - 0.364763319492, 1.007869958878, 0.381696671247, -0.008129999973, - 0.381696671247, 0.008803333156, 0.381696671247, 0.025736667216, - 0.381696671247, 0.042670000345, 0.381696671247, 0.059603333473, - 0.381696671247, 0.076536670327, 0.381696671247, 0.093469999731, - 0.381696671247, 0.110403336585, 0.381696671247, 0.127336665988, - 0.381696671247, 0.144270002842, 0.381696671247, 0.161203339696, - 0.381790071726, 0.178390175104, 0.381828874350, 0.195409923792, - 0.381867378950, 0.212417900562, 0.381960898638, 0.229540601373, - 0.382078737020, 0.246688708663, 0.382168382406, 0.263746738434, - 0.382046669722, 0.280386656523, 0.382190525532, 0.297516614199, - 0.382268458605, 0.314501851797, 0.382328987122, 0.331439971924, - 0.382395744324, 0.348368823528, 0.382325291634, 0.365121752024, - 0.382337421179, 0.381977409124, 0.382432907820, 0.398901075125, - 0.382468760014, 0.415754824877, 0.382234096527, 0.432443767786, - 0.382193356752, 0.449282854795, 0.382162719965, 0.466136485338, - 0.382143646479, 0.483000516891, 0.382435292006, 0.499870002270, - 0.382143646479, 0.516739487648, 0.382162719965, 0.533603489399, - 0.382193356752, 0.550457119942, 0.382234096527, 0.567296206951, - 0.382468760014, 0.583985149860, 0.382432907820, 0.600838899612, - 0.382337421179, 0.617762565613, 0.382325291634, 0.634618222713, - 0.382395744324, 0.651371181011, 0.382328987122, 0.668300032616, - 0.382268458605, 0.685238122940, 0.382190525532, 0.702223420143, - 0.382046669722, 0.719353318214, 0.382168382406, 0.735993266106, - 0.382078737020, 0.753051280975, 0.381960898638, 0.770199418068, - 0.381867378950, 0.787322103977, 0.381828874350, 0.804330050945, - 0.381790071726, 0.821349799633, 0.381696671247, 0.838536679745, - 0.381696671247, 0.855470001698, 0.381696671247, 0.872403323650, - 0.381696671247, 0.889336645603, 0.381696671247, 0.906270027161, - 0.381696671247, 0.923203349113, 0.381696671247, 0.940136671066, - 0.381696671247, 0.957069993019, 0.381696671247, 0.974003314972, - 0.381696671247, 0.990936636925, 0.381696671247, 1.007869958878, - 0.398629993200, -0.008129999973, 0.398629993200, 0.008803333156, - 0.398629993200, 0.025736667216, 0.398629993200, 0.042670000345, - 0.398629993200, 0.059603333473, 0.398629993200, 0.076536670327, - 0.398629993200, 0.093469999731, 0.398629993200, 0.110403336585, - 0.398629993200, 0.127336665988, 0.398629993200, 0.144270002842, - 0.398687571287, 0.161395266652, 0.398721545935, 0.178426608443, - 0.398755371571, 0.195446148515, 0.398824423552, 0.212554186583, - 0.398877888918, 0.229597687721, 0.398991405964, 0.246773496270, - 0.398930013180, 0.263503342867, 0.398930013180, 0.280386656523, - 0.399090528488, 0.297591090202, 0.399148970842, 0.314554810524, - 0.399210721254, 0.331504523754, 0.399166166782, 0.348274230957, - 0.399177789688, 0.365133702755, 0.399261057377, 0.382072925568, - 0.399297624826, 0.398937612772, 0.399070948362, 0.415570765734, - 0.399024069309, 0.432399392128, 0.399292171001, 0.449401080608, - 0.399330347776, 0.466236799955, 0.399354428053, 0.483057409525, - 0.399362653494, 0.499870002270, 0.399354428053, 0.516682624817, - 0.399330347776, 0.533503234386, 0.399292171001, 0.550338923931, - 0.399024069309, 0.567340612411, 0.399070948362, 0.584169209003, - 0.399297624826, 0.600802361965, 0.399261057377, 0.617667078972, - 0.399177789688, 0.634606301785, 0.399166166782, 0.651465773582, - 0.399210721254, 0.668235480785, 0.399148970842, 0.685185194016, - 0.399090528488, 0.702148914337, 0.398930013180, 0.719353318214, - 0.398930013180, 0.736236691475, 0.398991405964, 0.752966523170, - 0.398877888918, 0.770142316818, 0.398824423552, 0.787185788155, - 0.398755371571, 0.804293870926, 0.398721545935, 0.821313381195, - 0.398687571287, 0.838344752789, 0.398629993200, 0.855470001698, - 0.398629993200, 0.872403323650, 0.398629993200, 0.889336645603, - 0.398629993200, 0.906270027161, 0.398629993200, 0.923203349113, - 0.398629993200, 0.940136671066, 0.398629993200, 0.957069993019, - 0.398629993200, 0.974003314972, 0.398629993200, 0.990936636925, - 0.398629993200, 1.007869958878, 0.415563344955, -0.008129999973, - 0.415563344955, 0.008803333156, 0.415563344955, 0.025736667216, - 0.415563344955, 0.042670000345, 0.415563344955, 0.059603333473, - 0.415563344955, 0.076536670327, 0.415563344955, 0.093469999731, - 0.415563344955, 0.110403336585, 0.415563344955, 0.127336665988, - 0.415563344955, 0.144270002842, 0.415619164705, 0.161426678300, - 0.415647864342, 0.178457900882, 0.415676474571, 0.195477306843, - 0.415739923716, 0.212603792548, 0.415821284056, 0.229762136936, - 0.415888965130, 0.246846899390, 0.415813326836, 0.263503342867, - 0.415913730860, 0.280647724867, 0.415974259377, 0.297656208277, - 0.416022300720, 0.314613074064, 0.416005820036, 0.331421643496, - 0.416016310453, 0.348285347223, 0.416080325842, 0.365230530500, - 0.416114836931, 0.382108747959, 0.415930777788, 0.398710936308, - 0.415882587433, 0.415522605181, 0.416140437126, 0.432598352432, - 0.416187912226, 0.449444741011, 0.416185885668, 0.466252356768, - 0.416202843189, 0.483064562082, 0.416208714247, 0.499870002270, - 0.416202843189, 0.516675412655, 0.416185885668, 0.533487677574, - 0.416187912226, 0.550295233727, 0.416140437126, 0.567141652107, - 0.415882587433, 0.584217429161, 0.415930777788, 0.601029038429, - 0.416114836931, 0.617631256580, 0.416080325842, 0.634509444237, - 0.416016310453, 0.651454627514, 0.416005820036, 0.668318331242, - 0.416022300720, 0.685126960278, 0.415974259377, 0.702083766460, - 0.415913730860, 0.719092249870, 0.415813326836, 0.736236691475, - 0.415888965130, 0.752893090248, 0.415821284056, 0.769977867603, - 0.415739923716, 0.787136197090, 0.415676474571, 0.804262697697, - 0.415647864342, 0.821282088757, 0.415619164705, 0.838313341141, - 0.415563344955, 0.855470001698, 0.415563344955, 0.872403323650, - 0.415563344955, 0.889336645603, 0.415563344955, 0.906270027161, - 0.415563344955, 0.923203349113, 0.415563344955, 0.940136671066, - 0.415563344955, 0.957069993019, 0.415563344955, 0.974003314972, - 0.415563344955, 0.990936636925, 0.415563344955, 1.007869958878, - 0.432496666908, -0.008129999973, 0.432496666908, 0.008803333156, - 0.432496666908, 0.025736667216, 0.432496666908, 0.042670000345, - 0.432496666908, 0.059603333473, 0.432496666908, 0.076536670327, - 0.432496666908, 0.093469999731, 0.432496666908, 0.110403336585, - 0.432496666908, 0.127336665988, 0.432496666908, 0.144270002842, - 0.432546526194, 0.161452680826, 0.432569772005, 0.178483843803, - 0.432592928410, 0.195503160357, 0.432647645473, 0.212645024061, - 0.432718425989, 0.229823723435, 0.432773500681, 0.246908172965, - 0.432696670294, 0.263503342867, 0.432794034481, 0.280703127384, - 0.432837009430, 0.297691017389, 0.432881772518, 0.314662396908, - 0.432854533195, 0.331431359053, 0.432863295078, 0.348294883966, - 0.432926505804, 0.365263044834, 0.432803779840, 0.381874084473, - 0.432759374380, 0.398664057255, 0.432958364487, 0.415780454874, - 0.433007895947, 0.432647883892, 0.433012962341, 0.449457228184, - 0.433037996292, 0.466273993254, 0.432919263840, 0.483042299747, - 0.432913988829, 0.499870002270, 0.432919263840, 0.516697704792, - 0.433037996292, 0.533465981483, 0.433012962341, 0.550282776356, - 0.433007895947, 0.567092120647, 0.432958364487, 0.583959579468, - 0.432759374380, 0.601075947285, 0.432803779840, 0.617865920067, - 0.432926505804, 0.634476959705, 0.432863295078, 0.651445090771, - 0.432854533195, 0.668308615685, 0.432881772518, 0.685077607632, - 0.432837009430, 0.702048957348, 0.432794034481, 0.719036877155, - 0.432696670294, 0.736236691475, 0.432773500681, 0.752831816673, - 0.432718425989, 0.769916296005, 0.432647645473, 0.787094950676, - 0.432592928410, 0.804236829281, 0.432569772005, 0.821256160736, - 0.432546526194, 0.838287293911, 0.432496666908, 0.855470001698, - 0.432496666908, 0.872403323650, 0.432496666908, 0.889336645603, - 0.432496666908, 0.906270027161, 0.432496666908, 0.923203349113, - 0.432496666908, 0.940136671066, 0.432496666908, 0.957069993019, - 0.432496666908, 0.974003314972, 0.432496666908, 0.990936636925, - 0.432496666908, 1.007869958878, 0.449429988861, -0.008129999973, - 0.449429988861, 0.008803333156, 0.449429988861, 0.025736667216, - 0.449429988861, 0.042670000345, 0.449429988861, 0.059603333473, - 0.449429988861, 0.076536670327, 0.449429988861, 0.093469999731, - 0.449429988861, 0.110403336585, 0.449429988861, 0.127336665988, - 0.449429988861, 0.144270002842, 0.449470460415, 0.161473110318, - 0.449488043785, 0.178504243493, 0.449520945549, 0.195615619421, - 0.449548959732, 0.212677508593, 0.449605435133, 0.229872331023, - 0.449580013752, 0.246619999409, 0.449580013752, 0.263503342867, - 0.449663192034, 0.280747175217, 0.449695199728, 0.297730803490, - 0.449729591608, 0.314701884985, 0.449700742960, 0.331439197063, - 0.449738889933, 0.348396658897, 0.449762344360, 0.365289598703, - 0.449642866850, 0.381833344698, 0.449761092663, 0.398932158947, - 0.449804753065, 0.415827900171, 0.449817210436, 0.432652950287, - 0.449844151735, 0.449484139681, 0.449730366468, 0.466203570366, - 0.449716180563, 0.483032077551, 0.449710994959, 0.499870002270, - 0.449716180563, 0.516707956791, 0.449730366468, 0.533536434174, - 0.449844151735, 0.550255835056, 0.449817210436, 0.567087054253, - 0.449804753065, 0.583912074566, 0.449761092663, 0.600807845592, - 0.449642866850, 0.617906630039, 0.449762344360, 0.634450376034, - 0.449738889933, 0.651343345642, 0.449700742960, 0.668300807476, - 0.449729591608, 0.685038089752, 0.449695199728, 0.702009201050, - 0.449663192034, 0.718992829323, 0.449580013752, 0.736236691475, - 0.449580013752, 0.753120005131, 0.449605435133, 0.769867658615, - 0.449548959732, 0.787062466145, 0.449520945549, 0.804124355316, - 0.449488043785, 0.821235775948, 0.449470460415, 0.838266909122, - 0.449429988861, 0.855470001698, 0.449429988861, 0.872403323650, - 0.449429988861, 0.889336645603, 0.449429988861, 0.906270027161, - 0.449429988861, 0.923203349113, 0.449429988861, 0.940136671066, - 0.449429988861, 0.957069993019, 0.449429988861, 0.974003314972, - 0.449429988861, 0.990936636925, 0.449429988861, 1.007869958878, - 0.466363340616, -0.008129999973, 0.466363340616, 0.008803333156, - 0.466363340616, 0.025736667216, 0.466363340616, 0.042670000345, - 0.466363340616, 0.059603333473, 0.466363340616, 0.076536670327, - 0.466363340616, 0.093469999731, 0.466363340616, 0.110403336585, - 0.466363340616, 0.127336665988, 0.466363340616, 0.144270002842, - 0.466391772032, 0.161487802863, 0.466403573751, 0.178518921137, - 0.466426551342, 0.195639088750, 0.466445416212, 0.212700948119, - 0.466484665871, 0.229907438159, 0.466463327408, 0.246619999409, - 0.466463327408, 0.263503342867, 0.466523706913, 0.280779153109, - 0.466544955969, 0.297759801149, 0.466540336609, 0.314576804638, - 0.466544985771, 0.331444948912, 0.466573685408, 0.348416596651, - 0.466521680355, 0.365036725998, 0.466496497393, 0.381802707911, - 0.466596782207, 0.398970365524, 0.466612339020, 0.415825873613, - 0.466634005308, 0.432677984238, 0.466563582420, 0.449370384216, - 0.466547012329, 0.466187000275, 0.466778755188, 0.483144372702, - 0.466802805662, 0.499870002270, 0.466778755188, 0.516595602036, - 0.466547012329, 0.533553004265, 0.466563582420, 0.550369620323, - 0.466634005308, 0.567062020302, 0.466612339020, 0.583914101124, - 0.466596782207, 0.600769639015, 0.466496497393, 0.617937266827, - 0.466521680355, 0.634703278542, 0.466573685408, 0.651323378086, - 0.466544985771, 0.668295085430, 0.466540336609, 0.685163199902, - 0.466544955969, 0.701980233192, 0.466523706913, 0.718960821629, - 0.466463327408, 0.736236691475, 0.466463327408, 0.753120005131, - 0.466484665871, 0.769832551479, 0.466445416212, 0.787039041519, - 0.466426551342, 0.804100930691, 0.466403573751, 0.821221053600, - 0.466391772032, 0.838252186775, 0.466363340616, 0.855470001698, - 0.466363340616, 0.872403323650, 0.466363340616, 0.889336645603, - 0.466363340616, 0.906270027161, 0.466363340616, 0.923203349113, - 0.466363340616, 0.940136671066, 0.466363340616, 0.957069993019, - 0.466363340616, 0.974003314972, 0.466363340616, 0.990936636925, - 0.466363340616, 1.007869958878, 0.483296662569, -0.008129999973, - 0.483296662569, 0.008803333156, 0.483296662569, 0.025736667216, - 0.483296662569, 0.042670000345, 0.483296662569, 0.059603333473, - 0.483296662569, 0.076536670327, 0.483296662569, 0.093469999731, - 0.483296662569, 0.110403336585, 0.483296662569, 0.127336665988, - 0.483305424452, 0.144453704357, 0.483311325312, 0.161496669054, - 0.483317255974, 0.178527772427, 0.483329057693, 0.195653259754, - 0.483338534832, 0.212715119123, 0.483358681202, 0.229928672314, - 0.483346670866, 0.246619999409, 0.483365356922, 0.263764888048, - 0.483378350735, 0.280798554420, 0.483388960361, 0.297777414322, - 0.483385473490, 0.314580321312, 0.483387857676, 0.331448435783, - 0.483403205872, 0.348428875208, 0.483373433352, 0.365017533302, - 0.483360528946, 0.381783634424, 0.483417391777, 0.398994415998, - 0.483424574137, 0.415842831135, 0.483402311802, 0.432559251785, - 0.483392059803, 0.449356198311, 0.483504384756, 0.466418743134, - 0.483546257019, 0.483186274767, 0.483474999666, 0.499870002270, - 0.483546257019, 0.516553759575, 0.483504384756, 0.533321261406, - 0.483392059803, 0.550383806229, 0.483402311802, 0.567180752754, - 0.483424574137, 0.583897173405, 0.483417391777, 0.600745558739, - 0.483360528946, 0.617956340313, 0.483373433352, 0.634722471237, - 0.483403205872, 0.651311159134, 0.483387857676, 0.668291568756, - 0.483385473490, 0.685159683228, 0.483388960361, 0.701962590218, - 0.483378350735, 0.718941450119, 0.483365356922, 0.735975086689, - 0.483346670866, 0.753120005131, 0.483358681202, 0.769811332226, - 0.483338534832, 0.787024855614, 0.483329057693, 0.804086744785, - 0.483317255974, 0.821212232113, 0.483311325312, 0.838243305683, - 0.483305424452, 0.855286300182, 0.483296662569, 0.872403323650, - 0.483296662569, 0.889336645603, 0.483296662569, 0.906270027161, - 0.483296662569, 0.923203349113, 0.483296662569, 0.940136671066, - 0.483296662569, 0.957069993019, 0.483296662569, 0.974003314972, - 0.483296662569, 0.990936636925, 0.483296662569, 1.007869958878, - 0.500230014324, -0.008129999973, 0.500230014324, 0.008803333156, - 0.500230014324, 0.025736667216, 0.500230014324, 0.042670000345, - 0.500230014324, 0.059603333473, 0.500230014324, 0.076536670327, - 0.500230014324, 0.093469999731, 0.500230014324, 0.110403336585, - 0.500230014324, 0.127336665988, 0.500230014324, 0.144456669688, - 0.500230014324, 0.161499634385, 0.500230014324, 0.178530737758, - 0.500230014324, 0.195657998323, 0.500230014324, 0.212719857693, - 0.500230014324, 0.229935780168, 0.500230014324, 0.246619999409, - 0.500230014324, 0.263771414757, 0.500230014324, 0.280805081129, - 0.500230014324, 0.297783344984, 0.500230014324, 0.314581513405, - 0.500230014324, 0.331449627876, 0.500230014324, 0.348432987928, - 0.500230014324, 0.365011036396, 0.500230014324, 0.382075309753, - 0.500230014324, 0.399002671242, 0.500230014324, 0.415848702192, - 0.500230014324, 0.432554006577, 0.500230014324, 0.449351012707, - 0.500230014324, 0.466442823410, 0.500230014324, 0.483114987612, - 0.500230014324, 0.499870002270, 0.500230014324, 0.516624987125, - 0.500230014324, 0.533297181129, 0.500230014324, 0.550388991833, - 0.500230014324, 0.567185997963, 0.500230014324, 0.583891272545, - 0.500230014324, 0.600737333298, 0.500230014324, 0.617664694786, - 0.500230014324, 0.634728968143, 0.500230014324, 0.651306986809, - 0.500230014324, 0.668290376663, 0.500230014324, 0.685158491135, - 0.500230014324, 0.701956689358, 0.500230014324, 0.718934953213, - 0.500230014324, 0.735968589783, 0.500230014324, 0.753120005131, - 0.500230014324, 0.769804239273, 0.500230014324, 0.787020146847, - 0.500230014324, 0.804081976414, 0.500230014324, 0.821209251881, - 0.500230014324, 0.838240385056, 0.500230014324, 0.855283319950, - 0.500230014324, 0.872403323650, 0.500230014324, 0.889336645603, - 0.500230014324, 0.906270027161, 0.500230014324, 0.923203349113, - 0.500230014324, 0.940136671066, 0.500230014324, 0.957069993019, - 0.500230014324, 0.974003314972, 0.500230014324, 0.990936636925, - 0.500230014324, 1.007869958878, 0.517163336277, -0.008129999973, - 0.517163336277, 0.008803333156, 0.517163336277, 0.025736667216, - 0.517163336277, 0.042670000345, 0.517163336277, 0.059603333473, - 0.517163336277, 0.076536670327, 0.517163336277, 0.093469999731, - 0.517163336277, 0.110403336585, 0.517163336277, 0.127336665988, - 0.517154574394, 0.144453704357, 0.517148673534, 0.161496669054, - 0.517142772675, 0.178527772427, 0.517130911350, 0.195653259754, - 0.517121434212, 0.212715119123, 0.517101347446, 0.229928672314, - 0.517113327980, 0.246619999409, 0.517094671726, 0.263764888048, - 0.517081677914, 0.280798554420, 0.517071068287, 0.297777414322, - 0.517074525356, 0.314580321312, 0.517072141171, 0.331448435783, - 0.517056763172, 0.348428875208, 0.517086565495, 0.365017533302, - 0.517099499702, 0.381783634424, 0.517042577267, 0.398994415998, - 0.517035424709, 0.415842831135, 0.517057657242, 0.432559251785, - 0.517067909241, 0.449356198311, 0.516955614090, 0.466418743134, - 0.516913712025, 0.483186274767, 0.516984999180, 0.499870002270, - 0.516913712025, 0.516553759575, 0.516955614090, 0.533321261406, - 0.517067909241, 0.550383806229, 0.517057657242, 0.567180752754, - 0.517035424709, 0.583897173405, 0.517042577267, 0.600745558739, - 0.517099499702, 0.617956340313, 0.517086565495, 0.634722471237, - 0.517056763172, 0.651311159134, 0.517072141171, 0.668291568756, - 0.517074525356, 0.685159683228, 0.517071068287, 0.701962590218, - 0.517081677914, 0.718941450119, 0.517094671726, 0.735975086689, - 0.517113327980, 0.753120005131, 0.517101347446, 0.769811332226, - 0.517121434212, 0.787024855614, 0.517130911350, 0.804086744785, - 0.517142772675, 0.821212232113, 0.517148673534, 0.838243305683, - 0.517154574394, 0.855286300182, 0.517163336277, 0.872403323650, - 0.517163336277, 0.889336645603, 0.517163336277, 0.906270027161, - 0.517163336277, 0.923203349113, 0.517163336277, 0.940136671066, - 0.517163336277, 0.957069993019, 0.517163336277, 0.974003314972, - 0.517163336277, 0.990936636925, 0.517163336277, 1.007869958878, - 0.534096658230, -0.008129999973, 0.534096658230, 0.008803333156, - 0.534096658230, 0.025736667216, 0.534096658230, 0.042670000345, - 0.534096658230, 0.059603333473, 0.534096658230, 0.076536670327, - 0.534096658230, 0.093469999731, 0.534096658230, 0.110403336585, - 0.534096658230, 0.127336665988, 0.534096658230, 0.144270002842, - 0.534068226814, 0.161487802863, 0.534056425095, 0.178518921137, - 0.534033417702, 0.195639088750, 0.534014582634, 0.212700948119, - 0.533975303173, 0.229907438159, 0.533996641636, 0.246619999409, - 0.533996641636, 0.263503342867, 0.533936262131, 0.280779153109, - 0.533915042877, 0.297759801149, 0.533919692039, 0.314576804638, - 0.533914983273, 0.331444948912, 0.533886313438, 0.348416596651, - 0.533938348293, 0.365036725998, 0.533963501453, 0.381802707911, - 0.533863186836, 0.398970365524, 0.533847630024, 0.415825873613, - 0.533825993538, 0.432677984238, 0.533896386623, 0.449370384216, - 0.533913016319, 0.466187000275, 0.533681273460, 0.483144372702, - 0.533657193184, 0.499870002270, 0.533681273460, 0.516595602036, - 0.533913016319, 0.533553004265, 0.533896386623, 0.550369620323, - 0.533825993538, 0.567062020302, 0.533847630024, 0.583914101124, - 0.533863186836, 0.600769639015, 0.533963501453, 0.617937266827, - 0.533938348293, 0.634703278542, 0.533886313438, 0.651323378086, - 0.533914983273, 0.668295085430, 0.533919692039, 0.685163199902, - 0.533915042877, 0.701980233192, 0.533936262131, 0.718960821629, - 0.533996641636, 0.736236691475, 0.533996641636, 0.753120005131, - 0.533975303173, 0.769832551479, 0.534014582634, 0.787039041519, - 0.534033417702, 0.804100930691, 0.534056425095, 0.821221053600, - 0.534068226814, 0.838252186775, 0.534096658230, 0.855470001698, - 0.534096658230, 0.872403323650, 0.534096658230, 0.889336645603, - 0.534096658230, 0.906270027161, 0.534096658230, 0.923203349113, - 0.534096658230, 0.940136671066, 0.534096658230, 0.957069993019, - 0.534096658230, 0.974003314972, 0.534096658230, 0.990936636925, - 0.534096658230, 1.007869958878, 0.551029980183, -0.008129999973, - 0.551029980183, 0.008803333156, 0.551029980183, 0.025736667216, - 0.551029980183, 0.042670000345, 0.551029980183, 0.059603333473, - 0.551029980183, 0.076536670327, 0.551029980183, 0.093469999731, - 0.551029980183, 0.110403336585, 0.551029980183, 0.127336665988, - 0.551029980183, 0.144270002842, 0.550989508629, 0.161473110318, - 0.550971984863, 0.178504243493, 0.550939083099, 0.195615619421, - 0.550911009312, 0.212677508593, 0.550854563713, 0.229872331023, - 0.550880014896, 0.246619999409, 0.550880014896, 0.263503342867, - 0.550796806812, 0.280747175217, 0.550764799118, 0.297730803490, - 0.550730407238, 0.314701884985, 0.550759255886, 0.331439197063, - 0.550721108913, 0.348396658897, 0.550697624683, 0.365289598703, - 0.550817131996, 0.381833344698, 0.550698935986, 0.398932158947, - 0.550655245781, 0.415827900171, 0.550642788410, 0.432652950287, - 0.550615847111, 0.449484139681, 0.550729632378, 0.466203570366, - 0.550743818283, 0.483032077551, 0.550749003887, 0.499870002270, - 0.550743818283, 0.516707956791, 0.550729632378, 0.533536434174, - 0.550615847111, 0.550255835056, 0.550642788410, 0.567087054253, - 0.550655245781, 0.583912074566, 0.550698935986, 0.600807845592, - 0.550817131996, 0.617906630039, 0.550697624683, 0.634450376034, - 0.550721108913, 0.651343345642, 0.550759255886, 0.668300807476, - 0.550730407238, 0.685038089752, 0.550764799118, 0.702009201050, - 0.550796806812, 0.718992829323, 0.550880014896, 0.736236691475, - 0.550880014896, 0.753120005131, 0.550854563713, 0.769867658615, - 0.550911009312, 0.787062466145, 0.550939083099, 0.804124355316, - 0.550971984863, 0.821235775948, 0.550989508629, 0.838266909122, - 0.551029980183, 0.855470001698, 0.551029980183, 0.872403323650, - 0.551029980183, 0.889336645603, 0.551029980183, 0.906270027161, - 0.551029980183, 0.923203349113, 0.551029980183, 0.940136671066, - 0.551029980183, 0.957069993019, 0.551029980183, 0.974003314972, - 0.551029980183, 0.990936636925, 0.551029980183, 1.007869958878, - 0.567963361740, -0.008129999973, 0.567963361740, 0.008803333156, - 0.567963361740, 0.025736667216, 0.567963361740, 0.042670000345, - 0.567963361740, 0.059603333473, 0.567963361740, 0.076536670327, - 0.567963361740, 0.093469999731, 0.567963361740, 0.110403336585, - 0.567963361740, 0.127336665988, 0.567963361740, 0.144270002842, - 0.567913472652, 0.161452680826, 0.567890226841, 0.178483843803, - 0.567867100239, 0.195503160357, 0.567812323570, 0.212645024061, - 0.567741572857, 0.229823723435, 0.567686498165, 0.246908172965, - 0.567763328552, 0.263503342867, 0.567665934563, 0.280703127384, - 0.567623019218, 0.297691017389, 0.567578196526, 0.314662396908, - 0.567605435848, 0.331431359053, 0.567596733570, 0.348294883966, - 0.567533493042, 0.365263044834, 0.567656219006, 0.381874084473, - 0.567700624466, 0.398664057255, 0.567501664162, 0.415780454874, - 0.567452132702, 0.432647883892, 0.567447066307, 0.449457228184, - 0.567422032356, 0.466273993254, 0.567540764809, 0.483042299747, - 0.567546010017, 0.499870002270, 0.567540764809, 0.516697704792, - 0.567422032356, 0.533465981483, 0.567447066307, 0.550282776356, - 0.567452132702, 0.567092120647, 0.567501664162, 0.583959579468, - 0.567700624466, 0.601075947285, 0.567656219006, 0.617865920067, - 0.567533493042, 0.634476959705, 0.567596733570, 0.651445090771, - 0.567605435848, 0.668308615685, 0.567578196526, 0.685077607632, - 0.567623019218, 0.702048957348, 0.567665934563, 0.719036877155, - 0.567763328552, 0.736236691475, 0.567686498165, 0.752831816673, - 0.567741572857, 0.769916296005, 0.567812323570, 0.787094950676, - 0.567867100239, 0.804236829281, 0.567890226841, 0.821256160736, - 0.567913472652, 0.838287293911, 0.567963361740, 0.855470001698, - 0.567963361740, 0.872403323650, 0.567963361740, 0.889336645603, - 0.567963361740, 0.906270027161, 0.567963361740, 0.923203349113, - 0.567963361740, 0.940136671066, 0.567963361740, 0.957069993019, - 0.567963361740, 0.974003314972, 0.567963361740, 0.990936636925, - 0.567963361740, 1.007869958878, 0.584896683693, -0.008129999973, - 0.584896683693, 0.008803333156, 0.584896683693, 0.025736667216, - 0.584896683693, 0.042670000345, 0.584896683693, 0.059603333473, - 0.584896683693, 0.076536670327, 0.584896683693, 0.093469999731, - 0.584896683693, 0.110403336585, 0.584896683693, 0.127336665988, - 0.584896683693, 0.144270002842, 0.584840834141, 0.161426678300, - 0.584812104702, 0.178457900882, 0.584783554077, 0.195477306843, - 0.584720075130, 0.212603792548, 0.584638714790, 0.229762136936, - 0.584571003914, 0.246846899390, 0.584646642208, 0.263503342867, - 0.584546267986, 0.280647724867, 0.584485769272, 0.297656208277, - 0.584437727928, 0.314613074064, 0.584454178810, 0.331421643496, - 0.584443688393, 0.348285347223, 0.584379673004, 0.365230530500, - 0.584345161915, 0.382108747959, 0.584529221058, 0.398710936308, - 0.584577381611, 0.415522605181, 0.584319531918, 0.432598352432, - 0.584272086620, 0.449444741011, 0.584274113178, 0.466252356768, - 0.584257185459, 0.483064562082, 0.584251284599, 0.499870002270, - 0.584257185459, 0.516675412655, 0.584274113178, 0.533487677574, - 0.584272086620, 0.550295233727, 0.584319531918, 0.567141652107, - 0.584577381611, 0.584217429161, 0.584529221058, 0.601029038429, - 0.584345161915, 0.617631256580, 0.584379673004, 0.634509444237, - 0.584443688393, 0.651454627514, 0.584454178810, 0.668318331242, - 0.584437727928, 0.685126960278, 0.584485769272, 0.702083766460, - 0.584546267986, 0.719092249870, 0.584646642208, 0.736236691475, - 0.584571003914, 0.752893090248, 0.584638714790, 0.769977867603, - 0.584720075130, 0.787136197090, 0.584783554077, 0.804262697697, - 0.584812104702, 0.821282088757, 0.584840834141, 0.838313341141, - 0.584896683693, 0.855470001698, 0.584896683693, 0.872403323650, - 0.584896683693, 0.889336645603, 0.584896683693, 0.906270027161, - 0.584896683693, 0.923203349113, 0.584896683693, 0.940136671066, - 0.584896683693, 0.957069993019, 0.584896683693, 0.974003314972, - 0.584896683693, 0.990936636925, 0.584896683693, 1.007869958878, - 0.601830005646, -0.008129999973, 0.601830005646, 0.008803333156, - 0.601830005646, 0.025736667216, 0.601830005646, 0.042670000345, - 0.601830005646, 0.059603333473, 0.601830005646, 0.076536670327, - 0.601830005646, 0.093469999731, 0.601830005646, 0.110403336585, - 0.601830005646, 0.127336665988, 0.601830005646, 0.144270002842, - 0.601772427559, 0.161395266652, 0.601738452911, 0.178426608443, - 0.601704597473, 0.195446148515, 0.601635575294, 0.212554186583, - 0.601582109928, 0.229597687721, 0.601468622684, 0.246773496270, - 0.601530015469, 0.263503342867, 0.601530015469, 0.280386656523, - 0.601369440556, 0.297591090202, 0.601311028004, 0.314554810524, - 0.601249277592, 0.331504523754, 0.601293861866, 0.348274230957, - 0.601282238960, 0.365133702755, 0.601198911667, 0.382072925568, - 0.601162374020, 0.398937612772, 0.601389050484, 0.415570765734, - 0.601435959339, 0.432399392128, 0.601167857647, 0.449401080608, - 0.601129651070, 0.466236799955, 0.601105570793, 0.483057409525, - 0.601097345352, 0.499870002270, 0.601105570793, 0.516682624817, - 0.601129651070, 0.533503234386, 0.601167857647, 0.550338923931, - 0.601435959339, 0.567340612411, 0.601389050484, 0.584169209003, - 0.601162374020, 0.600802361965, 0.601198911667, 0.617667078972, - 0.601282238960, 0.634606301785, 0.601293861866, 0.651465773582, - 0.601249277592, 0.668235480785, 0.601311028004, 0.685185194016, - 0.601369440556, 0.702148914337, 0.601530015469, 0.719353318214, - 0.601530015469, 0.736236691475, 0.601468622684, 0.752966523170, - 0.601582109928, 0.770142316818, 0.601635575294, 0.787185788155, - 0.601704597473, 0.804293870926, 0.601738452911, 0.821313381195, - 0.601772427559, 0.838344752789, 0.601830005646, 0.855470001698, - 0.601830005646, 0.872403323650, 0.601830005646, 0.889336645603, - 0.601830005646, 0.906270027161, 0.601830005646, 0.923203349113, - 0.601830005646, 0.940136671066, 0.601830005646, 0.957069993019, - 0.601830005646, 0.974003314972, 0.601830005646, 0.990936636925, - 0.601830005646, 1.007869958878, 0.618763327599, -0.008129999973, - 0.618763327599, 0.008803333156, 0.618763327599, 0.025736667216, - 0.618763327599, 0.042670000345, 0.618763327599, 0.059603333473, - 0.618763327599, 0.076536670327, 0.618763327599, 0.093469999731, - 0.618763327599, 0.110403336585, 0.618763327599, 0.127336665988, - 0.618763327599, 0.144270002842, 0.618763327599, 0.161203339696, - 0.618669927120, 0.178390175104, 0.618631124496, 0.195409923792, - 0.618592619896, 0.212417900562, 0.618499100208, 0.229540601373, - 0.618381261826, 0.246688708663, 0.618291616440, 0.263746738434, - 0.618413329124, 0.280386656523, 0.618269503117, 0.297516614199, - 0.618191540241, 0.314501851797, 0.618131041527, 0.331439971924, - 0.618064284325, 0.348368823528, 0.618134737015, 0.365121752024, - 0.618122577667, 0.381977409124, 0.618027091026, 0.398901075125, - 0.617991209030, 0.415754824877, 0.618225932121, 0.432443767786, - 0.618266642094, 0.449282854795, 0.618297278881, 0.466136485338, - 0.618316352367, 0.483000516891, 0.618024706841, 0.499870002270, - 0.618316352367, 0.516739487648, 0.618297278881, 0.533603489399, - 0.618266642094, 0.550457119942, 0.618225932121, 0.567296206951, - 0.617991209030, 0.583985149860, 0.618027091026, 0.600838899612, - 0.618122577667, 0.617762565613, 0.618134737015, 0.634618222713, - 0.618064284325, 0.651371181011, 0.618131041527, 0.668300032616, - 0.618191540241, 0.685238122940, 0.618269503117, 0.702223420143, - 0.618413329124, 0.719353318214, 0.618291616440, 0.735993266106, - 0.618381261826, 0.753051280975, 0.618499100208, 0.770199418068, - 0.618592619896, 0.787322103977, 0.618631124496, 0.804330050945, - 0.618669927120, 0.821349799633, 0.618763327599, 0.838536679745, - 0.618763327599, 0.855470001698, 0.618763327599, 0.872403323650, - 0.618763327599, 0.889336645603, 0.618763327599, 0.906270027161, - 0.618763327599, 0.923203349113, 0.618763327599, 0.940136671066, - 0.618763327599, 0.957069993019, 0.618763327599, 0.974003314972, - 0.618763327599, 0.990936636925, 0.618763327599, 1.007869958878, - 0.635696649551, -0.008129999973, 0.635696649551, 0.008803333156, - 0.635696649551, 0.025736667216, 0.635696649551, 0.042670000345, - 0.635696649551, 0.059603333473, 0.635696649551, 0.076536670327, - 0.635696649551, 0.093469999731, 0.635696649551, 0.110403336585, - 0.635696649551, 0.127336665988, 0.635696649551, 0.144270002842, - 0.635696649551, 0.161203339696, 0.635607302189, 0.178348839283, - 0.635563790798, 0.195368915796, 0.635520696640, 0.212377250195, - 0.635426878929, 0.229476243258, 0.635359466076, 0.246502220631, - 0.635211288929, 0.263652771711, 0.635296642780, 0.280386656523, - 0.635296642780, 0.297269999981, 0.635101914406, 0.314421117306, - 0.635019004345, 0.331383764744, 0.634958386421, 0.348300576210, - 0.634889364243, 0.365210622549, 0.634978234768, 0.381965279579, - 0.634966313839, 0.398817777634, 0.634869456291, 0.415720343590, - 0.634836971760, 0.432566523552, 0.634810388088, 0.449402362108, - 0.635063290596, 0.466161668301, 0.635082483292, 0.483013451099, - 0.635088980198, 0.499870002270, 0.635082483292, 0.516726553440, - 0.635063290596, 0.533578336239, 0.634810388088, 0.550337672234, - 0.634836971760, 0.567173480988, 0.634869456291, 0.584019660950, - 0.634966313839, 0.600922226906, 0.634978234768, 0.617774724960, - 0.634889364243, 0.634529352188, 0.634958386421, 0.651439428329, - 0.635019004345, 0.668356239796, 0.635101914406, 0.685318887234, - 0.635296642780, 0.702470004559, 0.635296642780, 0.719353318214, - 0.635211288929, 0.736087262630, 0.635359466076, 0.753237783909, - 0.635426878929, 0.770263731480, 0.635520696640, 0.787362754345, - 0.635563790798, 0.804371118546, 0.635607302189, 0.821391165257, - 0.635696649551, 0.838536679745, 0.635696649551, 0.855470001698, - 0.635696649551, 0.872403323650, 0.635696649551, 0.889336645603, - 0.635696649551, 0.906270027161, 0.635696649551, 0.923203349113, - 0.635696649551, 0.940136671066, 0.635696649551, 0.957069993019, - 0.635696649551, 0.974003314972, 0.635696649551, 0.990936636925, - 0.635696649551, 1.007869958878, 0.652629971504, -0.008129999973, - 0.652629971504, 0.008803333156, 0.652629971504, 0.025736667216, - 0.652629971504, 0.042670000345, 0.652629971504, 0.059603333473, - 0.652629971504, 0.076536670327, 0.652629971504, 0.093469999731, - 0.652629971504, 0.110403336585, 0.652629971504, 0.127336665988, - 0.652629971504, 0.144270002842, 0.652629971504, 0.161203339696, - 0.652551293373, 0.178302869201, 0.652503311634, 0.195323377848, - 0.652455866337, 0.212332218885, 0.652409076691, 0.229329437017, - 0.652292728424, 0.246432125568, 0.652150332928, 0.263549476862, - 0.652043879032, 0.280583322048, 0.652180016041, 0.297269999981, - 0.652180016041, 0.314153343439, 0.651943564415, 0.331299364567, - 0.651858389378, 0.348241597414, 0.651799440384, 0.365141600370, - 0.651731193066, 0.382035732269, 0.651825726032, 0.398806154728, - 0.651814639568, 0.415656298399, 0.651805102825, 0.432503283024, - 0.651703357697, 0.449378877878, 0.651683390141, 0.466213703156, - 0.651671111584, 0.483043193817, 0.651666998863, 0.499870002270, - 0.651671111584, 0.516696810722, 0.651683390141, 0.533526301384, - 0.651703357697, 0.550361096859, 0.651805102825, 0.567236721516, - 0.651814639568, 0.584083676338, 0.651825726032, 0.600933849812, - 0.651731193066, 0.617704272270, 0.651799440384, 0.634598374367, - 0.651858389378, 0.651498436928, 0.651943564415, 0.668440639973, - 0.652180016041, 0.685586690903, 0.652180016041, 0.702470004559, - 0.652043879032, 0.719156682491, 0.652150332928, 0.736190557480, - 0.652292728424, 0.753307878971, 0.652409076691, 0.770410597324, - 0.652455866337, 0.787407815456, 0.652503311634, 0.804416596889, - 0.652551293373, 0.821437120438, 0.652629971504, 0.838536679745, - 0.652629971504, 0.855470001698, 0.652629971504, 0.872403323650, - 0.652629971504, 0.889336645603, 0.652629971504, 0.906270027161, - 0.652629971504, 0.923203349113, 0.652629971504, 0.940136671066, - 0.652629971504, 0.957069993019, 0.652629971504, 0.974003314972, - 0.652629971504, 0.990936636925, 0.652629971504, 1.007869958878, - 0.669563353062, -0.008129999973, 0.669563353062, 0.008803333156, - 0.669563353062, 0.025736667216, 0.669563353062, 0.042670000345, - 0.669563353062, 0.059603333473, 0.669563353062, 0.076536670327, - 0.669563353062, 0.093469999731, 0.669563353062, 0.110403336585, - 0.669563353062, 0.127336665988, 0.669563353062, 0.144270002842, - 0.669563353062, 0.161203339696, 0.669563353062, 0.178136661649, - 0.669450223446, 0.195273593068, 0.669398784637, 0.212283074856, - 0.669348120689, 0.229281038046, 0.669239282608, 0.246356055140, - 0.669161260128, 0.263366252184, 0.668995976448, 0.280474275351, - 0.668884932995, 0.297484099865, 0.669063329697, 0.314153343439, - 0.669063329697, 0.331036657095, 0.668800592422, 0.348156452179, - 0.668716192245, 0.365081012249, 0.668660044670, 0.381968975067, - 0.668595492840, 0.398850709200, 0.668678343296, 0.415645837784, - 0.668668627739, 0.432494550943, 0.668660819530, 0.449340760708, - 0.668655037880, 0.466184973717, 0.668651580811, 0.483027845621, - 0.668650388718, 0.499870002270, 0.668651580811, 0.516712129116, - 0.668655037880, 0.533555030823, 0.668660819530, 0.550399243832, - 0.668668627739, 0.567245423794, 0.668678343296, 0.584094166756, - 0.668595492840, 0.600889265537, 0.668660044670, 0.617771029472, - 0.668716192245, 0.634658992290, 0.668800592422, 0.651583552361, - 0.669063329697, 0.668703317642, 0.669063329697, 0.685586690903, - 0.668884932995, 0.702255904675, 0.668995976448, 0.719265758991, - 0.669161260128, 0.736373782158, 0.669239282608, 0.753383934498, - 0.669348120689, 0.770458936691, 0.669398784637, 0.787456929684, - 0.669450223446, 0.804466426373, 0.669563353062, 0.821603357792, - 0.669563353062, 0.838536679745, 0.669563353062, 0.855470001698, - 0.669563353062, 0.872403323650, 0.669563353062, 0.889336645603, - 0.669563353062, 0.906270027161, 0.669563353062, 0.923203349113, - 0.669563353062, 0.940136671066, 0.669563353062, 0.957069993019, - 0.669563353062, 0.974003314972, 0.669563353062, 0.990936636925, - 0.669563353062, 1.007869958878, 0.686496675014, -0.008129999973, - 0.686496675014, 0.008803333156, 0.686496675014, 0.025736667216, - 0.686496675014, 0.042670000345, 0.686496675014, 0.059603333473, - 0.686496675014, 0.076536670327, 0.686496675014, 0.093469999731, - 0.686496675014, 0.110403336585, 0.686496675014, 0.127336665988, - 0.686496675014, 0.144270002842, 0.686496675014, 0.161203339696, - 0.686496675014, 0.178136661649, 0.686496675014, 0.195069998503, - 0.686349928379, 0.212230160832, 0.686295688152, 0.229229032993, - 0.686242520809, 0.246216565371, 0.686116933823, 0.263286620378, - 0.686036109924, 0.280280977488, 0.685853421688, 0.297371745110, - 0.685740351677, 0.314359635115, 0.685946643353, 0.331036657095, - 0.685946643353, 0.347920000553, 0.685678899288, 0.364998072386, - 0.685598134995, 0.381908446550, 0.685545206070, 0.398788988590, - 0.685486912727, 0.415662288666, 0.685437619686, 0.432521790266, - 0.685398101807, 0.449369609356, 0.685523211956, 0.466180324554, - 0.685519635677, 0.483025491238, 0.685518503189, 0.499870002270, - 0.685519635677, 0.516714513302, 0.685523211956, 0.533559679985, - 0.685398101807, 0.550370395184, 0.685437619686, 0.567218244076, - 0.685486912727, 0.584077715874, 0.685545206070, 0.600951015949, - 0.685598134995, 0.617831528187, 0.685678899288, 0.634741902351, - 0.685946643353, 0.651820003986, 0.685946643353, 0.668703317642, - 0.685740351677, 0.685380399227, 0.685853421688, 0.702368259430, - 0.686036109924, 0.719459056854, 0.686116933823, 0.736453354359, - 0.686242520809, 0.753523409367, 0.686295688152, 0.770510971546, - 0.686349928379, 0.787509858608, 0.686496675014, 0.804669976234, - 0.686496675014, 0.821603357792, 0.686496675014, 0.838536679745, - 0.686496675014, 0.855470001698, 0.686496675014, 0.872403323650, - 0.686496675014, 0.889336645603, 0.686496675014, 0.906270027161, - 0.686496675014, 0.923203349113, 0.686496675014, 0.940136671066, - 0.686496675014, 0.957069993019, 0.686496675014, 0.974003314972, - 0.686496675014, 0.990936636925, 0.686496675014, 1.007869958878, - 0.703429996967, -0.008129999973, 0.703429996967, 0.008803333156, - 0.703429996967, 0.025736667216, 0.703429996967, 0.042670000345, - 0.703429996967, 0.059603333473, 0.703429996967, 0.076536670327, - 0.703429996967, 0.093469999731, 0.703429996967, 0.110403336585, - 0.703429996967, 0.127336665988, 0.703429996967, 0.144270002842, - 0.703429996967, 0.161203339696, 0.703429996967, 0.178136661649, - 0.703429996967, 0.195069998503, 0.703309714794, 0.212173715234, - 0.703252196312, 0.229173704982, 0.703195989132, 0.246162503958, - 0.703141212463, 0.263140231371, 0.703002929688, 0.280199319124, - 0.702920854092, 0.297179132700, 0.702728271484, 0.314246594906, - 0.702615916729, 0.331215083599, 0.702830016613, 0.347920000553, - 0.702830016613, 0.364803344011, 0.702583372593, 0.381830513477, - 0.702508926392, 0.398730546236, 0.702443778515, 0.415614247322, - 0.702408969402, 0.432476997375, 0.702369213104, 0.449335187674, - 0.702340185642, 0.466184973717, 0.702322602272, 0.483028948307, - 0.702316641808, 0.499870002270, 0.702322602272, 0.516711056232, - 0.702340185642, 0.533555030823, 0.702369213104, 0.550404787064, - 0.702408969402, 0.567263007164, 0.702443778515, 0.584125757217, - 0.702508926392, 0.601009488106, 0.702583372593, 0.617909491062, - 0.702830016613, 0.634936690331, 0.702830016613, 0.651820003986, - 0.702615916729, 0.668524920940, 0.702728271484, 0.685493409634, - 0.702920854092, 0.702560901642, 0.703002929688, 0.719540655613, - 0.703141212463, 0.736599743366, 0.703195989132, 0.753577470779, - 0.703252196312, 0.770566284657, 0.703309714794, 0.787566304207, - 0.703429996967, 0.804669976234, 0.703429996967, 0.821603357792, - 0.703429996967, 0.838536679745, 0.703429996967, 0.855470001698, - 0.703429996967, 0.872403323650, 0.703429996967, 0.889336645603, - 0.703429996967, 0.906270027161, 0.703429996967, 0.923203349113, - 0.703429996967, 0.940136671066, 0.703429996967, 0.957069993019, - 0.703429996967, 0.974003314972, 0.703429996967, 0.990936636925, - 0.703429996967, 1.007869958878, 0.720363318920, -0.008129999973, - 0.720363318920, 0.008803333156, 0.720363318920, 0.025736667216, - 0.720363318920, 0.042670000345, 0.720363318920, 0.059603333473, - 0.720363318920, 0.076536670327, 0.720363318920, 0.093469999731, - 0.720363318920, 0.110403336585, 0.720363318920, 0.127336665988, - 0.720363318920, 0.144270002842, 0.720363318920, 0.161203339696, - 0.720363318920, 0.178136661649, 0.720363318920, 0.195069998503, - 0.720363318920, 0.212003335357, 0.720218181610, 0.229115337133, - 0.720159113407, 0.246105611324, 0.720101773739, 0.263085007668, - 0.720046281815, 0.280053704977, 0.719900667667, 0.297097057104, - 0.719819009304, 0.314063906670, 0.719625711441, 0.331104040146, - 0.719516694546, 0.348056137562, 0.719713330269, 0.364803344011, - 0.719713330269, 0.381686657667, 0.719713330269, 0.398570001125, - 0.719452261925, 0.415553748608, 0.719396889210, 0.432434052229, - 0.719352841377, 0.449303179979, 0.719320833683, 0.466163724661, - 0.719301462173, 0.483018338680, 0.719294905663, 0.499870002270, - 0.719301462173, 0.516721665859, 0.719320833683, 0.533576309681, - 0.719352841377, 0.550436794758, 0.719396889210, 0.567305982113, - 0.719452261925, 0.584186255932, 0.719713330269, 0.601170003414, - 0.719713330269, 0.618053317070, 0.719713330269, 0.634936690331, - 0.719516694546, 0.651683866978, 0.719625711441, 0.668635964394, - 0.719819009304, 0.685676097870, 0.719900667667, 0.702642917633, - 0.720046281815, 0.719686329365, 0.720101773739, 0.736654996872, - 0.720159113407, 0.753634393215, 0.720218181610, 0.770624637604, - 0.720363318920, 0.787736654282, 0.720363318920, 0.804669976234, - 0.720363318920, 0.821603357792, 0.720363318920, 0.838536679745, - 0.720363318920, 0.855470001698, 0.720363318920, 0.872403323650, - 0.720363318920, 0.889336645603, 0.720363318920, 0.906270027161, - 0.720363318920, 0.923203349113, 0.720363318920, 0.940136671066, - 0.720363318920, 0.957069993019, 0.720363318920, 0.974003314972, - 0.720363318920, 0.990936636925, 0.720363318920, 1.007869958878, - 0.737296640873, -0.008129999973, 0.737296640873, 0.008803333156, - 0.737296640873, 0.025736667216, 0.737296640873, 0.042670000345, - 0.737296640873, 0.059603333473, 0.737296640873, 0.076536670327, - 0.737296640873, 0.093469999731, 0.737296640873, 0.110403336585, - 0.737296640873, 0.127336665988, 0.737296640873, 0.144270002842, - 0.737296640873, 0.161203339696, 0.737296640873, 0.178136661649, - 0.737296640873, 0.195069998503, 0.737296640873, 0.212003335357, - 0.737296640873, 0.228936672211, 0.737132251263, 0.246046155691, - 0.737072587013, 0.263027429581, 0.737015008926, 0.279998213053, - 0.736959755421, 0.296958774328, 0.736813366413, 0.313983052969, - 0.736733734608, 0.330938756466, 0.736550509930, 0.347949653864, - 0.736447215080, 0.364888727665, 0.736353278160, 0.381808370352, - 0.736596643925, 0.398570001125, 0.736596643925, 0.415453344584, - 0.736596643925, 0.432336658239, 0.736596643925, 0.449220001698, - 0.736596643925, 0.466103345156, 0.736335098743, 0.483005344868, - 0.736328601837, 0.499870002270, 0.736335098743, 0.516734659672, - 0.736596643925, 0.533636689186, 0.736596643925, 0.550520002842, - 0.736596643925, 0.567403316498, 0.736596643925, 0.584286689758, - 0.736596643925, 0.601170003414, 0.736353278160, 0.617931604385, - 0.736447215080, 0.634851276875, 0.736550509930, 0.651790320873, - 0.736733734608, 0.668801248074, 0.736813366413, 0.685756921768, - 0.736959755421, 0.702781200409, 0.737015008926, 0.719741761684, - 0.737072587013, 0.736712574959, 0.737132251263, 0.753693819046, - 0.737296640873, 0.770803332329, 0.737296640873, 0.787736654282, - 0.737296640873, 0.804669976234, 0.737296640873, 0.821603357792, - 0.737296640873, 0.838536679745, 0.737296640873, 0.855470001698, - 0.737296640873, 0.872403323650, 0.737296640873, 0.889336645603, - 0.737296640873, 0.906270027161, 0.737296640873, 0.923203349113, - 0.737296640873, 0.940136671066, 0.737296640873, 0.957069993019, - 0.737296640873, 0.974003314972, 0.737296640873, 0.990936636925, - 0.737296640873, 1.007869958878, 0.754230022430, -0.008129999973, - 0.754230022430, 0.008803333156, 0.754230022430, 0.025736667216, - 0.754230022430, 0.042670000345, 0.754230022430, 0.059603333473, - 0.754230022430, 0.076536670327, 0.754230022430, 0.093469999731, - 0.754230022430, 0.110403336585, 0.754230022430, 0.127336665988, - 0.754230022430, 0.144270002842, 0.754230022430, 0.161203339696, - 0.754230022430, 0.178136661649, 0.754230022430, 0.195069998503, - 0.754230022430, 0.212003335357, 0.754230022430, 0.228936672211, - 0.754230022430, 0.245869994164, 0.754053831100, 0.262967735529, - 0.753994405270, 0.279940843582, 0.753937482834, 0.296903997660, - 0.753883421421, 0.313857495785, 0.753743946552, 0.330860704184, - 0.753667891026, 0.347807288170, 0.753597795963, 0.364740520716, - 0.753411293030, 0.381718724966, 0.753326535225, 0.398631393909, - 0.753253102303, 0.415528953075, 0.753191828728, 0.432413518429, - 0.753480017185, 0.449220001698, 0.753480017185, 0.466103345156, - 0.753480017185, 0.482986658812, 0.753480017185, 0.499870002270, - 0.753480017185, 0.516753315926, 0.753480017185, 0.533636689186, - 0.753480017185, 0.550520002842, 0.753191828728, 0.567326486111, - 0.753253102303, 0.584211051464, 0.753326535225, 0.601108610630, - 0.753411293030, 0.618021249771, 0.753597795963, 0.634999454021, - 0.753667891026, 0.651932716370, 0.753743946552, 0.668879270554, - 0.753883421421, 0.685882508755, 0.753937482834, 0.702835977077, - 0.753994405270, 0.719799160957, 0.754053831100, 0.736772239208, - 0.754230022430, 0.753870010376, 0.754230022430, 0.770803332329, - 0.754230022430, 0.787736654282, 0.754230022430, 0.804669976234, - 0.754230022430, 0.821603357792, 0.754230022430, 0.838536679745, - 0.754230022430, 0.855470001698, 0.754230022430, 0.872403323650, - 0.754230022430, 0.889336645603, 0.754230022430, 0.906270027161, - 0.754230022430, 0.923203349113, 0.754230022430, 0.940136671066, - 0.754230022430, 0.957069993019, 0.754230022430, 0.974003314972, - 0.754230022430, 0.990936636925, 0.754230022430, 1.007869958878, - 0.771163344383, -0.008129999973, 0.771163344383, 0.008803333156, - 0.771163344383, 0.025736667216, 0.771163344383, 0.042670000345, - 0.771163344383, 0.059603333473, 0.771163344383, 0.076536670327, - 0.771163344383, 0.093469999731, 0.771163344383, 0.110403336585, - 0.771163344383, 0.127336665988, 0.771163344383, 0.144270002842, - 0.771163344383, 0.161203339696, 0.771163344383, 0.178136661649, - 0.771163344383, 0.195069998503, 0.771163344383, 0.212003335357, - 0.771163344383, 0.228936672211, 0.771163344383, 0.245869994164, - 0.771163344383, 0.262803345919, 0.770984649658, 0.279881834984, - 0.770926296711, 0.296847790480, 0.770870983601, 0.313804328442, - 0.770818948746, 0.330751895905, 0.770770549774, 0.347690939903, - 0.770623743534, 0.364673107862, 0.770559370518, 0.381600886583, - 0.770502328873, 0.398517876863, 0.770337879658, 0.415461301804, - 0.770276248455, 0.432358443737, 0.770227670670, 0.449245423079, - 0.770192563534, 0.466124683619, 0.770171344280, 0.482998669147, - 0.770164251328, 0.499870002270, 0.770171344280, 0.516741335392, - 0.770192563534, 0.533615291119, 0.770227670670, 0.550494551659, - 0.770276248455, 0.567381560802, 0.770337879658, 0.584278702736, - 0.770502328873, 0.601222097874, 0.770559370518, 0.618139088154, - 0.770623743534, 0.635066866875, 0.770770549774, 0.652049064636, - 0.770818948746, 0.668988108635, 0.770870983601, 0.685935676098, - 0.770926296711, 0.702892243862, 0.770984649658, 0.719858169556, - 0.771163344383, 0.736936688423, 0.771163344383, 0.753870010376, - 0.771163344383, 0.770803332329, 0.771163344383, 0.787736654282, - 0.771163344383, 0.804669976234, 0.771163344383, 0.821603357792, - 0.771163344383, 0.838536679745, 0.771163344383, 0.855470001698, - 0.771163344383, 0.872403323650, 0.771163344383, 0.889336645603, - 0.771163344383, 0.906270027161, 0.771163344383, 0.923203349113, - 0.771163344383, 0.940136671066, 0.771163344383, 0.957069993019, - 0.771163344383, 0.974003314972, 0.771163344383, 0.990936636925, - 0.771163344383, 1.007869958878, 0.788096666336, -0.008129999973, - 0.788096666336, 0.008803333156, 0.788096666336, 0.025736667216, - 0.788096666336, 0.042670000345, 0.788096666336, 0.059603333473, - 0.788096666336, 0.076536670327, 0.788096666336, 0.093469999731, - 0.788096666336, 0.110403336585, 0.788096666336, 0.127336665988, - 0.788096666336, 0.144270002842, 0.788096666336, 0.161203339696, - 0.788096666336, 0.178136661649, 0.788096666336, 0.195069998503, - 0.788096666336, 0.212003335357, 0.788096666336, 0.228936672211, - 0.788096666336, 0.245869994164, 0.788096666336, 0.262803345919, - 0.788096666336, 0.279736667871, 0.787926256657, 0.296790271997, - 0.787869870663, 0.313750088215, 0.787816941738, 0.330701231956, - 0.787767767906, 0.347644120455, 0.787722766399, 0.364579290152, - 0.787682116032, 0.381507366896, 0.787545800209, 0.398464411497, - 0.787496209145, 0.415379941463, 0.787454962730, 0.432287663221, - 0.787422478199, 0.449188977480, 0.787399053574, 0.466085404158, - 0.787384867668, 0.482978522778, 0.787380158901, 0.499870002270, - 0.787384867668, 0.516761481762, 0.787399053574, 0.533654570580, - 0.787422478199, 0.550551056862, 0.787454962730, 0.567452371120, - 0.787496209145, 0.584360063076, 0.787545800209, 0.601275563240, - 0.787682116032, 0.618232607841, 0.787722766399, 0.635160684586, - 0.787767767906, 0.652095913887, 0.787816941738, 0.669038772583, - 0.787869870663, 0.685989916325, 0.787926256657, 0.702949702740, - 0.788096666336, 0.720003306866, 0.788096666336, 0.736936688423, - 0.788096666336, 0.753870010376, 0.788096666336, 0.770803332329, - 0.788096666336, 0.787736654282, 0.788096666336, 0.804669976234, - 0.788096666336, 0.821603357792, 0.788096666336, 0.838536679745, - 0.788096666336, 0.855470001698, 0.788096666336, 0.872403323650, - 0.788096666336, 0.889336645603, 0.788096666336, 0.906270027161, - 0.788096666336, 0.923203349113, 0.788096666336, 0.940136671066, - 0.788096666336, 0.957069993019, 0.788096666336, 0.974003314972, - 0.788096666336, 0.990936636925, 0.788096666336, 1.007869958878, - 0.805029988289, -0.008129999973, 0.805029988289, 0.008803333156, - 0.805029988289, 0.025736667216, 0.805029988289, 0.042670000345, - 0.805029988289, 0.059603333473, 0.805029988289, 0.076536670327, - 0.805029988289, 0.093469999731, 0.805029988289, 0.110403336585, - 0.805029988289, 0.127336665988, 0.805029988289, 0.144270002842, - 0.805029988289, 0.161203339696, 0.805029988289, 0.178136661649, - 0.805029988289, 0.195069998503, 0.805029988289, 0.212003335357, - 0.805029988289, 0.228936672211, 0.805029988289, 0.245869994164, - 0.805029988289, 0.262803345919, 0.805029988289, 0.279736667871, - 0.805029988289, 0.296669989824, 0.805029988289, 0.313603341579, - 0.804826378822, 0.330649763346, 0.804776608944, 0.347596675158, - 0.804731070995, 0.364536195993, 0.804690063000, 0.381468862295, - 0.804653882980, 0.398395389318, 0.804622709751, 0.415316462517, - 0.804596841335, 0.432232916355, 0.804484367371, 0.449160933495, - 0.804460883141, 0.466066569090, 0.804446756840, 0.482969075441, - 0.804441988468, 0.499870002270, 0.804446756840, 0.516770958900, - 0.804460883141, 0.533673405647, 0.804484367371, 0.550579071045, - 0.804596841335, 0.567507088184, 0.804622709751, 0.584423542023, - 0.804653882980, 0.601344645023, 0.804690063000, 0.618271112442, - 0.804731070995, 0.635203838348, 0.804776608944, 0.652143299580, - 0.804826378822, 0.669090211391, 0.805029988289, 0.686136662960, - 0.805029988289, 0.703069984913, 0.805029988289, 0.720003306866, - 0.805029988289, 0.736936688423, 0.805029988289, 0.753870010376, - 0.805029988289, 0.770803332329, 0.805029988289, 0.787736654282, - 0.805029988289, 0.804669976234, 0.805029988289, 0.821603357792, - 0.805029988289, 0.838536679745, 0.805029988289, 0.855470001698, - 0.805029988289, 0.872403323650, 0.805029988289, 0.889336645603, - 0.805029988289, 0.906270027161, 0.805029988289, 0.923203349113, - 0.805029988289, 0.940136671066, 0.805029988289, 0.957069993019, - 0.805029988289, 0.974003314972, 0.805029988289, 0.990936636925, - 0.805029988289, 1.007869958878, 0.821963310242, -0.008129999973, - 0.821963310242, 0.008803333156, 0.821963310242, 0.025736667216, - 0.821963310242, 0.042670000345, 0.821963310242, 0.059603333473, - 0.821963310242, 0.076536670327, 0.821963310242, 0.093469999731, - 0.821963310242, 0.110403336585, 0.821963310242, 0.127336665988, - 0.821963310242, 0.144270002842, 0.821963310242, 0.161203339696, - 0.821963310242, 0.178136661649, 0.821963310242, 0.195069998503, - 0.821963310242, 0.212003335357, 0.821963310242, 0.228936672211, - 0.821963310242, 0.245869994164, 0.821963310242, 0.262803345919, - 0.821963310242, 0.279736667871, 0.821963310242, 0.296669989824, - 0.821963310242, 0.313603341579, 0.821963310242, 0.330536663532, - 0.821797132492, 0.347548723221, 0.821751177311, 0.364492684603, - 0.821709811687, 0.381430059671, 0.821673393250, 0.398361563683, - 0.821642100811, 0.415287882090, 0.821616172791, 0.432209759951, - 0.821595788002, 0.449128031731, 0.821581065655, 0.466043561697, - 0.821572244167, 0.482957243919, 0.821569263935, 0.499870002270, - 0.821572244167, 0.516782760620, 0.821581065655, 0.533696413040, - 0.821595788002, 0.550611972809, 0.821616172791, 0.567530214787, - 0.821642100811, 0.584452152252, 0.821673393250, 0.601378440857, - 0.821709811687, 0.618309915066, 0.821751177311, 0.635247349739, - 0.821797132492, 0.652191281319, 0.821963310242, 0.669203341007, - 0.821963310242, 0.686136662960, 0.821963310242, 0.703069984913, - 0.821963310242, 0.720003306866, 0.821963310242, 0.736936688423, - 0.821963310242, 0.753870010376, 0.821963310242, 0.770803332329, - 0.821963310242, 0.787736654282, 0.821963310242, 0.804669976234, - 0.821963310242, 0.821603357792, 0.821963310242, 0.838536679745, - 0.821963310242, 0.855470001698, 0.821963310242, 0.872403323650, - 0.821963310242, 0.889336645603, 0.821963310242, 0.906270027161, - 0.821963310242, 0.923203349113, 0.821963310242, 0.940136671066, - 0.821963310242, 0.957069993019, 0.821963310242, 0.974003314972, - 0.821963310242, 0.990936636925, 0.821963310242, 1.007869958878, - 0.838896691799, -0.008129999973, 0.838896691799, 0.008803333156, - 0.838896691799, 0.025736667216, 0.838896691799, 0.042670000345, - 0.838896691799, 0.059603333473, 0.838896691799, 0.076536670327, - 0.838896691799, 0.093469999731, 0.838896691799, 0.110403336585, - 0.838896691799, 0.127336665988, 0.838896691799, 0.144270002842, - 0.838896691799, 0.161203339696, 0.838896691799, 0.178136661649, - 0.838896691799, 0.195069998503, 0.838896691799, 0.212003335357, - 0.838896691799, 0.228936672211, 0.838896691799, 0.245869994164, - 0.838896691799, 0.262803345919, 0.838896691799, 0.279736667871, - 0.838896691799, 0.296669989824, 0.838896691799, 0.313603341579, - 0.838896691799, 0.330536663532, 0.838896691799, 0.347469985485, - 0.838896691799, 0.364403337240, 0.838896691799, 0.381336659193, - 0.838704764843, 0.398327589035, 0.838673293591, 0.415259182453, - 0.838647305965, 0.432186543941, 0.838626861572, 0.449110478163, - 0.838612198830, 0.466031789780, 0.838603317738, 0.482951343060, - 0.838600397110, 0.499870002270, 0.838603317738, 0.516788661480, - 0.838612198830, 0.533708214760, 0.838626861572, 0.550629556179, - 0.838647305965, 0.567553460598, 0.838673293591, 0.584480822086, - 0.838704764843, 0.601412415504, 0.838896691799, 0.618403315544, - 0.838896691799, 0.635336637497, 0.838896691799, 0.652270019054, - 0.838896691799, 0.669203341007, 0.838896691799, 0.686136662960, - 0.838896691799, 0.703069984913, 0.838896691799, 0.720003306866, - 0.838896691799, 0.736936688423, 0.838896691799, 0.753870010376, - 0.838896691799, 0.770803332329, 0.838896691799, 0.787736654282, - 0.838896691799, 0.804669976234, 0.838896691799, 0.821603357792, - 0.838896691799, 0.838536679745, 0.838896691799, 0.855470001698, - 0.838896691799, 0.872403323650, 0.838896691799, 0.889336645603, - 0.838896691799, 0.906270027161, 0.838896691799, 0.923203349113, - 0.838896691799, 0.940136671066, 0.838896691799, 0.957069993019, - 0.838896691799, 0.974003314972, 0.838896691799, 0.990936636925, - 0.838896691799, 1.007869958878, 0.855830013752, -0.008129999973, - 0.855830013752, 0.008803333156, 0.855830013752, 0.025736667216, - 0.855830013752, 0.042670000345, 0.855830013752, 0.059603333473, - 0.855830013752, 0.076536670327, 0.855830013752, 0.093469999731, - 0.855830013752, 0.110403336585, 0.855830013752, 0.127336665988, - 0.855830013752, 0.144270002842, 0.855830013752, 0.161203339696, - 0.855830013752, 0.178136661649, 0.855830013752, 0.195069998503, - 0.855830013752, 0.212003335357, 0.855830013752, 0.228936672211, - 0.855830013752, 0.245869994164, 0.855830013752, 0.262803345919, - 0.855830013752, 0.279736667871, 0.855830013752, 0.296669989824, - 0.855830013752, 0.313603341579, 0.855830013752, 0.330536663532, - 0.855830013752, 0.347469985485, 0.855830013752, 0.364403337240, - 0.855830013752, 0.381336659193, 0.855830013752, 0.398270010948, - 0.855830013752, 0.415203332901, 0.855830013752, 0.432136654854, - 0.855830013752, 0.449070006609, 0.855830013752, 0.466003328562, - 0.855646312237, 0.482945412397, 0.855643332005, 0.499870002270, - 0.855646312237, 0.516794562340, 0.855830013752, 0.533736646175, - 0.855830013752, 0.550670027733, 0.855830013752, 0.567603349686, - 0.855830013752, 0.584536671638, 0.855830013752, 0.601469993591, - 0.855830013752, 0.618403315544, 0.855830013752, 0.635336637497, - 0.855830013752, 0.652270019054, 0.855830013752, 0.669203341007, - 0.855830013752, 0.686136662960, 0.855830013752, 0.703069984913, - 0.855830013752, 0.720003306866, 0.855830013752, 0.736936688423, - 0.855830013752, 0.753870010376, 0.855830013752, 0.770803332329, - 0.855830013752, 0.787736654282, 0.855830013752, 0.804669976234, - 0.855830013752, 0.821603357792, 0.855830013752, 0.838536679745, - 0.855830013752, 0.855470001698, 0.855830013752, 0.872403323650, - 0.855830013752, 0.889336645603, 0.855830013752, 0.906270027161, - 0.855830013752, 0.923203349113, 0.855830013752, 0.940136671066, - 0.855830013752, 0.957069993019, 0.855830013752, 0.974003314972, - 0.855830013752, 0.990936636925, 0.855830013752, 1.007869958878, - 0.872763335705, -0.008129999973, 0.872763335705, 0.008803333156, - 0.872763335705, 0.025736667216, 0.872763335705, 0.042670000345, - 0.872763335705, 0.059603333473, 0.872763335705, 0.076536670327, - 0.872763335705, 0.093469999731, 0.872763335705, 0.110403336585, - 0.872763335705, 0.127336665988, 0.872763335705, 0.144270002842, - 0.872763335705, 0.161203339696, 0.872763335705, 0.178136661649, - 0.872763335705, 0.195069998503, 0.872763335705, 0.212003335357, - 0.872763335705, 0.228936672211, 0.872763335705, 0.245869994164, - 0.872763335705, 0.262803345919, 0.872763335705, 0.279736667871, - 0.872763335705, 0.296669989824, 0.872763335705, 0.313603341579, - 0.872763335705, 0.330536663532, 0.872763335705, 0.347469985485, - 0.872763335705, 0.364403337240, 0.872763335705, 0.381336659193, - 0.872763335705, 0.398270010948, 0.872763335705, 0.415203332901, - 0.872763335705, 0.432136654854, 0.872763335705, 0.449070006609, - 0.872763335705, 0.466003328562, 0.872763335705, 0.482936680317, - 0.872763335705, 0.499870002270, 0.872763335705, 0.516803324223, - 0.872763335705, 0.533736646175, 0.872763335705, 0.550670027733, - 0.872763335705, 0.567603349686, 0.872763335705, 0.584536671638, - 0.872763335705, 0.601469993591, 0.872763335705, 0.618403315544, - 0.872763335705, 0.635336637497, 0.872763335705, 0.652270019054, - 0.872763335705, 0.669203341007, 0.872763335705, 0.686136662960, - 0.872763335705, 0.703069984913, 0.872763335705, 0.720003306866, - 0.872763335705, 0.736936688423, 0.872763335705, 0.753870010376, - 0.872763335705, 0.770803332329, 0.872763335705, 0.787736654282, - 0.872763335705, 0.804669976234, 0.872763335705, 0.821603357792, - 0.872763335705, 0.838536679745, 0.872763335705, 0.855470001698, - 0.872763335705, 0.872403323650, 0.872763335705, 0.889336645603, - 0.872763335705, 0.906270027161, 0.872763335705, 0.923203349113, - 0.872763335705, 0.940136671066, 0.872763335705, 0.957069993019, - 0.872763335705, 0.974003314972, 0.872763335705, 0.990936636925, - 0.872763335705, 1.007869958878, 0.889696657658, -0.008129999973, - 0.889696657658, 0.008803333156, 0.889696657658, 0.025736667216, - 0.889696657658, 0.042670000345, 0.889696657658, 0.059603333473, - 0.889696657658, 0.076536670327, 0.889696657658, 0.093469999731, - 0.889696657658, 0.110403336585, 0.889696657658, 0.127336665988, - 0.889696657658, 0.144270002842, 0.889696657658, 0.161203339696, - 0.889696657658, 0.178136661649, 0.889696657658, 0.195069998503, - 0.889696657658, 0.212003335357, 0.889696657658, 0.228936672211, - 0.889696657658, 0.245869994164, 0.889696657658, 0.262803345919, - 0.889696657658, 0.279736667871, 0.889696657658, 0.296669989824, - 0.889696657658, 0.313603341579, 0.889696657658, 0.330536663532, - 0.889696657658, 0.347469985485, 0.889696657658, 0.364403337240, - 0.889696657658, 0.381336659193, 0.889696657658, 0.398270010948, - 0.889696657658, 0.415203332901, 0.889696657658, 0.432136654854, - 0.889696657658, 0.449070006609, 0.889696657658, 0.466003328562, - 0.889696657658, 0.482936680317, 0.889696657658, 0.499870002270, - 0.889696657658, 0.516803324223, 0.889696657658, 0.533736646175, - 0.889696657658, 0.550670027733, 0.889696657658, 0.567603349686, - 0.889696657658, 0.584536671638, 0.889696657658, 0.601469993591, - 0.889696657658, 0.618403315544, 0.889696657658, 0.635336637497, - 0.889696657658, 0.652270019054, 0.889696657658, 0.669203341007, - 0.889696657658, 0.686136662960, 0.889696657658, 0.703069984913, - 0.889696657658, 0.720003306866, 0.889696657658, 0.736936688423, - 0.889696657658, 0.753870010376, 0.889696657658, 0.770803332329, - 0.889696657658, 0.787736654282, 0.889696657658, 0.804669976234, - 0.889696657658, 0.821603357792, 0.889696657658, 0.838536679745, - 0.889696657658, 0.855470001698, 0.889696657658, 0.872403323650, - 0.889696657658, 0.889336645603, 0.889696657658, 0.906270027161, - 0.889696657658, 0.923203349113, 0.889696657658, 0.940136671066, - 0.889696657658, 0.957069993019, 0.889696657658, 0.974003314972, - 0.889696657658, 0.990936636925, 0.889696657658, 1.007869958878, - 0.906629979610, -0.008129999973, 0.906629979610, 0.008803333156, - 0.906629979610, 0.025736667216, 0.906629979610, 0.042670000345, - 0.906629979610, 0.059603333473, 0.906629979610, 0.076536670327, - 0.906629979610, 0.093469999731, 0.906629979610, 0.110403336585, - 0.906629979610, 0.127336665988, 0.906629979610, 0.144270002842, - 0.906629979610, 0.161203339696, 0.906629979610, 0.178136661649, - 0.906629979610, 0.195069998503, 0.906629979610, 0.212003335357, - 0.906629979610, 0.228936672211, 0.906629979610, 0.245869994164, - 0.906629979610, 0.262803345919, 0.906629979610, 0.279736667871, - 0.906629979610, 0.296669989824, 0.906629979610, 0.313603341579, - 0.906629979610, 0.330536663532, 0.906629979610, 0.347469985485, - 0.906629979610, 0.364403337240, 0.906629979610, 0.381336659193, - 0.906629979610, 0.398270010948, 0.906629979610, 0.415203332901, - 0.906629979610, 0.432136654854, 0.906629979610, 0.449070006609, - 0.906629979610, 0.466003328562, 0.906629979610, 0.482936680317, - 0.906629979610, 0.499870002270, 0.906629979610, 0.516803324223, - 0.906629979610, 0.533736646175, 0.906629979610, 0.550670027733, - 0.906629979610, 0.567603349686, 0.906629979610, 0.584536671638, - 0.906629979610, 0.601469993591, 0.906629979610, 0.618403315544, - 0.906629979610, 0.635336637497, 0.906629979610, 0.652270019054, - 0.906629979610, 0.669203341007, 0.906629979610, 0.686136662960, - 0.906629979610, 0.703069984913, 0.906629979610, 0.720003306866, - 0.906629979610, 0.736936688423, 0.906629979610, 0.753870010376, - 0.906629979610, 0.770803332329, 0.906629979610, 0.787736654282, - 0.906629979610, 0.804669976234, 0.906629979610, 0.821603357792, - 0.906629979610, 0.838536679745, 0.906629979610, 0.855470001698, - 0.906629979610, 0.872403323650, 0.906629979610, 0.889336645603, - 0.906629979610, 0.906270027161, 0.906629979610, 0.923203349113, - 0.906629979610, 0.940136671066, 0.906629979610, 0.957069993019, - 0.906629979610, 0.974003314972, 0.906629979610, 0.990936636925, - 0.906629979610, 1.007869958878, 0.923563361168, -0.008129999973, - 0.923563361168, 0.008803333156, 0.923563361168, 0.025736667216, - 0.923563361168, 0.042670000345, 0.923563361168, 0.059603333473, - 0.923563361168, 0.076536670327, 0.923563361168, 0.093469999731, - 0.923563361168, 0.110403336585, 0.923563361168, 0.127336665988, - 0.923563361168, 0.144270002842, 0.923563361168, 0.161203339696, - 0.923563361168, 0.178136661649, 0.923563361168, 0.195069998503, - 0.923563361168, 0.212003335357, 0.923563361168, 0.228936672211, - 0.923563361168, 0.245869994164, 0.923563361168, 0.262803345919, - 0.923563361168, 0.279736667871, 0.923563361168, 0.296669989824, - 0.923563361168, 0.313603341579, 0.923563361168, 0.330536663532, - 0.923563361168, 0.347469985485, 0.923563361168, 0.364403337240, - 0.923563361168, 0.381336659193, 0.923563361168, 0.398270010948, - 0.923563361168, 0.415203332901, 0.923563361168, 0.432136654854, - 0.923563361168, 0.449070006609, 0.923563361168, 0.466003328562, - 0.923563361168, 0.482936680317, 0.923563361168, 0.499870002270, - 0.923563361168, 0.516803324223, 0.923563361168, 0.533736646175, - 0.923563361168, 0.550670027733, 0.923563361168, 0.567603349686, - 0.923563361168, 0.584536671638, 0.923563361168, 0.601469993591, - 0.923563361168, 0.618403315544, 0.923563361168, 0.635336637497, - 0.923563361168, 0.652270019054, 0.923563361168, 0.669203341007, - 0.923563361168, 0.686136662960, 0.923563361168, 0.703069984913, - 0.923563361168, 0.720003306866, 0.923563361168, 0.736936688423, - 0.923563361168, 0.753870010376, 0.923563361168, 0.770803332329, - 0.923563361168, 0.787736654282, 0.923563361168, 0.804669976234, - 0.923563361168, 0.821603357792, 0.923563361168, 0.838536679745, - 0.923563361168, 0.855470001698, 0.923563361168, 0.872403323650, - 0.923563361168, 0.889336645603, 0.923563361168, 0.906270027161, - 0.923563361168, 0.923203349113, 0.923563361168, 0.940136671066, - 0.923563361168, 0.957069993019, 0.923563361168, 0.974003314972, - 0.923563361168, 0.990936636925, 0.923563361168, 1.007869958878, - 0.940496683121, -0.008129999973, 0.940496683121, 0.008803333156, - 0.940496683121, 0.025736667216, 0.940496683121, 0.042670000345, - 0.940496683121, 0.059603333473, 0.940496683121, 0.076536670327, - 0.940496683121, 0.093469999731, 0.940496683121, 0.110403336585, - 0.940496683121, 0.127336665988, 0.940496683121, 0.144270002842, - 0.940496683121, 0.161203339696, 0.940496683121, 0.178136661649, - 0.940496683121, 0.195069998503, 0.940496683121, 0.212003335357, - 0.940496683121, 0.228936672211, 0.940496683121, 0.245869994164, - 0.940496683121, 0.262803345919, 0.940496683121, 0.279736667871, - 0.940496683121, 0.296669989824, 0.940496683121, 0.313603341579, - 0.940496683121, 0.330536663532, 0.940496683121, 0.347469985485, - 0.940496683121, 0.364403337240, 0.940496683121, 0.381336659193, - 0.940496683121, 0.398270010948, 0.940496683121, 0.415203332901, - 0.940496683121, 0.432136654854, 0.940496683121, 0.449070006609, - 0.940496683121, 0.466003328562, 0.940496683121, 0.482936680317, - 0.940496683121, 0.499870002270, 0.940496683121, 0.516803324223, - 0.940496683121, 0.533736646175, 0.940496683121, 0.550670027733, - 0.940496683121, 0.567603349686, 0.940496683121, 0.584536671638, - 0.940496683121, 0.601469993591, 0.940496683121, 0.618403315544, - 0.940496683121, 0.635336637497, 0.940496683121, 0.652270019054, - 0.940496683121, 0.669203341007, 0.940496683121, 0.686136662960, - 0.940496683121, 0.703069984913, 0.940496683121, 0.720003306866, - 0.940496683121, 0.736936688423, 0.940496683121, 0.753870010376, - 0.940496683121, 0.770803332329, 0.940496683121, 0.787736654282, - 0.940496683121, 0.804669976234, 0.940496683121, 0.821603357792, - 0.940496683121, 0.838536679745, 0.940496683121, 0.855470001698, - 0.940496683121, 0.872403323650, 0.940496683121, 0.889336645603, - 0.940496683121, 0.906270027161, 0.940496683121, 0.923203349113, - 0.940496683121, 0.940136671066, 0.940496683121, 0.957069993019, - 0.940496683121, 0.974003314972, 0.940496683121, 0.990936636925, - 0.940496683121, 1.007869958878, 0.957430005074, -0.008129999973, - 0.957430005074, 0.008803333156, 0.957430005074, 0.025736667216, - 0.957430005074, 0.042670000345, 0.957430005074, 0.059603333473, - 0.957430005074, 0.076536670327, 0.957430005074, 0.093469999731, - 0.957430005074, 0.110403336585, 0.957430005074, 0.127336665988, - 0.957430005074, 0.144270002842, 0.957430005074, 0.161203339696, - 0.957430005074, 0.178136661649, 0.957430005074, 0.195069998503, - 0.957430005074, 0.212003335357, 0.957430005074, 0.228936672211, - 0.957430005074, 0.245869994164, 0.957430005074, 0.262803345919, - 0.957430005074, 0.279736667871, 0.957430005074, 0.296669989824, - 0.957430005074, 0.313603341579, 0.957430005074, 0.330536663532, - 0.957430005074, 0.347469985485, 0.957430005074, 0.364403337240, - 0.957430005074, 0.381336659193, 0.957430005074, 0.398270010948, - 0.957430005074, 0.415203332901, 0.957430005074, 0.432136654854, - 0.957430005074, 0.449070006609, 0.957430005074, 0.466003328562, - 0.957430005074, 0.482936680317, 0.957430005074, 0.499870002270, - 0.957430005074, 0.516803324223, 0.957430005074, 0.533736646175, - 0.957430005074, 0.550670027733, 0.957430005074, 0.567603349686, - 0.957430005074, 0.584536671638, 0.957430005074, 0.601469993591, - 0.957430005074, 0.618403315544, 0.957430005074, 0.635336637497, - 0.957430005074, 0.652270019054, 0.957430005074, 0.669203341007, - 0.957430005074, 0.686136662960, 0.957430005074, 0.703069984913, - 0.957430005074, 0.720003306866, 0.957430005074, 0.736936688423, - 0.957430005074, 0.753870010376, 0.957430005074, 0.770803332329, - 0.957430005074, 0.787736654282, 0.957430005074, 0.804669976234, - 0.957430005074, 0.821603357792, 0.957430005074, 0.838536679745, - 0.957430005074, 0.855470001698, 0.957430005074, 0.872403323650, - 0.957430005074, 0.889336645603, 0.957430005074, 0.906270027161, - 0.957430005074, 0.923203349113, 0.957430005074, 0.940136671066, - 0.957430005074, 0.957069993019, 0.957430005074, 0.974003314972, - 0.957430005074, 0.990936636925, 0.957430005074, 1.007869958878, - 0.974363327026, -0.008129999973, 0.974363327026, 0.008803333156, - 0.974363327026, 0.025736667216, 0.974363327026, 0.042670000345, - 0.974363327026, 0.059603333473, 0.974363327026, 0.076536670327, - 0.974363327026, 0.093469999731, 0.974363327026, 0.110403336585, - 0.974363327026, 0.127336665988, 0.974363327026, 0.144270002842, - 0.974363327026, 0.161203339696, 0.974363327026, 0.178136661649, - 0.974363327026, 0.195069998503, 0.974363327026, 0.212003335357, - 0.974363327026, 0.228936672211, 0.974363327026, 0.245869994164, - 0.974363327026, 0.262803345919, 0.974363327026, 0.279736667871, - 0.974363327026, 0.296669989824, 0.974363327026, 0.313603341579, - 0.974363327026, 0.330536663532, 0.974363327026, 0.347469985485, - 0.974363327026, 0.364403337240, 0.974363327026, 0.381336659193, - 0.974363327026, 0.398270010948, 0.974363327026, 0.415203332901, - 0.974363327026, 0.432136654854, 0.974363327026, 0.449070006609, - 0.974363327026, 0.466003328562, 0.974363327026, 0.482936680317, - 0.974363327026, 0.499870002270, 0.974363327026, 0.516803324223, - 0.974363327026, 0.533736646175, 0.974363327026, 0.550670027733, - 0.974363327026, 0.567603349686, 0.974363327026, 0.584536671638, - 0.974363327026, 0.601469993591, 0.974363327026, 0.618403315544, - 0.974363327026, 0.635336637497, 0.974363327026, 0.652270019054, - 0.974363327026, 0.669203341007, 0.974363327026, 0.686136662960, - 0.974363327026, 0.703069984913, 0.974363327026, 0.720003306866, - 0.974363327026, 0.736936688423, 0.974363327026, 0.753870010376, - 0.974363327026, 0.770803332329, 0.974363327026, 0.787736654282, - 0.974363327026, 0.804669976234, 0.974363327026, 0.821603357792, - 0.974363327026, 0.838536679745, 0.974363327026, 0.855470001698, - 0.974363327026, 0.872403323650, 0.974363327026, 0.889336645603, - 0.974363327026, 0.906270027161, 0.974363327026, 0.923203349113, - 0.974363327026, 0.940136671066, 0.974363327026, 0.957069993019, - 0.974363327026, 0.974003314972, 0.974363327026, 0.990936636925, - 0.974363327026, 1.007869958878, 0.991296648979, -0.008129999973, - 0.991296648979, 0.008803333156, 0.991296648979, 0.025736667216, - 0.991296648979, 0.042670000345, 0.991296648979, 0.059603333473, - 0.991296648979, 0.076536670327, 0.991296648979, 0.093469999731, - 0.991296648979, 0.110403336585, 0.991296648979, 0.127336665988, - 0.991296648979, 0.144270002842, 0.991296648979, 0.161203339696, - 0.991296648979, 0.178136661649, 0.991296648979, 0.195069998503, - 0.991296648979, 0.212003335357, 0.991296648979, 0.228936672211, - 0.991296648979, 0.245869994164, 0.991296648979, 0.262803345919, - 0.991296648979, 0.279736667871, 0.991296648979, 0.296669989824, - 0.991296648979, 0.313603341579, 0.991296648979, 0.330536663532, - 0.991296648979, 0.347469985485, 0.991296648979, 0.364403337240, - 0.991296648979, 0.381336659193, 0.991296648979, 0.398270010948, - 0.991296648979, 0.415203332901, 0.991296648979, 0.432136654854, - 0.991296648979, 0.449070006609, 0.991296648979, 0.466003328562, - 0.991296648979, 0.482936680317, 0.991296648979, 0.499870002270, - 0.991296648979, 0.516803324223, 0.991296648979, 0.533736646175, - 0.991296648979, 0.550670027733, 0.991296648979, 0.567603349686, - 0.991296648979, 0.584536671638, 0.991296648979, 0.601469993591, - 0.991296648979, 0.618403315544, 0.991296648979, 0.635336637497, - 0.991296648979, 0.652270019054, 0.991296648979, 0.669203341007, - 0.991296648979, 0.686136662960, 0.991296648979, 0.703069984913, - 0.991296648979, 0.720003306866, 0.991296648979, 0.736936688423, - 0.991296648979, 0.753870010376, 0.991296648979, 0.770803332329, - 0.991296648979, 0.787736654282, 0.991296648979, 0.804669976234, - 0.991296648979, 0.821603357792, 0.991296648979, 0.838536679745, - 0.991296648979, 0.855470001698, 0.991296648979, 0.872403323650, - 0.991296648979, 0.889336645603, 0.991296648979, 0.906270027161, - 0.991296648979, 0.923203349113, 0.991296648979, 0.940136671066, - 0.991296648979, 0.957069993019, 0.991296648979, 0.974003314972, - 0.991296648979, 0.990936636925, 0.991296648979, 1.007869958878, - 1.008229970932, -0.008129999973, 1.008229970932, 0.008803333156, - 1.008229970932, 0.025736667216, 1.008229970932, 0.042670000345, - 1.008229970932, 0.059603333473, 1.008229970932, 0.076536670327, - 1.008229970932, 0.093469999731, 1.008229970932, 0.110403336585, - 1.008229970932, 0.127336665988, 1.008229970932, 0.144270002842, - 1.008229970932, 0.161203339696, 1.008229970932, 0.178136661649, - 1.008229970932, 0.195069998503, 1.008229970932, 0.212003335357, - 1.008229970932, 0.228936672211, 1.008229970932, 0.245869994164, - 1.008229970932, 0.262803345919, 1.008229970932, 0.279736667871, - 1.008229970932, 0.296669989824, 1.008229970932, 0.313603341579, - 1.008229970932, 0.330536663532, 1.008229970932, 0.347469985485, - 1.008229970932, 0.364403337240, 1.008229970932, 0.381336659193, - 1.008229970932, 0.398270010948, 1.008229970932, 0.415203332901, - 1.008229970932, 0.432136654854, 1.008229970932, 0.449070006609, - 1.008229970932, 0.466003328562, 1.008229970932, 0.482936680317, - 1.008229970932, 0.499870002270, 1.008229970932, 0.516803324223, - 1.008229970932, 0.533736646175, 1.008229970932, 0.550670027733, - 1.008229970932, 0.567603349686, 1.008229970932, 0.584536671638, - 1.008229970932, 0.601469993591, 1.008229970932, 0.618403315544, - 1.008229970932, 0.635336637497, 1.008229970932, 0.652270019054, - 1.008229970932, 0.669203341007, 1.008229970932, 0.686136662960, - 1.008229970932, 0.703069984913, 1.008229970932, 0.720003306866, - 1.008229970932, 0.736936688423, 1.008229970932, 0.753870010376, - 1.008229970932, 0.770803332329, 1.008229970932, 0.787736654282, - 1.008229970932, 0.804669976234, 1.008229970932, 0.821603357792, - 1.008229970932, 0.838536679745, 1.008229970932, 0.855470001698, - 1.008229970932, 0.872403323650, 1.008229970932, 0.889336645603, - 1.008229970932, 0.906270027161, 1.008229970932, 0.923203349113, - 1.008229970932, 0.940136671066, 1.008229970932, 0.957069993019, - 1.008229970932, 0.974003314972, 1.008229970932, 0.990936636925, - 1.008229970932, 1.007869958878, -}; - -} /* namespace Pdraw */ - -#endif /* USE_GLES2 */ +/** + * Parrot Drones Awesome Video Viewer Library + * OpenGL ES 2.0 HMD distortion correction + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Extracted from FPVToolbox by Frédéric Bertolus + * https://github.com/niavok/fpvtoolbox + * and translated from Java to C++ + */ + +#ifdef USE_GLES2 + +namespace Pdraw { + +extern const float pdraw_gles2HmdTexCoordsCockpitglassesBlue[7442] = { + -0.007770000026, -0.008129999973, -0.007770000026, 0.008803333156, + -0.007770000026, 0.025736667216, -0.007770000026, 0.042670000345, + -0.007770000026, 0.059603333473, -0.007770000026, 0.076536670327, + -0.007770000026, 0.093469999731, -0.007770000026, 0.110403336585, + -0.007770000026, 0.127336665988, -0.007770000026, 0.144270002842, + -0.007770000026, 0.161203339696, -0.007770000026, 0.178136661649, + -0.007770000026, 0.195069998503, -0.007770000026, 0.212003335357, + -0.007770000026, 0.228936672211, -0.007770000026, 0.245869994164, + -0.007770000026, 0.262803345919, -0.007770000026, 0.279736667871, + -0.007770000026, 0.296669989824, -0.007770000026, 0.313603341579, + -0.007770000026, 0.330536663532, -0.007770000026, 0.347469985485, + -0.007770000026, 0.364403337240, -0.007770000026, 0.381336659193, + -0.007770000026, 0.398270010948, -0.007770000026, 0.415203332901, + -0.007770000026, 0.432136654854, -0.007770000026, 0.449070006609, + -0.007770000026, 0.466003328562, -0.007770000026, 0.482936680317, + -0.007770000026, 0.499870002270, -0.007770000026, 0.516803324223, + -0.007770000026, 0.533736646175, -0.007770000026, 0.550670027733, + -0.007770000026, 0.567603349686, -0.007770000026, 0.584536671638, + -0.007770000026, 0.601469993591, -0.007770000026, 0.618403315544, + -0.007770000026, 0.635336637497, -0.007770000026, 0.652270019054, + -0.007770000026, 0.669203341007, -0.007770000026, 0.686136662960, + -0.007770000026, 0.703069984913, -0.007770000026, 0.720003306866, + -0.007770000026, 0.736936688423, -0.007770000026, 0.753870010376, + -0.007770000026, 0.770803332329, -0.007770000026, 0.787736654282, + -0.007770000026, 0.804669976234, -0.007770000026, 0.821603357792, + -0.007770000026, 0.838536679745, -0.007770000026, 0.855470001698, + -0.007770000026, 0.872403323650, -0.007770000026, 0.889336645603, + -0.007770000026, 0.906270027161, -0.007770000026, 0.923203349113, + -0.007770000026, 0.940136671066, -0.007770000026, 0.957069993019, + -0.007770000026, 0.974003314972, -0.007770000026, 0.990936636925, + -0.007770000026, 1.007869958878, 0.009163333103, -0.008129999973, + 0.009163333103, 0.008803333156, 0.009163333103, 0.025736667216, + 0.009163333103, 0.042670000345, 0.009163333103, 0.059603333473, + 0.009163333103, 0.076536670327, 0.009163333103, 0.093469999731, + 0.009163333103, 0.110403336585, 0.009163333103, 0.127336665988, + 0.009163333103, 0.144270002842, 0.009163333103, 0.161203339696, + 0.009163333103, 0.178136661649, 0.009163333103, 0.195069998503, + 0.009163333103, 0.212003335357, 0.009163333103, 0.228936672211, + 0.009163333103, 0.245869994164, 0.009163333103, 0.262803345919, + 0.009163333103, 0.279736667871, 0.009163333103, 0.296669989824, + 0.009163333103, 0.313603341579, 0.009163333103, 0.330536663532, + 0.009163333103, 0.347469985485, 0.009163333103, 0.364403337240, + 0.009163333103, 0.381336659193, 0.009163333103, 0.398270010948, + 0.009163333103, 0.415203332901, 0.009163333103, 0.432136654854, + 0.009163333103, 0.449070006609, 0.009163333103, 0.466003328562, + 0.009163333103, 0.482936680317, 0.009163333103, 0.499870002270, + 0.009163333103, 0.516803324223, 0.009163333103, 0.533736646175, + 0.009163333103, 0.550670027733, 0.009163333103, 0.567603349686, + 0.009163333103, 0.584536671638, 0.009163333103, 0.601469993591, + 0.009163333103, 0.618403315544, 0.009163333103, 0.635336637497, + 0.009163333103, 0.652270019054, 0.009163333103, 0.669203341007, + 0.009163333103, 0.686136662960, 0.009163333103, 0.703069984913, + 0.009163333103, 0.720003306866, 0.009163333103, 0.736936688423, + 0.009163333103, 0.753870010376, 0.009163333103, 0.770803332329, + 0.009163333103, 0.787736654282, 0.009163333103, 0.804669976234, + 0.009163333103, 0.821603357792, 0.009163333103, 0.838536679745, + 0.009163333103, 0.855470001698, 0.009163333103, 0.872403323650, + 0.009163333103, 0.889336645603, 0.009163333103, 0.906270027161, + 0.009163333103, 0.923203349113, 0.009163333103, 0.940136671066, + 0.009163333103, 0.957069993019, 0.009163333103, 0.974003314972, + 0.009163333103, 0.990936636925, 0.009163333103, 1.007869958878, + 0.026096666232, -0.008129999973, 0.026096666232, 0.008803333156, + 0.026096666232, 0.025736667216, 0.026096666232, 0.042670000345, + 0.026096666232, 0.059603333473, 0.026096666232, 0.076536670327, + 0.026096666232, 0.093469999731, 0.026096666232, 0.110403336585, + 0.026096666232, 0.127336665988, 0.026096666232, 0.144270002842, + 0.026096666232, 0.161203339696, 0.026096666232, 0.178136661649, + 0.026096666232, 0.195069998503, 0.026096666232, 0.212003335357, + 0.026096666232, 0.228936672211, 0.026096666232, 0.245869994164, + 0.026096666232, 0.262803345919, 0.026096666232, 0.279736667871, + 0.026096666232, 0.296669989824, 0.026096666232, 0.313603341579, + 0.026096666232, 0.330536663532, 0.026096666232, 0.347469985485, + 0.026096666232, 0.364403337240, 0.026096666232, 0.381336659193, + 0.026096666232, 0.398270010948, 0.026096666232, 0.415203332901, + 0.026096666232, 0.432136654854, 0.026096666232, 0.449070006609, + 0.026096666232, 0.466003328562, 0.026096666232, 0.482936680317, + 0.026096666232, 0.499870002270, 0.026096666232, 0.516803324223, + 0.026096666232, 0.533736646175, 0.026096666232, 0.550670027733, + 0.026096666232, 0.567603349686, 0.026096666232, 0.584536671638, + 0.026096666232, 0.601469993591, 0.026096666232, 0.618403315544, + 0.026096666232, 0.635336637497, 0.026096666232, 0.652270019054, + 0.026096666232, 0.669203341007, 0.026096666232, 0.686136662960, + 0.026096666232, 0.703069984913, 0.026096666232, 0.720003306866, + 0.026096666232, 0.736936688423, 0.026096666232, 0.753870010376, + 0.026096666232, 0.770803332329, 0.026096666232, 0.787736654282, + 0.026096666232, 0.804669976234, 0.026096666232, 0.821603357792, + 0.026096666232, 0.838536679745, 0.026096666232, 0.855470001698, + 0.026096666232, 0.872403323650, 0.026096666232, 0.889336645603, + 0.026096666232, 0.906270027161, 0.026096666232, 0.923203349113, + 0.026096666232, 0.940136671066, 0.026096666232, 0.957069993019, + 0.026096666232, 0.974003314972, 0.026096666232, 0.990936636925, + 0.026096666232, 1.007869958878, 0.043030001223, -0.008129999973, + 0.043030001223, 0.008803333156, 0.043030001223, 0.025736667216, + 0.043030001223, 0.042670000345, 0.043030001223, 0.059603333473, + 0.043030001223, 0.076536670327, 0.043030001223, 0.093469999731, + 0.043030001223, 0.110403336585, 0.043030001223, 0.127336665988, + 0.043030001223, 0.144270002842, 0.043030001223, 0.161203339696, + 0.043030001223, 0.178136661649, 0.043030001223, 0.195069998503, + 0.043030001223, 0.212003335357, 0.043030001223, 0.228936672211, + 0.043030001223, 0.245869994164, 0.043030001223, 0.262803345919, + 0.043030001223, 0.279736667871, 0.043030001223, 0.296669989824, + 0.043030001223, 0.313603341579, 0.043030001223, 0.330536663532, + 0.043030001223, 0.347469985485, 0.043030001223, 0.364403337240, + 0.043030001223, 0.381336659193, 0.043030001223, 0.398270010948, + 0.043030001223, 0.415203332901, 0.043030001223, 0.432136654854, + 0.043030001223, 0.449070006609, 0.043030001223, 0.466003328562, + 0.043030001223, 0.482936680317, 0.043030001223, 0.499870002270, + 0.043030001223, 0.516803324223, 0.043030001223, 0.533736646175, + 0.043030001223, 0.550670027733, 0.043030001223, 0.567603349686, + 0.043030001223, 0.584536671638, 0.043030001223, 0.601469993591, + 0.043030001223, 0.618403315544, 0.043030001223, 0.635336637497, + 0.043030001223, 0.652270019054, 0.043030001223, 0.669203341007, + 0.043030001223, 0.686136662960, 0.043030001223, 0.703069984913, + 0.043030001223, 0.720003306866, 0.043030001223, 0.736936688423, + 0.043030001223, 0.753870010376, 0.043030001223, 0.770803332329, + 0.043030001223, 0.787736654282, 0.043030001223, 0.804669976234, + 0.043030001223, 0.821603357792, 0.043030001223, 0.838536679745, + 0.043030001223, 0.855470001698, 0.043030001223, 0.872403323650, + 0.043030001223, 0.889336645603, 0.043030001223, 0.906270027161, + 0.043030001223, 0.923203349113, 0.043030001223, 0.940136671066, + 0.043030001223, 0.957069993019, 0.043030001223, 0.974003314972, + 0.043030001223, 0.990936636925, 0.043030001223, 1.007869958878, + 0.059963334352, -0.008129999973, 0.059963334352, 0.008803333156, + 0.059963334352, 0.025736667216, 0.059963334352, 0.042670000345, + 0.059963334352, 0.059603333473, 0.059963334352, 0.076536670327, + 0.059963334352, 0.093469999731, 0.059963334352, 0.110403336585, + 0.059963334352, 0.127336665988, 0.059963334352, 0.144270002842, + 0.059963334352, 0.161203339696, 0.059963334352, 0.178136661649, + 0.059963334352, 0.195069998503, 0.059963334352, 0.212003335357, + 0.059963334352, 0.228936672211, 0.059963334352, 0.245869994164, + 0.059963334352, 0.262803345919, 0.059963334352, 0.279736667871, + 0.059963334352, 0.296669989824, 0.059963334352, 0.313603341579, + 0.059963334352, 0.330536663532, 0.059963334352, 0.347469985485, + 0.059963334352, 0.364403337240, 0.059963334352, 0.381336659193, + 0.059963334352, 0.398270010948, 0.059963334352, 0.415203332901, + 0.059963334352, 0.432136654854, 0.059963334352, 0.449070006609, + 0.059963334352, 0.466003328562, 0.059963334352, 0.482936680317, + 0.059963334352, 0.499870002270, 0.059963334352, 0.516803324223, + 0.059963334352, 0.533736646175, 0.059963334352, 0.550670027733, + 0.059963334352, 0.567603349686, 0.059963334352, 0.584536671638, + 0.059963334352, 0.601469993591, 0.059963334352, 0.618403315544, + 0.059963334352, 0.635336637497, 0.059963334352, 0.652270019054, + 0.059963334352, 0.669203341007, 0.059963334352, 0.686136662960, + 0.059963334352, 0.703069984913, 0.059963334352, 0.720003306866, + 0.059963334352, 0.736936688423, 0.059963334352, 0.753870010376, + 0.059963334352, 0.770803332329, 0.059963334352, 0.787736654282, + 0.059963334352, 0.804669976234, 0.059963334352, 0.821603357792, + 0.059963334352, 0.838536679745, 0.059963334352, 0.855470001698, + 0.059963334352, 0.872403323650, 0.059963334352, 0.889336645603, + 0.059963334352, 0.906270027161, 0.059963334352, 0.923203349113, + 0.059963334352, 0.940136671066, 0.059963334352, 0.957069993019, + 0.059963334352, 0.974003314972, 0.059963334352, 0.990936636925, + 0.059963334352, 1.007869958878, 0.076896667480, -0.008129999973, + 0.076896667480, 0.008803333156, 0.076896667480, 0.025736667216, + 0.076896667480, 0.042670000345, 0.076896667480, 0.059603333473, + 0.076896667480, 0.076536670327, 0.076896667480, 0.093469999731, + 0.076896667480, 0.110403336585, 0.076896667480, 0.127336665988, + 0.076896667480, 0.144270002842, 0.076896667480, 0.161203339696, + 0.076896667480, 0.178136661649, 0.076896667480, 0.195069998503, + 0.076896667480, 0.212003335357, 0.076896667480, 0.228936672211, + 0.076896667480, 0.245869994164, 0.076896667480, 0.262803345919, + 0.076896667480, 0.279736667871, 0.076896667480, 0.296669989824, + 0.076896667480, 0.313603341579, 0.076896667480, 0.330536663532, + 0.076896667480, 0.347469985485, 0.076896667480, 0.364403337240, + 0.076896667480, 0.381336659193, 0.076896667480, 0.398270010948, + 0.076896667480, 0.415203332901, 0.076896667480, 0.432136654854, + 0.076896667480, 0.449070006609, 0.076896667480, 0.466003328562, + 0.076896667480, 0.482936680317, 0.076896667480, 0.499870002270, + 0.076896667480, 0.516803324223, 0.076896667480, 0.533736646175, + 0.076896667480, 0.550670027733, 0.076896667480, 0.567603349686, + 0.076896667480, 0.584536671638, 0.076896667480, 0.601469993591, + 0.076896667480, 0.618403315544, 0.076896667480, 0.635336637497, + 0.076896667480, 0.652270019054, 0.076896667480, 0.669203341007, + 0.076896667480, 0.686136662960, 0.076896667480, 0.703069984913, + 0.076896667480, 0.720003306866, 0.076896667480, 0.736936688423, + 0.076896667480, 0.753870010376, 0.076896667480, 0.770803332329, + 0.076896667480, 0.787736654282, 0.076896667480, 0.804669976234, + 0.076896667480, 0.821603357792, 0.076896667480, 0.838536679745, + 0.076896667480, 0.855470001698, 0.076896667480, 0.872403323650, + 0.076896667480, 0.889336645603, 0.076896667480, 0.906270027161, + 0.076896667480, 0.923203349113, 0.076896667480, 0.940136671066, + 0.076896667480, 0.957069993019, 0.076896667480, 0.974003314972, + 0.076896667480, 0.990936636925, 0.076896667480, 1.007869958878, + 0.093829996884, -0.008129999973, 0.093829996884, 0.008803333156, + 0.093829996884, 0.025736667216, 0.093829996884, 0.042670000345, + 0.093829996884, 0.059603333473, 0.093829996884, 0.076536670327, + 0.093829996884, 0.093469999731, 0.093829996884, 0.110403336585, + 0.093829996884, 0.127336665988, 0.093829996884, 0.144270002842, + 0.093829996884, 0.161203339696, 0.093829996884, 0.178136661649, + 0.093829996884, 0.195069998503, 0.093829996884, 0.212003335357, + 0.093829996884, 0.228936672211, 0.093829996884, 0.245869994164, + 0.093829996884, 0.262803345919, 0.093829996884, 0.279736667871, + 0.093829996884, 0.296669989824, 0.093829996884, 0.313603341579, + 0.093829996884, 0.330536663532, 0.093829996884, 0.347469985485, + 0.093829996884, 0.364403337240, 0.093829996884, 0.381336659193, + 0.093829996884, 0.398270010948, 0.093829996884, 0.415203332901, + 0.093829996884, 0.432136654854, 0.093829996884, 0.449070006609, + 0.093829996884, 0.466003328562, 0.093829996884, 0.482936680317, + 0.093829996884, 0.499870002270, 0.093829996884, 0.516803324223, + 0.093829996884, 0.533736646175, 0.093829996884, 0.550670027733, + 0.093829996884, 0.567603349686, 0.093829996884, 0.584536671638, + 0.093829996884, 0.601469993591, 0.093829996884, 0.618403315544, + 0.093829996884, 0.635336637497, 0.093829996884, 0.652270019054, + 0.093829996884, 0.669203341007, 0.093829996884, 0.686136662960, + 0.093829996884, 0.703069984913, 0.093829996884, 0.720003306866, + 0.093829996884, 0.736936688423, 0.093829996884, 0.753870010376, + 0.093829996884, 0.770803332329, 0.093829996884, 0.787736654282, + 0.093829996884, 0.804669976234, 0.093829996884, 0.821603357792, + 0.093829996884, 0.838536679745, 0.093829996884, 0.855470001698, + 0.093829996884, 0.872403323650, 0.093829996884, 0.889336645603, + 0.093829996884, 0.906270027161, 0.093829996884, 0.923203349113, + 0.093829996884, 0.940136671066, 0.093829996884, 0.957069993019, + 0.093829996884, 0.974003314972, 0.093829996884, 0.990936636925, + 0.093829996884, 1.007869958878, 0.110763333738, -0.008129999973, + 0.110763333738, 0.008803333156, 0.110763333738, 0.025736667216, + 0.110763333738, 0.042670000345, 0.110763333738, 0.059603333473, + 0.110763333738, 0.076536670327, 0.110763333738, 0.093469999731, + 0.110763333738, 0.110403336585, 0.110763333738, 0.127336665988, + 0.110763333738, 0.144270002842, 0.110763333738, 0.161203339696, + 0.110763333738, 0.178136661649, 0.110763333738, 0.195069998503, + 0.110763333738, 0.212003335357, 0.110763333738, 0.228936672211, + 0.110763333738, 0.245869994164, 0.110763333738, 0.262803345919, + 0.110763333738, 0.279736667871, 0.110763333738, 0.296669989824, + 0.110763333738, 0.313603341579, 0.110763333738, 0.330536663532, + 0.110763333738, 0.347469985485, 0.110763333738, 0.364403337240, + 0.110763333738, 0.381336659193, 0.110763333738, 0.398270010948, + 0.110763333738, 0.415203332901, 0.110763333738, 0.432136654854, + 0.110763333738, 0.449070006609, 0.110763333738, 0.466003328562, + 0.110763333738, 0.482936680317, 0.110763333738, 0.499870002270, + 0.110763333738, 0.516803324223, 0.110763333738, 0.533736646175, + 0.110763333738, 0.550670027733, 0.110763333738, 0.567603349686, + 0.110763333738, 0.584536671638, 0.110763333738, 0.601469993591, + 0.110763333738, 0.618403315544, 0.110763333738, 0.635336637497, + 0.110763333738, 0.652270019054, 0.110763333738, 0.669203341007, + 0.110763333738, 0.686136662960, 0.110763333738, 0.703069984913, + 0.110763333738, 0.720003306866, 0.110763333738, 0.736936688423, + 0.110763333738, 0.753870010376, 0.110763333738, 0.770803332329, + 0.110763333738, 0.787736654282, 0.110763333738, 0.804669976234, + 0.110763333738, 0.821603357792, 0.110763333738, 0.838536679745, + 0.110763333738, 0.855470001698, 0.110763333738, 0.872403323650, + 0.110763333738, 0.889336645603, 0.110763333738, 0.906270027161, + 0.110763333738, 0.923203349113, 0.110763333738, 0.940136671066, + 0.110763333738, 0.957069993019, 0.110763333738, 0.974003314972, + 0.110763333738, 0.990936636925, 0.110763333738, 1.007869958878, + 0.127696663141, -0.008129999973, 0.127696663141, 0.008803333156, + 0.127696663141, 0.025736667216, 0.127696663141, 0.042670000345, + 0.127696663141, 0.059603333473, 0.127696663141, 0.076536670327, + 0.127696663141, 0.093469999731, 0.127696663141, 0.110403336585, + 0.127696663141, 0.127336665988, 0.127696663141, 0.144270002842, + 0.127696663141, 0.161203339696, 0.127696663141, 0.178136661649, + 0.127696663141, 0.195069998503, 0.127696663141, 0.212003335357, + 0.127696663141, 0.228936672211, 0.127696663141, 0.245869994164, + 0.127696663141, 0.262803345919, 0.127696663141, 0.279736667871, + 0.127696663141, 0.296669989824, 0.127696663141, 0.313603341579, + 0.127696663141, 0.330536663532, 0.127696663141, 0.347469985485, + 0.127696663141, 0.364403337240, 0.127696663141, 0.381336659193, + 0.127696663141, 0.398270010948, 0.127696663141, 0.415203332901, + 0.127696663141, 0.432136654854, 0.127696663141, 0.449070006609, + 0.127696663141, 0.466003328562, 0.127696663141, 0.482936680317, + 0.127696663141, 0.499870002270, 0.127696663141, 0.516803324223, + 0.127696663141, 0.533736646175, 0.127696663141, 0.550670027733, + 0.127696663141, 0.567603349686, 0.127696663141, 0.584536671638, + 0.127696663141, 0.601469993591, 0.127696663141, 0.618403315544, + 0.127696663141, 0.635336637497, 0.127696663141, 0.652270019054, + 0.127696663141, 0.669203341007, 0.127696663141, 0.686136662960, + 0.127696663141, 0.703069984913, 0.127696663141, 0.720003306866, + 0.127696663141, 0.736936688423, 0.127696663141, 0.753870010376, + 0.127696663141, 0.770803332329, 0.127696663141, 0.787736654282, + 0.127696663141, 0.804669976234, 0.127696663141, 0.821603357792, + 0.127696663141, 0.838536679745, 0.127696663141, 0.855470001698, + 0.127696663141, 0.872403323650, 0.127696663141, 0.889336645603, + 0.127696663141, 0.906270027161, 0.127696663141, 0.923203349113, + 0.127696663141, 0.940136671066, 0.127696663141, 0.957069993019, + 0.127696663141, 0.974003314972, 0.127696663141, 0.990936636925, + 0.127696663141, 1.007869958878, 0.144629999995, -0.008129999973, + 0.144629999995, 0.008803333156, 0.144629999995, 0.025736667216, + 0.144629999995, 0.042670000345, 0.144629999995, 0.059603333473, + 0.144629999995, 0.076536670327, 0.144629999995, 0.093469999731, + 0.144629999995, 0.110403336585, 0.144629999995, 0.127336665988, + 0.144629999995, 0.144270002842, 0.144629999995, 0.161203339696, + 0.144629999995, 0.178136661649, 0.144629999995, 0.195069998503, + 0.144629999995, 0.212003335357, 0.144629999995, 0.228936672211, + 0.144629999995, 0.245869994164, 0.144629999995, 0.262803345919, + 0.144629999995, 0.279736667871, 0.144629999995, 0.296669989824, + 0.144629999995, 0.313603341579, 0.144629999995, 0.330536663532, + 0.144629999995, 0.347469985485, 0.144629999995, 0.364403337240, + 0.144629999995, 0.381336659193, 0.144629999995, 0.398270010948, + 0.144629999995, 0.415203332901, 0.144629999995, 0.432136654854, + 0.144629999995, 0.449070006609, 0.144629999995, 0.466003328562, + 0.144813701510, 0.482945412397, 0.144816666842, 0.499870002270, + 0.144813701510, 0.516794562340, 0.144629999995, 0.533736646175, + 0.144629999995, 0.550670027733, 0.144629999995, 0.567603349686, + 0.144629999995, 0.584536671638, 0.144629999995, 0.601469993591, + 0.144629999995, 0.618403315544, 0.144629999995, 0.635336637497, + 0.144629999995, 0.652270019054, 0.144629999995, 0.669203341007, + 0.144629999995, 0.686136662960, 0.144629999995, 0.703069984913, + 0.144629999995, 0.720003306866, 0.144629999995, 0.736936688423, + 0.144629999995, 0.753870010376, 0.144629999995, 0.770803332329, + 0.144629999995, 0.787736654282, 0.144629999995, 0.804669976234, + 0.144629999995, 0.821603357792, 0.144629999995, 0.838536679745, + 0.144629999995, 0.855470001698, 0.144629999995, 0.872403323650, + 0.144629999995, 0.889336645603, 0.144629999995, 0.906270027161, + 0.144629999995, 0.923203349113, 0.144629999995, 0.940136671066, + 0.144629999995, 0.957069993019, 0.144629999995, 0.974003314972, + 0.144629999995, 0.990936636925, 0.144629999995, 1.007869958878, + 0.161563336849, -0.008129999973, 0.161563336849, 0.008803333156, + 0.161563336849, 0.025736667216, 0.161563336849, 0.042670000345, + 0.161563336849, 0.059603333473, 0.161563336849, 0.076536670327, + 0.161563336849, 0.093469999731, 0.161563336849, 0.110403336585, + 0.161563336849, 0.127336665988, 0.161563336849, 0.144270002842, + 0.161563336849, 0.161203339696, 0.161563336849, 0.178136661649, + 0.161563336849, 0.195069998503, 0.161563336849, 0.212003335357, + 0.161563336849, 0.228936672211, 0.161563336849, 0.245869994164, + 0.161563336849, 0.262803345919, 0.161563336849, 0.279736667871, + 0.161563336849, 0.296669989824, 0.161563336849, 0.313603341579, + 0.161563336849, 0.330536663532, 0.161563336849, 0.347469985485, + 0.161563336849, 0.364403337240, 0.161563336849, 0.381336659193, + 0.161755263805, 0.398327589035, 0.161786675453, 0.415259182453, + 0.161812692881, 0.432186543941, 0.161833107471, 0.449110478163, + 0.161847800016, 0.466031789780, 0.161856666207, 0.482951343060, + 0.161859631538, 0.499870002270, 0.161856666207, 0.516788661480, + 0.161847800016, 0.533708214760, 0.161833107471, 0.550629556179, + 0.161812692881, 0.567553460598, 0.161786675453, 0.584480822086, + 0.161755263805, 0.601412415504, 0.161563336849, 0.618403315544, + 0.161563336849, 0.635336637497, 0.161563336849, 0.652270019054, + 0.161563336849, 0.669203341007, 0.161563336849, 0.686136662960, + 0.161563336849, 0.703069984913, 0.161563336849, 0.720003306866, + 0.161563336849, 0.736936688423, 0.161563336849, 0.753870010376, + 0.161563336849, 0.770803332329, 0.161563336849, 0.787736654282, + 0.161563336849, 0.804669976234, 0.161563336849, 0.821603357792, + 0.161563336849, 0.838536679745, 0.161563336849, 0.855470001698, + 0.161563336849, 0.872403323650, 0.161563336849, 0.889336645603, + 0.161563336849, 0.906270027161, 0.161563336849, 0.923203349113, + 0.161563336849, 0.940136671066, 0.161563336849, 0.957069993019, + 0.161563336849, 0.974003314972, 0.161563336849, 0.990936636925, + 0.161563336849, 1.007869958878, 0.178496673703, -0.008129999973, + 0.178496673703, 0.008803333156, 0.178496673703, 0.025736667216, + 0.178496673703, 0.042670000345, 0.178496673703, 0.059603333473, + 0.178496673703, 0.076536670327, 0.178496673703, 0.093469999731, + 0.178496673703, 0.110403336585, 0.178496673703, 0.127336665988, + 0.178496673703, 0.144270002842, 0.178496673703, 0.161203339696, + 0.178496673703, 0.178136661649, 0.178496673703, 0.195069998503, + 0.178496673703, 0.212003335357, 0.178496673703, 0.228936672211, + 0.178496673703, 0.245869994164, 0.178496673703, 0.262803345919, + 0.178496673703, 0.279736667871, 0.178496673703, 0.296669989824, + 0.178496673703, 0.313603341579, 0.178496673703, 0.330536663532, + 0.178662881255, 0.347548723221, 0.178708851337, 0.364492684603, + 0.178750172257, 0.381430059671, 0.178786605597, 0.398361563683, + 0.178817912936, 0.415287882090, 0.178843840957, 0.432209759951, + 0.178864240646, 0.449128031731, 0.178878918290, 0.466043561697, + 0.178887784481, 0.482957243919, 0.178890734911, 0.499870002270, + 0.178887784481, 0.516782760620, 0.178878918290, 0.533696413040, + 0.178864240646, 0.550611972809, 0.178843840957, 0.567530214787, + 0.178817912936, 0.584452152252, 0.178786605597, 0.601378440857, + 0.178750172257, 0.618309915066, 0.178708851337, 0.635247349739, + 0.178662881255, 0.652191281319, 0.178496673703, 0.669203341007, + 0.178496673703, 0.686136662960, 0.178496673703, 0.703069984913, + 0.178496673703, 0.720003306866, 0.178496673703, 0.736936688423, + 0.178496673703, 0.753870010376, 0.178496673703, 0.770803332329, + 0.178496673703, 0.787736654282, 0.178496673703, 0.804669976234, + 0.178496673703, 0.821603357792, 0.178496673703, 0.838536679745, + 0.178496673703, 0.855470001698, 0.178496673703, 0.872403323650, + 0.178496673703, 0.889336645603, 0.178496673703, 0.906270027161, + 0.178496673703, 0.923203349113, 0.178496673703, 0.940136671066, + 0.178496673703, 0.957069993019, 0.178496673703, 0.974003314972, + 0.178496673703, 0.990936636925, 0.178496673703, 1.007869958878, + 0.195429995656, -0.008129999973, 0.195429995656, 0.008803333156, + 0.195429995656, 0.025736667216, 0.195429995656, 0.042670000345, + 0.195429995656, 0.059603333473, 0.195429995656, 0.076536670327, + 0.195429995656, 0.093469999731, 0.195429995656, 0.110403336585, + 0.195429995656, 0.127336665988, 0.195429995656, 0.144270002842, + 0.195429995656, 0.161203339696, 0.195429995656, 0.178136661649, + 0.195429995656, 0.195069998503, 0.195429995656, 0.212003335357, + 0.195429995656, 0.228936672211, 0.195429995656, 0.245869994164, + 0.195429995656, 0.262803345919, 0.195429995656, 0.279736667871, + 0.195429995656, 0.296669989824, 0.195429995656, 0.313603341579, + 0.195633605123, 0.330649763346, 0.195683375001, 0.347596675158, + 0.195728912950, 0.364536195993, 0.195769920945, 0.381468862295, + 0.195806145668, 0.398395389318, 0.195837303996, 0.415316462517, + 0.195863157511, 0.432232916355, 0.195975631475, 0.449160933495, + 0.195999100804, 0.466066569090, 0.196013256907, 0.482969075441, + 0.196017995477, 0.499870002270, 0.196013256907, 0.516770958900, + 0.195999100804, 0.533673405647, 0.195975631475, 0.550579071045, + 0.195863157511, 0.567507088184, 0.195837303996, 0.584423542023, + 0.195806145668, 0.601344645023, 0.195769920945, 0.618271112442, + 0.195728912950, 0.635203838348, 0.195683375001, 0.652143299580, + 0.195633605123, 0.669090211391, 0.195429995656, 0.686136662960, + 0.195429995656, 0.703069984913, 0.195429995656, 0.720003306866, + 0.195429995656, 0.736936688423, 0.195429995656, 0.753870010376, + 0.195429995656, 0.770803332329, 0.195429995656, 0.787736654282, + 0.195429995656, 0.804669976234, 0.195429995656, 0.821603357792, + 0.195429995656, 0.838536679745, 0.195429995656, 0.855470001698, + 0.195429995656, 0.872403323650, 0.195429995656, 0.889336645603, + 0.195429995656, 0.906270027161, 0.195429995656, 0.923203349113, + 0.195429995656, 0.940136671066, 0.195429995656, 0.957069993019, + 0.195429995656, 0.974003314972, 0.195429995656, 0.990936636925, + 0.195429995656, 1.007869958878, 0.212363332510, -0.008129999973, + 0.212363332510, 0.008803333156, 0.212363332510, 0.025736667216, + 0.212363332510, 0.042670000345, 0.212363332510, 0.059603333473, + 0.212363332510, 0.076536670327, 0.212363332510, 0.093469999731, + 0.212363332510, 0.110403336585, 0.212363332510, 0.127336665988, + 0.212363332510, 0.144270002842, 0.212363332510, 0.161203339696, + 0.212363332510, 0.178136661649, 0.212363332510, 0.195069998503, + 0.212363332510, 0.212003335357, 0.212363332510, 0.228936672211, + 0.212363332510, 0.245869994164, 0.212363332510, 0.262803345919, + 0.212363332510, 0.279736667871, 0.212533727288, 0.296790271997, + 0.212590157986, 0.313750088215, 0.212643086910, 0.330701231956, + 0.212692216039, 0.347644120455, 0.212737247348, 0.364579290152, + 0.212777897716, 0.381507366896, 0.212914198637, 0.398464411497, + 0.212963789701, 0.415379941463, 0.213005021214, 0.432287663221, + 0.213037505746, 0.449188977480, 0.213060960174, 0.466085404158, + 0.213075116277, 0.482978522778, 0.213079854846, 0.499870002270, + 0.213075116277, 0.516761481762, 0.213060960174, 0.533654570580, + 0.213037505746, 0.550551056862, 0.213005021214, 0.567452371120, + 0.212963789701, 0.584360063076, 0.212914198637, 0.601275563240, + 0.212777897716, 0.618232607841, 0.212737247348, 0.635160684586, + 0.212692216039, 0.652095913887, 0.212643086910, 0.669038772583, + 0.212590157986, 0.685989916325, 0.212533727288, 0.702949702740, + 0.212363332510, 0.720003306866, 0.212363332510, 0.736936688423, + 0.212363332510, 0.753870010376, 0.212363332510, 0.770803332329, + 0.212363332510, 0.787736654282, 0.212363332510, 0.804669976234, + 0.212363332510, 0.821603357792, 0.212363332510, 0.838536679745, + 0.212363332510, 0.855470001698, 0.212363332510, 0.872403323650, + 0.212363332510, 0.889336645603, 0.212363332510, 0.906270027161, + 0.212363332510, 0.923203349113, 0.212363332510, 0.940136671066, + 0.212363332510, 0.957069993019, 0.212363332510, 0.974003314972, + 0.212363332510, 0.990936636925, 0.212363332510, 1.007869958878, + 0.229296669364, -0.008129999973, 0.229296669364, 0.008803333156, + 0.229296669364, 0.025736667216, 0.229296669364, 0.042670000345, + 0.229296669364, 0.059603333473, 0.229296669364, 0.076536670327, + 0.229296669364, 0.093469999731, 0.229296669364, 0.110403336585, + 0.229296669364, 0.127336665988, 0.229296669364, 0.144270002842, + 0.229296669364, 0.161203339696, 0.229296669364, 0.178136661649, + 0.229296669364, 0.195069998503, 0.229296669364, 0.212003335357, + 0.229296669364, 0.228936672211, 0.229296669364, 0.245869994164, + 0.229296669364, 0.262803345919, 0.229475349188, 0.279881834984, + 0.229533702135, 0.296847790480, 0.229589030147, 0.313804328442, + 0.229641035199, 0.330751895905, 0.229689434171, 0.347690939903, + 0.229836240411, 0.364673107862, 0.229900613427, 0.381600886583, + 0.229957684875, 0.398517876863, 0.230122134089, 0.415461301804, + 0.230183720589, 0.432358443737, 0.230232328176, 0.449245423079, + 0.230267450213, 0.466124683619, 0.230288669467, 0.482998669147, + 0.230295777321, 0.499870002270, 0.230288669467, 0.516741335392, + 0.230267450213, 0.533615291119, 0.230232328176, 0.550494551659, + 0.230183720589, 0.567381560802, 0.230122134089, 0.584278702736, + 0.229957684875, 0.601222097874, 0.229900613427, 0.618139088154, + 0.229836240411, 0.635066866875, 0.229689434171, 0.652049064636, + 0.229641035199, 0.668988108635, 0.229589030147, 0.685935676098, + 0.229533702135, 0.702892243862, 0.229475349188, 0.719858169556, + 0.229296669364, 0.736936688423, 0.229296669364, 0.753870010376, + 0.229296669364, 0.770803332329, 0.229296669364, 0.787736654282, + 0.229296669364, 0.804669976234, 0.229296669364, 0.821603357792, + 0.229296669364, 0.838536679745, 0.229296669364, 0.855470001698, + 0.229296669364, 0.872403323650, 0.229296669364, 0.889336645603, + 0.229296669364, 0.906270027161, 0.229296669364, 0.923203349113, + 0.229296669364, 0.940136671066, 0.229296669364, 0.957069993019, + 0.229296669364, 0.974003314972, 0.229296669364, 0.990936636925, + 0.229296669364, 1.007869958878, 0.246230006218, -0.008129999973, + 0.246230006218, 0.008803333156, 0.246230006218, 0.025736667216, + 0.246230006218, 0.042670000345, 0.246230006218, 0.059603333473, + 0.246230006218, 0.076536670327, 0.246230006218, 0.093469999731, + 0.246230006218, 0.110403336585, 0.246230006218, 0.127336665988, + 0.246230006218, 0.144270002842, 0.246230006218, 0.161203339696, + 0.246230006218, 0.178136661649, 0.246230006218, 0.195069998503, + 0.246230006218, 0.212003335357, 0.246230006218, 0.228936672211, + 0.246230006218, 0.245869994164, 0.246406152844, 0.262967735529, + 0.246465608478, 0.279940843582, 0.246522501111, 0.296903997660, + 0.246576577425, 0.313857495785, 0.246716052294, 0.330860704184, + 0.246792122722, 0.347807288170, 0.246862217784, 0.364740520716, + 0.247048705816, 0.381718724966, 0.247133493423, 0.398631393909, + 0.247206896544, 0.415528953075, 0.247268170118, 0.432413518429, + 0.246979996562, 0.449220001698, 0.246979996562, 0.466103345156, + 0.246979996562, 0.482986658812, 0.246979996562, 0.499870002270, + 0.246979996562, 0.516753315926, 0.246979996562, 0.533636689186, + 0.246979996562, 0.550520002842, 0.247268170118, 0.567326486111, + 0.247206896544, 0.584211051464, 0.247133493423, 0.601108610630, + 0.247048705816, 0.618021249771, 0.246862217784, 0.634999454021, + 0.246792122722, 0.651932716370, 0.246716052294, 0.668879270554, + 0.246576577425, 0.685882508755, 0.246522501111, 0.702835977077, + 0.246465608478, 0.719799160957, 0.246406152844, 0.736772239208, + 0.246230006218, 0.753870010376, 0.246230006218, 0.770803332329, + 0.246230006218, 0.787736654282, 0.246230006218, 0.804669976234, + 0.246230006218, 0.821603357792, 0.246230006218, 0.838536679745, + 0.246230006218, 0.855470001698, 0.246230006218, 0.872403323650, + 0.246230006218, 0.889336645603, 0.246230006218, 0.906270027161, + 0.246230006218, 0.923203349113, 0.246230006218, 0.940136671066, + 0.246230006218, 0.957069993019, 0.246230006218, 0.974003314972, + 0.246230006218, 0.990936636925, 0.246230006218, 1.007869958878, + 0.263163328171, -0.008129999973, 0.263163328171, 0.008803333156, + 0.263163328171, 0.025736667216, 0.263163328171, 0.042670000345, + 0.263163328171, 0.059603333473, 0.263163328171, 0.076536670327, + 0.263163328171, 0.093469999731, 0.263163328171, 0.110403336585, + 0.263163328171, 0.127336665988, 0.263163328171, 0.144270002842, + 0.263163328171, 0.161203339696, 0.263163328171, 0.178136661649, + 0.263163328171, 0.195069998503, 0.263163328171, 0.212003335357, + 0.263163328171, 0.228936672211, 0.263327747583, 0.246046155691, + 0.263387411833, 0.263027429581, 0.263444989920, 0.279998213053, + 0.263500243425, 0.296958774328, 0.263646632433, 0.313983052969, + 0.263726234436, 0.330938756466, 0.263909459114, 0.347949653864, + 0.264012753963, 0.364888727665, 0.264106750488, 0.381808370352, + 0.263863325119, 0.398570001125, 0.263863325119, 0.415453344584, + 0.263863325119, 0.432336658239, 0.263863325119, 0.449220001698, + 0.263863325119, 0.466103345156, 0.264124900103, 0.483005344868, + 0.264131397009, 0.499870002270, 0.264124900103, 0.516734659672, + 0.263863325119, 0.533636689186, 0.263863325119, 0.550520002842, + 0.263863325119, 0.567403316498, 0.263863325119, 0.584286689758, + 0.263863325119, 0.601170003414, 0.264106750488, 0.617931604385, + 0.264012753963, 0.634851276875, 0.263909459114, 0.651790320873, + 0.263726234436, 0.668801248074, 0.263646632433, 0.685756921768, + 0.263500243425, 0.702781200409, 0.263444989920, 0.719741761684, + 0.263387411833, 0.736712574959, 0.263327747583, 0.753693819046, + 0.263163328171, 0.770803332329, 0.263163328171, 0.787736654282, + 0.263163328171, 0.804669976234, 0.263163328171, 0.821603357792, + 0.263163328171, 0.838536679745, 0.263163328171, 0.855470001698, + 0.263163328171, 0.872403323650, 0.263163328171, 0.889336645603, + 0.263163328171, 0.906270027161, 0.263163328171, 0.923203349113, + 0.263163328171, 0.940136671066, 0.263163328171, 0.957069993019, + 0.263163328171, 0.974003314972, 0.263163328171, 0.990936636925, + 0.263163328171, 1.007869958878, 0.280096679926, -0.008129999973, + 0.280096679926, 0.008803333156, 0.280096679926, 0.025736667216, + 0.280096679926, 0.042670000345, 0.280096679926, 0.059603333473, + 0.280096679926, 0.076536670327, 0.280096679926, 0.093469999731, + 0.280096679926, 0.110403336585, 0.280096679926, 0.127336665988, + 0.280096679926, 0.144270002842, 0.280096679926, 0.161203339696, + 0.280096679926, 0.178136661649, 0.280096679926, 0.195069998503, + 0.280096679926, 0.212003335357, 0.280241847038, 0.229115337133, + 0.280300855637, 0.246105611324, 0.280358195305, 0.263085007668, + 0.280413687229, 0.280053704977, 0.280559331179, 0.297097057104, + 0.280640959740, 0.314063906670, 0.280834257603, 0.331104040146, + 0.280943304300, 0.348056137562, 0.280746668577, 0.364803344011, + 0.280746668577, 0.381686657667, 0.280746668577, 0.398570001125, + 0.281007736921, 0.415553748608, 0.281063139439, 0.432434052229, + 0.281107157469, 0.449303179979, 0.281139165163, 0.466163724661, + 0.281158566475, 0.483018338680, 0.281165063381, 0.499870002270, + 0.281158566475, 0.516721665859, 0.281139165163, 0.533576309681, + 0.281107157469, 0.550436794758, 0.281063139439, 0.567305982113, + 0.281007736921, 0.584186255932, 0.280746668577, 0.601170003414, + 0.280746668577, 0.618053317070, 0.280746668577, 0.634936690331, + 0.280943304300, 0.651683866978, 0.280834257603, 0.668635964394, + 0.280640959740, 0.685676097870, 0.280559331179, 0.702642917633, + 0.280413687229, 0.719686329365, 0.280358195305, 0.736654996872, + 0.280300855637, 0.753634393215, 0.280241847038, 0.770624637604, + 0.280096679926, 0.787736654282, 0.280096679926, 0.804669976234, + 0.280096679926, 0.821603357792, 0.280096679926, 0.838536679745, + 0.280096679926, 0.855470001698, 0.280096679926, 0.872403323650, + 0.280096679926, 0.889336645603, 0.280096679926, 0.906270027161, + 0.280096679926, 0.923203349113, 0.280096679926, 0.940136671066, + 0.280096679926, 0.957069993019, 0.280096679926, 0.974003314972, + 0.280096679926, 0.990936636925, 0.280096679926, 1.007869958878, + 0.297030001879, -0.008129999973, 0.297030001879, 0.008803333156, + 0.297030001879, 0.025736667216, 0.297030001879, 0.042670000345, + 0.297030001879, 0.059603333473, 0.297030001879, 0.076536670327, + 0.297030001879, 0.093469999731, 0.297030001879, 0.110403336585, + 0.297030001879, 0.127336665988, 0.297030001879, 0.144270002842, + 0.297030001879, 0.161203339696, 0.297030001879, 0.178136661649, + 0.297030001879, 0.195069998503, 0.297150284052, 0.212173715234, + 0.297207772732, 0.229173704982, 0.297264009714, 0.246162503958, + 0.297318786383, 0.263140231371, 0.297457069159, 0.280199319124, + 0.297539114952, 0.297179132700, 0.297731757164, 0.314246594906, + 0.297844111919, 0.331215083599, 0.297630012035, 0.347920000553, + 0.297630012035, 0.364803344011, 0.297876596451, 0.381830513477, + 0.297951072454, 0.398730546236, 0.298016220331, 0.415614247322, + 0.298051029444, 0.432476997375, 0.298090815544, 0.449335187674, + 0.298119783401, 0.466184973717, 0.298137426376, 0.483028948307, + 0.298143327236, 0.499870002270, 0.298137426376, 0.516711056232, + 0.298119783401, 0.533555030823, 0.298090815544, 0.550404787064, + 0.298051029444, 0.567263007164, 0.298016220331, 0.584125757217, + 0.297951072454, 0.601009488106, 0.297876596451, 0.617909491062, + 0.297630012035, 0.634936690331, 0.297630012035, 0.651820003986, + 0.297844111919, 0.668524920940, 0.297731757164, 0.685493409634, + 0.297539114952, 0.702560901642, 0.297457069159, 0.719540655613, + 0.297318786383, 0.736599743366, 0.297264009714, 0.753577470779, + 0.297207772732, 0.770566284657, 0.297150284052, 0.787566304207, + 0.297030001879, 0.804669976234, 0.297030001879, 0.821603357792, + 0.297030001879, 0.838536679745, 0.297030001879, 0.855470001698, + 0.297030001879, 0.872403323650, 0.297030001879, 0.889336645603, + 0.297030001879, 0.906270027161, 0.297030001879, 0.923203349113, + 0.297030001879, 0.940136671066, 0.297030001879, 0.957069993019, + 0.297030001879, 0.974003314972, 0.297030001879, 0.990936636925, + 0.297030001879, 1.007869958878, 0.313963323832, -0.008129999973, + 0.313963323832, 0.008803333156, 0.313963323832, 0.025736667216, + 0.313963323832, 0.042670000345, 0.313963323832, 0.059603333473, + 0.313963323832, 0.076536670327, 0.313963323832, 0.093469999731, + 0.313963323832, 0.110403336585, 0.313963323832, 0.127336665988, + 0.313963323832, 0.144270002842, 0.313963323832, 0.161203339696, + 0.313963323832, 0.178136661649, 0.313963323832, 0.195069998503, + 0.314110100269, 0.212230160832, 0.314164340496, 0.229229032993, + 0.314217478037, 0.246216565371, 0.314343065023, 0.263286620378, + 0.314423888922, 0.280280977488, 0.314606606960, 0.297371745110, + 0.314719617367, 0.314359635115, 0.314513325691, 0.331036657095, + 0.314513325691, 0.347920000553, 0.314781129360, 0.364998072386, + 0.314861863852, 0.381908446550, 0.314914792776, 0.398788988590, + 0.314973056316, 0.415662288666, 0.315022379160, 0.432521790266, + 0.315061897039, 0.449369609356, 0.314936816692, 0.466180324554, + 0.314940333366, 0.483025491238, 0.314941525459, 0.499870002270, + 0.314940333366, 0.516714513302, 0.314936816692, 0.533559679985, + 0.315061897039, 0.550370395184, 0.315022379160, 0.567218244076, + 0.314973056316, 0.584077715874, 0.314914792776, 0.600951015949, + 0.314861863852, 0.617831528187, 0.314781129360, 0.634741902351, + 0.314513325691, 0.651820003986, 0.314513325691, 0.668703317642, + 0.314719617367, 0.685380399227, 0.314606606960, 0.702368259430, + 0.314423888922, 0.719459056854, 0.314343065023, 0.736453354359, + 0.314217478037, 0.753523409367, 0.314164340496, 0.770510971546, + 0.314110100269, 0.787509858608, 0.313963323832, 0.804669976234, + 0.313963323832, 0.821603357792, 0.313963323832, 0.838536679745, + 0.313963323832, 0.855470001698, 0.313963323832, 0.872403323650, + 0.313963323832, 0.889336645603, 0.313963323832, 0.906270027161, + 0.313963323832, 0.923203349113, 0.313963323832, 0.940136671066, + 0.313963323832, 0.957069993019, 0.313963323832, 0.974003314972, + 0.313963323832, 0.990936636925, 0.313963323832, 1.007869958878, + 0.330896675587, -0.008129999973, 0.330896675587, 0.008803333156, + 0.330896675587, 0.025736667216, 0.330896675587, 0.042670000345, + 0.330896675587, 0.059603333473, 0.330896675587, 0.076536670327, + 0.330896675587, 0.093469999731, 0.330896675587, 0.110403336585, + 0.330896675587, 0.127336665988, 0.330896675587, 0.144270002842, + 0.330896675587, 0.161203339696, 0.330896675587, 0.178136661649, + 0.331009775400, 0.195273593068, 0.331061214209, 0.212283074856, + 0.331111907959, 0.229281038046, 0.331220716238, 0.246356055140, + 0.331298738718, 0.263366252184, 0.331464052200, 0.280474275351, + 0.331575095654, 0.297484099865, 0.331396669149, 0.314153343439, + 0.331396669149, 0.331036657095, 0.331659376621, 0.348156452179, + 0.331743776798, 0.365081012249, 0.331799954176, 0.381968975067, + 0.331864506006, 0.398850709200, 0.331781655550, 0.415645837784, + 0.331791371107, 0.432494550943, 0.331799179316, 0.449340760708, + 0.331804931164, 0.466184973717, 0.331808447838, 0.483027845621, + 0.331809639931, 0.499870002270, 0.331808447838, 0.516712129116, + 0.331804931164, 0.533555030823, 0.331799179316, 0.550399243832, + 0.331791371107, 0.567245423794, 0.331781655550, 0.584094166756, + 0.331864506006, 0.600889265537, 0.331799954176, 0.617771029472, + 0.331743776798, 0.634658992290, 0.331659376621, 0.651583552361, + 0.331396669149, 0.668703317642, 0.331396669149, 0.685586690903, + 0.331575095654, 0.702255904675, 0.331464052200, 0.719265758991, + 0.331298738718, 0.736373782158, 0.331220716238, 0.753383934498, + 0.331111907959, 0.770458936691, 0.331061214209, 0.787456929684, + 0.331009775400, 0.804466426373, 0.330896675587, 0.821603357792, + 0.330896675587, 0.838536679745, 0.330896675587, 0.855470001698, + 0.330896675587, 0.872403323650, 0.330896675587, 0.889336645603, + 0.330896675587, 0.906270027161, 0.330896675587, 0.923203349113, + 0.330896675587, 0.940136671066, 0.330896675587, 0.957069993019, + 0.330896675587, 0.974003314972, 0.330896675587, 0.990936636925, + 0.330896675587, 1.007869958878, 0.347829997540, -0.008129999973, + 0.347829997540, 0.008803333156, 0.347829997540, 0.025736667216, + 0.347829997540, 0.042670000345, 0.347829997540, 0.059603333473, + 0.347829997540, 0.076536670327, 0.347829997540, 0.093469999731, + 0.347829997540, 0.110403336585, 0.347829997540, 0.127336665988, + 0.347829997540, 0.144270002842, 0.347829997540, 0.161203339696, + 0.347908735275, 0.178302869201, 0.347956687212, 0.195323377848, + 0.348004102707, 0.212332218885, 0.348050922155, 0.229329437017, + 0.348167270422, 0.246432125568, 0.348309665918, 0.263549476862, + 0.348416149616, 0.280583322048, 0.348280012608, 0.297269999981, + 0.348280012608, 0.314153343439, 0.348516434431, 0.331299364567, + 0.348601579666, 0.348241597414, 0.348660558462, 0.365141600370, + 0.348728805780, 0.382035732269, 0.348634243011, 0.398806154728, + 0.348645359278, 0.415656298399, 0.348654896021, 0.432503283024, + 0.348756641150, 0.449378877878, 0.348776608706, 0.466213703156, + 0.348788857460, 0.483043193817, 0.348792999983, 0.499870002270, + 0.348788857460, 0.516696810722, 0.348776608706, 0.533526301384, + 0.348756641150, 0.550361096859, 0.348654896021, 0.567236721516, + 0.348645359278, 0.584083676338, 0.348634243011, 0.600933849812, + 0.348728805780, 0.617704272270, 0.348660558462, 0.634598374367, + 0.348601579666, 0.651498436928, 0.348516434431, 0.668440639973, + 0.348280012608, 0.685586690903, 0.348280012608, 0.702470004559, + 0.348416149616, 0.719156682491, 0.348309665918, 0.736190557480, + 0.348167270422, 0.753307878971, 0.348050922155, 0.770410597324, + 0.348004102707, 0.787407815456, 0.347956687212, 0.804416596889, + 0.347908735275, 0.821437120438, 0.347829997540, 0.838536679745, + 0.347829997540, 0.855470001698, 0.347829997540, 0.872403323650, + 0.347829997540, 0.889336645603, 0.347829997540, 0.906270027161, + 0.347829997540, 0.923203349113, 0.347829997540, 0.940136671066, + 0.347829997540, 0.957069993019, 0.347829997540, 0.974003314972, + 0.347829997540, 0.990936636925, 0.347829997540, 1.007869958878, + 0.364763319492, -0.008129999973, 0.364763319492, 0.008803333156, + 0.364763319492, 0.025736667216, 0.364763319492, 0.042670000345, + 0.364763319492, 0.059603333473, 0.364763319492, 0.076536670327, + 0.364763319492, 0.093469999731, 0.364763319492, 0.110403336585, + 0.364763319492, 0.127336665988, 0.364763319492, 0.144270002842, + 0.364763319492, 0.161203339696, 0.364852666855, 0.178348839283, + 0.364896178246, 0.195368915796, 0.364939302206, 0.212377250195, + 0.365033119917, 0.229476243258, 0.365100532770, 0.246502220631, + 0.365248709917, 0.263652771711, 0.365163326263, 0.280386656523, + 0.365163326263, 0.297269999981, 0.365358084440, 0.314421117306, + 0.365441024303, 0.331383764744, 0.365501612425, 0.348300576210, + 0.365570634604, 0.365210622549, 0.365481764078, 0.381965279579, + 0.365493714809, 0.398817777634, 0.365590542555, 0.415720343590, + 0.365623027086, 0.432566523552, 0.365649610758, 0.449402362108, + 0.365396708250, 0.466161668301, 0.365377515554, 0.483013451099, + 0.365371048450, 0.499870002270, 0.365377515554, 0.516726553440, + 0.365396708250, 0.533578336239, 0.365649610758, 0.550337672234, + 0.365623027086, 0.567173480988, 0.365590542555, 0.584019660950, + 0.365493714809, 0.600922226906, 0.365481764078, 0.617774724960, + 0.365570634604, 0.634529352188, 0.365501612425, 0.651439428329, + 0.365441024303, 0.668356239796, 0.365358084440, 0.685318887234, + 0.365163326263, 0.702470004559, 0.365163326263, 0.719353318214, + 0.365248709917, 0.736087262630, 0.365100532770, 0.753237783909, + 0.365033119917, 0.770263731480, 0.364939302206, 0.787362754345, + 0.364896178246, 0.804371118546, 0.364852666855, 0.821391165257, + 0.364763319492, 0.838536679745, 0.364763319492, 0.855470001698, + 0.364763319492, 0.872403323650, 0.364763319492, 0.889336645603, + 0.364763319492, 0.906270027161, 0.364763319492, 0.923203349113, + 0.364763319492, 0.940136671066, 0.364763319492, 0.957069993019, + 0.364763319492, 0.974003314972, 0.364763319492, 0.990936636925, + 0.364763319492, 1.007869958878, 0.381696671247, -0.008129999973, + 0.381696671247, 0.008803333156, 0.381696671247, 0.025736667216, + 0.381696671247, 0.042670000345, 0.381696671247, 0.059603333473, + 0.381696671247, 0.076536670327, 0.381696671247, 0.093469999731, + 0.381696671247, 0.110403336585, 0.381696671247, 0.127336665988, + 0.381696671247, 0.144270002842, 0.381696671247, 0.161203339696, + 0.381790071726, 0.178390175104, 0.381828874350, 0.195409923792, + 0.381867378950, 0.212417900562, 0.381960898638, 0.229540601373, + 0.382078737020, 0.246688708663, 0.382168382406, 0.263746738434, + 0.382046669722, 0.280386656523, 0.382190525532, 0.297516614199, + 0.382268458605, 0.314501851797, 0.382328987122, 0.331439971924, + 0.382395744324, 0.348368823528, 0.382325291634, 0.365121752024, + 0.382337421179, 0.381977409124, 0.382432907820, 0.398901075125, + 0.382468760014, 0.415754824877, 0.382234096527, 0.432443767786, + 0.382193356752, 0.449282854795, 0.382162719965, 0.466136485338, + 0.382143646479, 0.483000516891, 0.382435292006, 0.499870002270, + 0.382143646479, 0.516739487648, 0.382162719965, 0.533603489399, + 0.382193356752, 0.550457119942, 0.382234096527, 0.567296206951, + 0.382468760014, 0.583985149860, 0.382432907820, 0.600838899612, + 0.382337421179, 0.617762565613, 0.382325291634, 0.634618222713, + 0.382395744324, 0.651371181011, 0.382328987122, 0.668300032616, + 0.382268458605, 0.685238122940, 0.382190525532, 0.702223420143, + 0.382046669722, 0.719353318214, 0.382168382406, 0.735993266106, + 0.382078737020, 0.753051280975, 0.381960898638, 0.770199418068, + 0.381867378950, 0.787322103977, 0.381828874350, 0.804330050945, + 0.381790071726, 0.821349799633, 0.381696671247, 0.838536679745, + 0.381696671247, 0.855470001698, 0.381696671247, 0.872403323650, + 0.381696671247, 0.889336645603, 0.381696671247, 0.906270027161, + 0.381696671247, 0.923203349113, 0.381696671247, 0.940136671066, + 0.381696671247, 0.957069993019, 0.381696671247, 0.974003314972, + 0.381696671247, 0.990936636925, 0.381696671247, 1.007869958878, + 0.398629993200, -0.008129999973, 0.398629993200, 0.008803333156, + 0.398629993200, 0.025736667216, 0.398629993200, 0.042670000345, + 0.398629993200, 0.059603333473, 0.398629993200, 0.076536670327, + 0.398629993200, 0.093469999731, 0.398629993200, 0.110403336585, + 0.398629993200, 0.127336665988, 0.398629993200, 0.144270002842, + 0.398687571287, 0.161395266652, 0.398721545935, 0.178426608443, + 0.398755371571, 0.195446148515, 0.398824423552, 0.212554186583, + 0.398877888918, 0.229597687721, 0.398991405964, 0.246773496270, + 0.398930013180, 0.263503342867, 0.398930013180, 0.280386656523, + 0.399090528488, 0.297591090202, 0.399148970842, 0.314554810524, + 0.399210721254, 0.331504523754, 0.399166166782, 0.348274230957, + 0.399177789688, 0.365133702755, 0.399261057377, 0.382072925568, + 0.399297624826, 0.398937612772, 0.399070948362, 0.415570765734, + 0.399024069309, 0.432399392128, 0.399292171001, 0.449401080608, + 0.399330347776, 0.466236799955, 0.399354428053, 0.483057409525, + 0.399362653494, 0.499870002270, 0.399354428053, 0.516682624817, + 0.399330347776, 0.533503234386, 0.399292171001, 0.550338923931, + 0.399024069309, 0.567340612411, 0.399070948362, 0.584169209003, + 0.399297624826, 0.600802361965, 0.399261057377, 0.617667078972, + 0.399177789688, 0.634606301785, 0.399166166782, 0.651465773582, + 0.399210721254, 0.668235480785, 0.399148970842, 0.685185194016, + 0.399090528488, 0.702148914337, 0.398930013180, 0.719353318214, + 0.398930013180, 0.736236691475, 0.398991405964, 0.752966523170, + 0.398877888918, 0.770142316818, 0.398824423552, 0.787185788155, + 0.398755371571, 0.804293870926, 0.398721545935, 0.821313381195, + 0.398687571287, 0.838344752789, 0.398629993200, 0.855470001698, + 0.398629993200, 0.872403323650, 0.398629993200, 0.889336645603, + 0.398629993200, 0.906270027161, 0.398629993200, 0.923203349113, + 0.398629993200, 0.940136671066, 0.398629993200, 0.957069993019, + 0.398629993200, 0.974003314972, 0.398629993200, 0.990936636925, + 0.398629993200, 1.007869958878, 0.415563344955, -0.008129999973, + 0.415563344955, 0.008803333156, 0.415563344955, 0.025736667216, + 0.415563344955, 0.042670000345, 0.415563344955, 0.059603333473, + 0.415563344955, 0.076536670327, 0.415563344955, 0.093469999731, + 0.415563344955, 0.110403336585, 0.415563344955, 0.127336665988, + 0.415563344955, 0.144270002842, 0.415619164705, 0.161426678300, + 0.415647864342, 0.178457900882, 0.415676474571, 0.195477306843, + 0.415739923716, 0.212603792548, 0.415821284056, 0.229762136936, + 0.415888965130, 0.246846899390, 0.415813326836, 0.263503342867, + 0.415913730860, 0.280647724867, 0.415974259377, 0.297656208277, + 0.416022300720, 0.314613074064, 0.416005820036, 0.331421643496, + 0.416016310453, 0.348285347223, 0.416080325842, 0.365230530500, + 0.416114836931, 0.382108747959, 0.415930777788, 0.398710936308, + 0.415882587433, 0.415522605181, 0.416140437126, 0.432598352432, + 0.416187912226, 0.449444741011, 0.416185885668, 0.466252356768, + 0.416202843189, 0.483064562082, 0.416208714247, 0.499870002270, + 0.416202843189, 0.516675412655, 0.416185885668, 0.533487677574, + 0.416187912226, 0.550295233727, 0.416140437126, 0.567141652107, + 0.415882587433, 0.584217429161, 0.415930777788, 0.601029038429, + 0.416114836931, 0.617631256580, 0.416080325842, 0.634509444237, + 0.416016310453, 0.651454627514, 0.416005820036, 0.668318331242, + 0.416022300720, 0.685126960278, 0.415974259377, 0.702083766460, + 0.415913730860, 0.719092249870, 0.415813326836, 0.736236691475, + 0.415888965130, 0.752893090248, 0.415821284056, 0.769977867603, + 0.415739923716, 0.787136197090, 0.415676474571, 0.804262697697, + 0.415647864342, 0.821282088757, 0.415619164705, 0.838313341141, + 0.415563344955, 0.855470001698, 0.415563344955, 0.872403323650, + 0.415563344955, 0.889336645603, 0.415563344955, 0.906270027161, + 0.415563344955, 0.923203349113, 0.415563344955, 0.940136671066, + 0.415563344955, 0.957069993019, 0.415563344955, 0.974003314972, + 0.415563344955, 0.990936636925, 0.415563344955, 1.007869958878, + 0.432496666908, -0.008129999973, 0.432496666908, 0.008803333156, + 0.432496666908, 0.025736667216, 0.432496666908, 0.042670000345, + 0.432496666908, 0.059603333473, 0.432496666908, 0.076536670327, + 0.432496666908, 0.093469999731, 0.432496666908, 0.110403336585, + 0.432496666908, 0.127336665988, 0.432496666908, 0.144270002842, + 0.432546526194, 0.161452680826, 0.432569772005, 0.178483843803, + 0.432592928410, 0.195503160357, 0.432647645473, 0.212645024061, + 0.432718425989, 0.229823723435, 0.432773500681, 0.246908172965, + 0.432696670294, 0.263503342867, 0.432794034481, 0.280703127384, + 0.432837009430, 0.297691017389, 0.432881772518, 0.314662396908, + 0.432854533195, 0.331431359053, 0.432863295078, 0.348294883966, + 0.432926505804, 0.365263044834, 0.432803779840, 0.381874084473, + 0.432759374380, 0.398664057255, 0.432958364487, 0.415780454874, + 0.433007895947, 0.432647883892, 0.433012962341, 0.449457228184, + 0.433037996292, 0.466273993254, 0.432919263840, 0.483042299747, + 0.432913988829, 0.499870002270, 0.432919263840, 0.516697704792, + 0.433037996292, 0.533465981483, 0.433012962341, 0.550282776356, + 0.433007895947, 0.567092120647, 0.432958364487, 0.583959579468, + 0.432759374380, 0.601075947285, 0.432803779840, 0.617865920067, + 0.432926505804, 0.634476959705, 0.432863295078, 0.651445090771, + 0.432854533195, 0.668308615685, 0.432881772518, 0.685077607632, + 0.432837009430, 0.702048957348, 0.432794034481, 0.719036877155, + 0.432696670294, 0.736236691475, 0.432773500681, 0.752831816673, + 0.432718425989, 0.769916296005, 0.432647645473, 0.787094950676, + 0.432592928410, 0.804236829281, 0.432569772005, 0.821256160736, + 0.432546526194, 0.838287293911, 0.432496666908, 0.855470001698, + 0.432496666908, 0.872403323650, 0.432496666908, 0.889336645603, + 0.432496666908, 0.906270027161, 0.432496666908, 0.923203349113, + 0.432496666908, 0.940136671066, 0.432496666908, 0.957069993019, + 0.432496666908, 0.974003314972, 0.432496666908, 0.990936636925, + 0.432496666908, 1.007869958878, 0.449429988861, -0.008129999973, + 0.449429988861, 0.008803333156, 0.449429988861, 0.025736667216, + 0.449429988861, 0.042670000345, 0.449429988861, 0.059603333473, + 0.449429988861, 0.076536670327, 0.449429988861, 0.093469999731, + 0.449429988861, 0.110403336585, 0.449429988861, 0.127336665988, + 0.449429988861, 0.144270002842, 0.449470460415, 0.161473110318, + 0.449488043785, 0.178504243493, 0.449520945549, 0.195615619421, + 0.449548959732, 0.212677508593, 0.449605435133, 0.229872331023, + 0.449580013752, 0.246619999409, 0.449580013752, 0.263503342867, + 0.449663192034, 0.280747175217, 0.449695199728, 0.297730803490, + 0.449729591608, 0.314701884985, 0.449700742960, 0.331439197063, + 0.449738889933, 0.348396658897, 0.449762344360, 0.365289598703, + 0.449642866850, 0.381833344698, 0.449761092663, 0.398932158947, + 0.449804753065, 0.415827900171, 0.449817210436, 0.432652950287, + 0.449844151735, 0.449484139681, 0.449730366468, 0.466203570366, + 0.449716180563, 0.483032077551, 0.449710994959, 0.499870002270, + 0.449716180563, 0.516707956791, 0.449730366468, 0.533536434174, + 0.449844151735, 0.550255835056, 0.449817210436, 0.567087054253, + 0.449804753065, 0.583912074566, 0.449761092663, 0.600807845592, + 0.449642866850, 0.617906630039, 0.449762344360, 0.634450376034, + 0.449738889933, 0.651343345642, 0.449700742960, 0.668300807476, + 0.449729591608, 0.685038089752, 0.449695199728, 0.702009201050, + 0.449663192034, 0.718992829323, 0.449580013752, 0.736236691475, + 0.449580013752, 0.753120005131, 0.449605435133, 0.769867658615, + 0.449548959732, 0.787062466145, 0.449520945549, 0.804124355316, + 0.449488043785, 0.821235775948, 0.449470460415, 0.838266909122, + 0.449429988861, 0.855470001698, 0.449429988861, 0.872403323650, + 0.449429988861, 0.889336645603, 0.449429988861, 0.906270027161, + 0.449429988861, 0.923203349113, 0.449429988861, 0.940136671066, + 0.449429988861, 0.957069993019, 0.449429988861, 0.974003314972, + 0.449429988861, 0.990936636925, 0.449429988861, 1.007869958878, + 0.466363340616, -0.008129999973, 0.466363340616, 0.008803333156, + 0.466363340616, 0.025736667216, 0.466363340616, 0.042670000345, + 0.466363340616, 0.059603333473, 0.466363340616, 0.076536670327, + 0.466363340616, 0.093469999731, 0.466363340616, 0.110403336585, + 0.466363340616, 0.127336665988, 0.466363340616, 0.144270002842, + 0.466391772032, 0.161487802863, 0.466403573751, 0.178518921137, + 0.466426551342, 0.195639088750, 0.466445416212, 0.212700948119, + 0.466484665871, 0.229907438159, 0.466463327408, 0.246619999409, + 0.466463327408, 0.263503342867, 0.466523706913, 0.280779153109, + 0.466544955969, 0.297759801149, 0.466540336609, 0.314576804638, + 0.466544985771, 0.331444948912, 0.466573685408, 0.348416596651, + 0.466521680355, 0.365036725998, 0.466496497393, 0.381802707911, + 0.466596782207, 0.398970365524, 0.466612339020, 0.415825873613, + 0.466634005308, 0.432677984238, 0.466563582420, 0.449370384216, + 0.466547012329, 0.466187000275, 0.466778755188, 0.483144372702, + 0.466802805662, 0.499870002270, 0.466778755188, 0.516595602036, + 0.466547012329, 0.533553004265, 0.466563582420, 0.550369620323, + 0.466634005308, 0.567062020302, 0.466612339020, 0.583914101124, + 0.466596782207, 0.600769639015, 0.466496497393, 0.617937266827, + 0.466521680355, 0.634703278542, 0.466573685408, 0.651323378086, + 0.466544985771, 0.668295085430, 0.466540336609, 0.685163199902, + 0.466544955969, 0.701980233192, 0.466523706913, 0.718960821629, + 0.466463327408, 0.736236691475, 0.466463327408, 0.753120005131, + 0.466484665871, 0.769832551479, 0.466445416212, 0.787039041519, + 0.466426551342, 0.804100930691, 0.466403573751, 0.821221053600, + 0.466391772032, 0.838252186775, 0.466363340616, 0.855470001698, + 0.466363340616, 0.872403323650, 0.466363340616, 0.889336645603, + 0.466363340616, 0.906270027161, 0.466363340616, 0.923203349113, + 0.466363340616, 0.940136671066, 0.466363340616, 0.957069993019, + 0.466363340616, 0.974003314972, 0.466363340616, 0.990936636925, + 0.466363340616, 1.007869958878, 0.483296662569, -0.008129999973, + 0.483296662569, 0.008803333156, 0.483296662569, 0.025736667216, + 0.483296662569, 0.042670000345, 0.483296662569, 0.059603333473, + 0.483296662569, 0.076536670327, 0.483296662569, 0.093469999731, + 0.483296662569, 0.110403336585, 0.483296662569, 0.127336665988, + 0.483305424452, 0.144453704357, 0.483311325312, 0.161496669054, + 0.483317255974, 0.178527772427, 0.483329057693, 0.195653259754, + 0.483338534832, 0.212715119123, 0.483358681202, 0.229928672314, + 0.483346670866, 0.246619999409, 0.483365356922, 0.263764888048, + 0.483378350735, 0.280798554420, 0.483388960361, 0.297777414322, + 0.483385473490, 0.314580321312, 0.483387857676, 0.331448435783, + 0.483403205872, 0.348428875208, 0.483373433352, 0.365017533302, + 0.483360528946, 0.381783634424, 0.483417391777, 0.398994415998, + 0.483424574137, 0.415842831135, 0.483402311802, 0.432559251785, + 0.483392059803, 0.449356198311, 0.483504384756, 0.466418743134, + 0.483546257019, 0.483186274767, 0.483474999666, 0.499870002270, + 0.483546257019, 0.516553759575, 0.483504384756, 0.533321261406, + 0.483392059803, 0.550383806229, 0.483402311802, 0.567180752754, + 0.483424574137, 0.583897173405, 0.483417391777, 0.600745558739, + 0.483360528946, 0.617956340313, 0.483373433352, 0.634722471237, + 0.483403205872, 0.651311159134, 0.483387857676, 0.668291568756, + 0.483385473490, 0.685159683228, 0.483388960361, 0.701962590218, + 0.483378350735, 0.718941450119, 0.483365356922, 0.735975086689, + 0.483346670866, 0.753120005131, 0.483358681202, 0.769811332226, + 0.483338534832, 0.787024855614, 0.483329057693, 0.804086744785, + 0.483317255974, 0.821212232113, 0.483311325312, 0.838243305683, + 0.483305424452, 0.855286300182, 0.483296662569, 0.872403323650, + 0.483296662569, 0.889336645603, 0.483296662569, 0.906270027161, + 0.483296662569, 0.923203349113, 0.483296662569, 0.940136671066, + 0.483296662569, 0.957069993019, 0.483296662569, 0.974003314972, + 0.483296662569, 0.990936636925, 0.483296662569, 1.007869958878, + 0.500230014324, -0.008129999973, 0.500230014324, 0.008803333156, + 0.500230014324, 0.025736667216, 0.500230014324, 0.042670000345, + 0.500230014324, 0.059603333473, 0.500230014324, 0.076536670327, + 0.500230014324, 0.093469999731, 0.500230014324, 0.110403336585, + 0.500230014324, 0.127336665988, 0.500230014324, 0.144456669688, + 0.500230014324, 0.161499634385, 0.500230014324, 0.178530737758, + 0.500230014324, 0.195657998323, 0.500230014324, 0.212719857693, + 0.500230014324, 0.229935780168, 0.500230014324, 0.246619999409, + 0.500230014324, 0.263771414757, 0.500230014324, 0.280805081129, + 0.500230014324, 0.297783344984, 0.500230014324, 0.314581513405, + 0.500230014324, 0.331449627876, 0.500230014324, 0.348432987928, + 0.500230014324, 0.365011036396, 0.500230014324, 0.382075309753, + 0.500230014324, 0.399002671242, 0.500230014324, 0.415848702192, + 0.500230014324, 0.432554006577, 0.500230014324, 0.449351012707, + 0.500230014324, 0.466442823410, 0.500230014324, 0.483114987612, + 0.500230014324, 0.499870002270, 0.500230014324, 0.516624987125, + 0.500230014324, 0.533297181129, 0.500230014324, 0.550388991833, + 0.500230014324, 0.567185997963, 0.500230014324, 0.583891272545, + 0.500230014324, 0.600737333298, 0.500230014324, 0.617664694786, + 0.500230014324, 0.634728968143, 0.500230014324, 0.651306986809, + 0.500230014324, 0.668290376663, 0.500230014324, 0.685158491135, + 0.500230014324, 0.701956689358, 0.500230014324, 0.718934953213, + 0.500230014324, 0.735968589783, 0.500230014324, 0.753120005131, + 0.500230014324, 0.769804239273, 0.500230014324, 0.787020146847, + 0.500230014324, 0.804081976414, 0.500230014324, 0.821209251881, + 0.500230014324, 0.838240385056, 0.500230014324, 0.855283319950, + 0.500230014324, 0.872403323650, 0.500230014324, 0.889336645603, + 0.500230014324, 0.906270027161, 0.500230014324, 0.923203349113, + 0.500230014324, 0.940136671066, 0.500230014324, 0.957069993019, + 0.500230014324, 0.974003314972, 0.500230014324, 0.990936636925, + 0.500230014324, 1.007869958878, 0.517163336277, -0.008129999973, + 0.517163336277, 0.008803333156, 0.517163336277, 0.025736667216, + 0.517163336277, 0.042670000345, 0.517163336277, 0.059603333473, + 0.517163336277, 0.076536670327, 0.517163336277, 0.093469999731, + 0.517163336277, 0.110403336585, 0.517163336277, 0.127336665988, + 0.517154574394, 0.144453704357, 0.517148673534, 0.161496669054, + 0.517142772675, 0.178527772427, 0.517130911350, 0.195653259754, + 0.517121434212, 0.212715119123, 0.517101347446, 0.229928672314, + 0.517113327980, 0.246619999409, 0.517094671726, 0.263764888048, + 0.517081677914, 0.280798554420, 0.517071068287, 0.297777414322, + 0.517074525356, 0.314580321312, 0.517072141171, 0.331448435783, + 0.517056763172, 0.348428875208, 0.517086565495, 0.365017533302, + 0.517099499702, 0.381783634424, 0.517042577267, 0.398994415998, + 0.517035424709, 0.415842831135, 0.517057657242, 0.432559251785, + 0.517067909241, 0.449356198311, 0.516955614090, 0.466418743134, + 0.516913712025, 0.483186274767, 0.516984999180, 0.499870002270, + 0.516913712025, 0.516553759575, 0.516955614090, 0.533321261406, + 0.517067909241, 0.550383806229, 0.517057657242, 0.567180752754, + 0.517035424709, 0.583897173405, 0.517042577267, 0.600745558739, + 0.517099499702, 0.617956340313, 0.517086565495, 0.634722471237, + 0.517056763172, 0.651311159134, 0.517072141171, 0.668291568756, + 0.517074525356, 0.685159683228, 0.517071068287, 0.701962590218, + 0.517081677914, 0.718941450119, 0.517094671726, 0.735975086689, + 0.517113327980, 0.753120005131, 0.517101347446, 0.769811332226, + 0.517121434212, 0.787024855614, 0.517130911350, 0.804086744785, + 0.517142772675, 0.821212232113, 0.517148673534, 0.838243305683, + 0.517154574394, 0.855286300182, 0.517163336277, 0.872403323650, + 0.517163336277, 0.889336645603, 0.517163336277, 0.906270027161, + 0.517163336277, 0.923203349113, 0.517163336277, 0.940136671066, + 0.517163336277, 0.957069993019, 0.517163336277, 0.974003314972, + 0.517163336277, 0.990936636925, 0.517163336277, 1.007869958878, + 0.534096658230, -0.008129999973, 0.534096658230, 0.008803333156, + 0.534096658230, 0.025736667216, 0.534096658230, 0.042670000345, + 0.534096658230, 0.059603333473, 0.534096658230, 0.076536670327, + 0.534096658230, 0.093469999731, 0.534096658230, 0.110403336585, + 0.534096658230, 0.127336665988, 0.534096658230, 0.144270002842, + 0.534068226814, 0.161487802863, 0.534056425095, 0.178518921137, + 0.534033417702, 0.195639088750, 0.534014582634, 0.212700948119, + 0.533975303173, 0.229907438159, 0.533996641636, 0.246619999409, + 0.533996641636, 0.263503342867, 0.533936262131, 0.280779153109, + 0.533915042877, 0.297759801149, 0.533919692039, 0.314576804638, + 0.533914983273, 0.331444948912, 0.533886313438, 0.348416596651, + 0.533938348293, 0.365036725998, 0.533963501453, 0.381802707911, + 0.533863186836, 0.398970365524, 0.533847630024, 0.415825873613, + 0.533825993538, 0.432677984238, 0.533896386623, 0.449370384216, + 0.533913016319, 0.466187000275, 0.533681273460, 0.483144372702, + 0.533657193184, 0.499870002270, 0.533681273460, 0.516595602036, + 0.533913016319, 0.533553004265, 0.533896386623, 0.550369620323, + 0.533825993538, 0.567062020302, 0.533847630024, 0.583914101124, + 0.533863186836, 0.600769639015, 0.533963501453, 0.617937266827, + 0.533938348293, 0.634703278542, 0.533886313438, 0.651323378086, + 0.533914983273, 0.668295085430, 0.533919692039, 0.685163199902, + 0.533915042877, 0.701980233192, 0.533936262131, 0.718960821629, + 0.533996641636, 0.736236691475, 0.533996641636, 0.753120005131, + 0.533975303173, 0.769832551479, 0.534014582634, 0.787039041519, + 0.534033417702, 0.804100930691, 0.534056425095, 0.821221053600, + 0.534068226814, 0.838252186775, 0.534096658230, 0.855470001698, + 0.534096658230, 0.872403323650, 0.534096658230, 0.889336645603, + 0.534096658230, 0.906270027161, 0.534096658230, 0.923203349113, + 0.534096658230, 0.940136671066, 0.534096658230, 0.957069993019, + 0.534096658230, 0.974003314972, 0.534096658230, 0.990936636925, + 0.534096658230, 1.007869958878, 0.551029980183, -0.008129999973, + 0.551029980183, 0.008803333156, 0.551029980183, 0.025736667216, + 0.551029980183, 0.042670000345, 0.551029980183, 0.059603333473, + 0.551029980183, 0.076536670327, 0.551029980183, 0.093469999731, + 0.551029980183, 0.110403336585, 0.551029980183, 0.127336665988, + 0.551029980183, 0.144270002842, 0.550989508629, 0.161473110318, + 0.550971984863, 0.178504243493, 0.550939083099, 0.195615619421, + 0.550911009312, 0.212677508593, 0.550854563713, 0.229872331023, + 0.550880014896, 0.246619999409, 0.550880014896, 0.263503342867, + 0.550796806812, 0.280747175217, 0.550764799118, 0.297730803490, + 0.550730407238, 0.314701884985, 0.550759255886, 0.331439197063, + 0.550721108913, 0.348396658897, 0.550697624683, 0.365289598703, + 0.550817131996, 0.381833344698, 0.550698935986, 0.398932158947, + 0.550655245781, 0.415827900171, 0.550642788410, 0.432652950287, + 0.550615847111, 0.449484139681, 0.550729632378, 0.466203570366, + 0.550743818283, 0.483032077551, 0.550749003887, 0.499870002270, + 0.550743818283, 0.516707956791, 0.550729632378, 0.533536434174, + 0.550615847111, 0.550255835056, 0.550642788410, 0.567087054253, + 0.550655245781, 0.583912074566, 0.550698935986, 0.600807845592, + 0.550817131996, 0.617906630039, 0.550697624683, 0.634450376034, + 0.550721108913, 0.651343345642, 0.550759255886, 0.668300807476, + 0.550730407238, 0.685038089752, 0.550764799118, 0.702009201050, + 0.550796806812, 0.718992829323, 0.550880014896, 0.736236691475, + 0.550880014896, 0.753120005131, 0.550854563713, 0.769867658615, + 0.550911009312, 0.787062466145, 0.550939083099, 0.804124355316, + 0.550971984863, 0.821235775948, 0.550989508629, 0.838266909122, + 0.551029980183, 0.855470001698, 0.551029980183, 0.872403323650, + 0.551029980183, 0.889336645603, 0.551029980183, 0.906270027161, + 0.551029980183, 0.923203349113, 0.551029980183, 0.940136671066, + 0.551029980183, 0.957069993019, 0.551029980183, 0.974003314972, + 0.551029980183, 0.990936636925, 0.551029980183, 1.007869958878, + 0.567963361740, -0.008129999973, 0.567963361740, 0.008803333156, + 0.567963361740, 0.025736667216, 0.567963361740, 0.042670000345, + 0.567963361740, 0.059603333473, 0.567963361740, 0.076536670327, + 0.567963361740, 0.093469999731, 0.567963361740, 0.110403336585, + 0.567963361740, 0.127336665988, 0.567963361740, 0.144270002842, + 0.567913472652, 0.161452680826, 0.567890226841, 0.178483843803, + 0.567867100239, 0.195503160357, 0.567812323570, 0.212645024061, + 0.567741572857, 0.229823723435, 0.567686498165, 0.246908172965, + 0.567763328552, 0.263503342867, 0.567665934563, 0.280703127384, + 0.567623019218, 0.297691017389, 0.567578196526, 0.314662396908, + 0.567605435848, 0.331431359053, 0.567596733570, 0.348294883966, + 0.567533493042, 0.365263044834, 0.567656219006, 0.381874084473, + 0.567700624466, 0.398664057255, 0.567501664162, 0.415780454874, + 0.567452132702, 0.432647883892, 0.567447066307, 0.449457228184, + 0.567422032356, 0.466273993254, 0.567540764809, 0.483042299747, + 0.567546010017, 0.499870002270, 0.567540764809, 0.516697704792, + 0.567422032356, 0.533465981483, 0.567447066307, 0.550282776356, + 0.567452132702, 0.567092120647, 0.567501664162, 0.583959579468, + 0.567700624466, 0.601075947285, 0.567656219006, 0.617865920067, + 0.567533493042, 0.634476959705, 0.567596733570, 0.651445090771, + 0.567605435848, 0.668308615685, 0.567578196526, 0.685077607632, + 0.567623019218, 0.702048957348, 0.567665934563, 0.719036877155, + 0.567763328552, 0.736236691475, 0.567686498165, 0.752831816673, + 0.567741572857, 0.769916296005, 0.567812323570, 0.787094950676, + 0.567867100239, 0.804236829281, 0.567890226841, 0.821256160736, + 0.567913472652, 0.838287293911, 0.567963361740, 0.855470001698, + 0.567963361740, 0.872403323650, 0.567963361740, 0.889336645603, + 0.567963361740, 0.906270027161, 0.567963361740, 0.923203349113, + 0.567963361740, 0.940136671066, 0.567963361740, 0.957069993019, + 0.567963361740, 0.974003314972, 0.567963361740, 0.990936636925, + 0.567963361740, 1.007869958878, 0.584896683693, -0.008129999973, + 0.584896683693, 0.008803333156, 0.584896683693, 0.025736667216, + 0.584896683693, 0.042670000345, 0.584896683693, 0.059603333473, + 0.584896683693, 0.076536670327, 0.584896683693, 0.093469999731, + 0.584896683693, 0.110403336585, 0.584896683693, 0.127336665988, + 0.584896683693, 0.144270002842, 0.584840834141, 0.161426678300, + 0.584812104702, 0.178457900882, 0.584783554077, 0.195477306843, + 0.584720075130, 0.212603792548, 0.584638714790, 0.229762136936, + 0.584571003914, 0.246846899390, 0.584646642208, 0.263503342867, + 0.584546267986, 0.280647724867, 0.584485769272, 0.297656208277, + 0.584437727928, 0.314613074064, 0.584454178810, 0.331421643496, + 0.584443688393, 0.348285347223, 0.584379673004, 0.365230530500, + 0.584345161915, 0.382108747959, 0.584529221058, 0.398710936308, + 0.584577381611, 0.415522605181, 0.584319531918, 0.432598352432, + 0.584272086620, 0.449444741011, 0.584274113178, 0.466252356768, + 0.584257185459, 0.483064562082, 0.584251284599, 0.499870002270, + 0.584257185459, 0.516675412655, 0.584274113178, 0.533487677574, + 0.584272086620, 0.550295233727, 0.584319531918, 0.567141652107, + 0.584577381611, 0.584217429161, 0.584529221058, 0.601029038429, + 0.584345161915, 0.617631256580, 0.584379673004, 0.634509444237, + 0.584443688393, 0.651454627514, 0.584454178810, 0.668318331242, + 0.584437727928, 0.685126960278, 0.584485769272, 0.702083766460, + 0.584546267986, 0.719092249870, 0.584646642208, 0.736236691475, + 0.584571003914, 0.752893090248, 0.584638714790, 0.769977867603, + 0.584720075130, 0.787136197090, 0.584783554077, 0.804262697697, + 0.584812104702, 0.821282088757, 0.584840834141, 0.838313341141, + 0.584896683693, 0.855470001698, 0.584896683693, 0.872403323650, + 0.584896683693, 0.889336645603, 0.584896683693, 0.906270027161, + 0.584896683693, 0.923203349113, 0.584896683693, 0.940136671066, + 0.584896683693, 0.957069993019, 0.584896683693, 0.974003314972, + 0.584896683693, 0.990936636925, 0.584896683693, 1.007869958878, + 0.601830005646, -0.008129999973, 0.601830005646, 0.008803333156, + 0.601830005646, 0.025736667216, 0.601830005646, 0.042670000345, + 0.601830005646, 0.059603333473, 0.601830005646, 0.076536670327, + 0.601830005646, 0.093469999731, 0.601830005646, 0.110403336585, + 0.601830005646, 0.127336665988, 0.601830005646, 0.144270002842, + 0.601772427559, 0.161395266652, 0.601738452911, 0.178426608443, + 0.601704597473, 0.195446148515, 0.601635575294, 0.212554186583, + 0.601582109928, 0.229597687721, 0.601468622684, 0.246773496270, + 0.601530015469, 0.263503342867, 0.601530015469, 0.280386656523, + 0.601369440556, 0.297591090202, 0.601311028004, 0.314554810524, + 0.601249277592, 0.331504523754, 0.601293861866, 0.348274230957, + 0.601282238960, 0.365133702755, 0.601198911667, 0.382072925568, + 0.601162374020, 0.398937612772, 0.601389050484, 0.415570765734, + 0.601435959339, 0.432399392128, 0.601167857647, 0.449401080608, + 0.601129651070, 0.466236799955, 0.601105570793, 0.483057409525, + 0.601097345352, 0.499870002270, 0.601105570793, 0.516682624817, + 0.601129651070, 0.533503234386, 0.601167857647, 0.550338923931, + 0.601435959339, 0.567340612411, 0.601389050484, 0.584169209003, + 0.601162374020, 0.600802361965, 0.601198911667, 0.617667078972, + 0.601282238960, 0.634606301785, 0.601293861866, 0.651465773582, + 0.601249277592, 0.668235480785, 0.601311028004, 0.685185194016, + 0.601369440556, 0.702148914337, 0.601530015469, 0.719353318214, + 0.601530015469, 0.736236691475, 0.601468622684, 0.752966523170, + 0.601582109928, 0.770142316818, 0.601635575294, 0.787185788155, + 0.601704597473, 0.804293870926, 0.601738452911, 0.821313381195, + 0.601772427559, 0.838344752789, 0.601830005646, 0.855470001698, + 0.601830005646, 0.872403323650, 0.601830005646, 0.889336645603, + 0.601830005646, 0.906270027161, 0.601830005646, 0.923203349113, + 0.601830005646, 0.940136671066, 0.601830005646, 0.957069993019, + 0.601830005646, 0.974003314972, 0.601830005646, 0.990936636925, + 0.601830005646, 1.007869958878, 0.618763327599, -0.008129999973, + 0.618763327599, 0.008803333156, 0.618763327599, 0.025736667216, + 0.618763327599, 0.042670000345, 0.618763327599, 0.059603333473, + 0.618763327599, 0.076536670327, 0.618763327599, 0.093469999731, + 0.618763327599, 0.110403336585, 0.618763327599, 0.127336665988, + 0.618763327599, 0.144270002842, 0.618763327599, 0.161203339696, + 0.618669927120, 0.178390175104, 0.618631124496, 0.195409923792, + 0.618592619896, 0.212417900562, 0.618499100208, 0.229540601373, + 0.618381261826, 0.246688708663, 0.618291616440, 0.263746738434, + 0.618413329124, 0.280386656523, 0.618269503117, 0.297516614199, + 0.618191540241, 0.314501851797, 0.618131041527, 0.331439971924, + 0.618064284325, 0.348368823528, 0.618134737015, 0.365121752024, + 0.618122577667, 0.381977409124, 0.618027091026, 0.398901075125, + 0.617991209030, 0.415754824877, 0.618225932121, 0.432443767786, + 0.618266642094, 0.449282854795, 0.618297278881, 0.466136485338, + 0.618316352367, 0.483000516891, 0.618024706841, 0.499870002270, + 0.618316352367, 0.516739487648, 0.618297278881, 0.533603489399, + 0.618266642094, 0.550457119942, 0.618225932121, 0.567296206951, + 0.617991209030, 0.583985149860, 0.618027091026, 0.600838899612, + 0.618122577667, 0.617762565613, 0.618134737015, 0.634618222713, + 0.618064284325, 0.651371181011, 0.618131041527, 0.668300032616, + 0.618191540241, 0.685238122940, 0.618269503117, 0.702223420143, + 0.618413329124, 0.719353318214, 0.618291616440, 0.735993266106, + 0.618381261826, 0.753051280975, 0.618499100208, 0.770199418068, + 0.618592619896, 0.787322103977, 0.618631124496, 0.804330050945, + 0.618669927120, 0.821349799633, 0.618763327599, 0.838536679745, + 0.618763327599, 0.855470001698, 0.618763327599, 0.872403323650, + 0.618763327599, 0.889336645603, 0.618763327599, 0.906270027161, + 0.618763327599, 0.923203349113, 0.618763327599, 0.940136671066, + 0.618763327599, 0.957069993019, 0.618763327599, 0.974003314972, + 0.618763327599, 0.990936636925, 0.618763327599, 1.007869958878, + 0.635696649551, -0.008129999973, 0.635696649551, 0.008803333156, + 0.635696649551, 0.025736667216, 0.635696649551, 0.042670000345, + 0.635696649551, 0.059603333473, 0.635696649551, 0.076536670327, + 0.635696649551, 0.093469999731, 0.635696649551, 0.110403336585, + 0.635696649551, 0.127336665988, 0.635696649551, 0.144270002842, + 0.635696649551, 0.161203339696, 0.635607302189, 0.178348839283, + 0.635563790798, 0.195368915796, 0.635520696640, 0.212377250195, + 0.635426878929, 0.229476243258, 0.635359466076, 0.246502220631, + 0.635211288929, 0.263652771711, 0.635296642780, 0.280386656523, + 0.635296642780, 0.297269999981, 0.635101914406, 0.314421117306, + 0.635019004345, 0.331383764744, 0.634958386421, 0.348300576210, + 0.634889364243, 0.365210622549, 0.634978234768, 0.381965279579, + 0.634966313839, 0.398817777634, 0.634869456291, 0.415720343590, + 0.634836971760, 0.432566523552, 0.634810388088, 0.449402362108, + 0.635063290596, 0.466161668301, 0.635082483292, 0.483013451099, + 0.635088980198, 0.499870002270, 0.635082483292, 0.516726553440, + 0.635063290596, 0.533578336239, 0.634810388088, 0.550337672234, + 0.634836971760, 0.567173480988, 0.634869456291, 0.584019660950, + 0.634966313839, 0.600922226906, 0.634978234768, 0.617774724960, + 0.634889364243, 0.634529352188, 0.634958386421, 0.651439428329, + 0.635019004345, 0.668356239796, 0.635101914406, 0.685318887234, + 0.635296642780, 0.702470004559, 0.635296642780, 0.719353318214, + 0.635211288929, 0.736087262630, 0.635359466076, 0.753237783909, + 0.635426878929, 0.770263731480, 0.635520696640, 0.787362754345, + 0.635563790798, 0.804371118546, 0.635607302189, 0.821391165257, + 0.635696649551, 0.838536679745, 0.635696649551, 0.855470001698, + 0.635696649551, 0.872403323650, 0.635696649551, 0.889336645603, + 0.635696649551, 0.906270027161, 0.635696649551, 0.923203349113, + 0.635696649551, 0.940136671066, 0.635696649551, 0.957069993019, + 0.635696649551, 0.974003314972, 0.635696649551, 0.990936636925, + 0.635696649551, 1.007869958878, 0.652629971504, -0.008129999973, + 0.652629971504, 0.008803333156, 0.652629971504, 0.025736667216, + 0.652629971504, 0.042670000345, 0.652629971504, 0.059603333473, + 0.652629971504, 0.076536670327, 0.652629971504, 0.093469999731, + 0.652629971504, 0.110403336585, 0.652629971504, 0.127336665988, + 0.652629971504, 0.144270002842, 0.652629971504, 0.161203339696, + 0.652551293373, 0.178302869201, 0.652503311634, 0.195323377848, + 0.652455866337, 0.212332218885, 0.652409076691, 0.229329437017, + 0.652292728424, 0.246432125568, 0.652150332928, 0.263549476862, + 0.652043879032, 0.280583322048, 0.652180016041, 0.297269999981, + 0.652180016041, 0.314153343439, 0.651943564415, 0.331299364567, + 0.651858389378, 0.348241597414, 0.651799440384, 0.365141600370, + 0.651731193066, 0.382035732269, 0.651825726032, 0.398806154728, + 0.651814639568, 0.415656298399, 0.651805102825, 0.432503283024, + 0.651703357697, 0.449378877878, 0.651683390141, 0.466213703156, + 0.651671111584, 0.483043193817, 0.651666998863, 0.499870002270, + 0.651671111584, 0.516696810722, 0.651683390141, 0.533526301384, + 0.651703357697, 0.550361096859, 0.651805102825, 0.567236721516, + 0.651814639568, 0.584083676338, 0.651825726032, 0.600933849812, + 0.651731193066, 0.617704272270, 0.651799440384, 0.634598374367, + 0.651858389378, 0.651498436928, 0.651943564415, 0.668440639973, + 0.652180016041, 0.685586690903, 0.652180016041, 0.702470004559, + 0.652043879032, 0.719156682491, 0.652150332928, 0.736190557480, + 0.652292728424, 0.753307878971, 0.652409076691, 0.770410597324, + 0.652455866337, 0.787407815456, 0.652503311634, 0.804416596889, + 0.652551293373, 0.821437120438, 0.652629971504, 0.838536679745, + 0.652629971504, 0.855470001698, 0.652629971504, 0.872403323650, + 0.652629971504, 0.889336645603, 0.652629971504, 0.906270027161, + 0.652629971504, 0.923203349113, 0.652629971504, 0.940136671066, + 0.652629971504, 0.957069993019, 0.652629971504, 0.974003314972, + 0.652629971504, 0.990936636925, 0.652629971504, 1.007869958878, + 0.669563353062, -0.008129999973, 0.669563353062, 0.008803333156, + 0.669563353062, 0.025736667216, 0.669563353062, 0.042670000345, + 0.669563353062, 0.059603333473, 0.669563353062, 0.076536670327, + 0.669563353062, 0.093469999731, 0.669563353062, 0.110403336585, + 0.669563353062, 0.127336665988, 0.669563353062, 0.144270002842, + 0.669563353062, 0.161203339696, 0.669563353062, 0.178136661649, + 0.669450223446, 0.195273593068, 0.669398784637, 0.212283074856, + 0.669348120689, 0.229281038046, 0.669239282608, 0.246356055140, + 0.669161260128, 0.263366252184, 0.668995976448, 0.280474275351, + 0.668884932995, 0.297484099865, 0.669063329697, 0.314153343439, + 0.669063329697, 0.331036657095, 0.668800592422, 0.348156452179, + 0.668716192245, 0.365081012249, 0.668660044670, 0.381968975067, + 0.668595492840, 0.398850709200, 0.668678343296, 0.415645837784, + 0.668668627739, 0.432494550943, 0.668660819530, 0.449340760708, + 0.668655037880, 0.466184973717, 0.668651580811, 0.483027845621, + 0.668650388718, 0.499870002270, 0.668651580811, 0.516712129116, + 0.668655037880, 0.533555030823, 0.668660819530, 0.550399243832, + 0.668668627739, 0.567245423794, 0.668678343296, 0.584094166756, + 0.668595492840, 0.600889265537, 0.668660044670, 0.617771029472, + 0.668716192245, 0.634658992290, 0.668800592422, 0.651583552361, + 0.669063329697, 0.668703317642, 0.669063329697, 0.685586690903, + 0.668884932995, 0.702255904675, 0.668995976448, 0.719265758991, + 0.669161260128, 0.736373782158, 0.669239282608, 0.753383934498, + 0.669348120689, 0.770458936691, 0.669398784637, 0.787456929684, + 0.669450223446, 0.804466426373, 0.669563353062, 0.821603357792, + 0.669563353062, 0.838536679745, 0.669563353062, 0.855470001698, + 0.669563353062, 0.872403323650, 0.669563353062, 0.889336645603, + 0.669563353062, 0.906270027161, 0.669563353062, 0.923203349113, + 0.669563353062, 0.940136671066, 0.669563353062, 0.957069993019, + 0.669563353062, 0.974003314972, 0.669563353062, 0.990936636925, + 0.669563353062, 1.007869958878, 0.686496675014, -0.008129999973, + 0.686496675014, 0.008803333156, 0.686496675014, 0.025736667216, + 0.686496675014, 0.042670000345, 0.686496675014, 0.059603333473, + 0.686496675014, 0.076536670327, 0.686496675014, 0.093469999731, + 0.686496675014, 0.110403336585, 0.686496675014, 0.127336665988, + 0.686496675014, 0.144270002842, 0.686496675014, 0.161203339696, + 0.686496675014, 0.178136661649, 0.686496675014, 0.195069998503, + 0.686349928379, 0.212230160832, 0.686295688152, 0.229229032993, + 0.686242520809, 0.246216565371, 0.686116933823, 0.263286620378, + 0.686036109924, 0.280280977488, 0.685853421688, 0.297371745110, + 0.685740351677, 0.314359635115, 0.685946643353, 0.331036657095, + 0.685946643353, 0.347920000553, 0.685678899288, 0.364998072386, + 0.685598134995, 0.381908446550, 0.685545206070, 0.398788988590, + 0.685486912727, 0.415662288666, 0.685437619686, 0.432521790266, + 0.685398101807, 0.449369609356, 0.685523211956, 0.466180324554, + 0.685519635677, 0.483025491238, 0.685518503189, 0.499870002270, + 0.685519635677, 0.516714513302, 0.685523211956, 0.533559679985, + 0.685398101807, 0.550370395184, 0.685437619686, 0.567218244076, + 0.685486912727, 0.584077715874, 0.685545206070, 0.600951015949, + 0.685598134995, 0.617831528187, 0.685678899288, 0.634741902351, + 0.685946643353, 0.651820003986, 0.685946643353, 0.668703317642, + 0.685740351677, 0.685380399227, 0.685853421688, 0.702368259430, + 0.686036109924, 0.719459056854, 0.686116933823, 0.736453354359, + 0.686242520809, 0.753523409367, 0.686295688152, 0.770510971546, + 0.686349928379, 0.787509858608, 0.686496675014, 0.804669976234, + 0.686496675014, 0.821603357792, 0.686496675014, 0.838536679745, + 0.686496675014, 0.855470001698, 0.686496675014, 0.872403323650, + 0.686496675014, 0.889336645603, 0.686496675014, 0.906270027161, + 0.686496675014, 0.923203349113, 0.686496675014, 0.940136671066, + 0.686496675014, 0.957069993019, 0.686496675014, 0.974003314972, + 0.686496675014, 0.990936636925, 0.686496675014, 1.007869958878, + 0.703429996967, -0.008129999973, 0.703429996967, 0.008803333156, + 0.703429996967, 0.025736667216, 0.703429996967, 0.042670000345, + 0.703429996967, 0.059603333473, 0.703429996967, 0.076536670327, + 0.703429996967, 0.093469999731, 0.703429996967, 0.110403336585, + 0.703429996967, 0.127336665988, 0.703429996967, 0.144270002842, + 0.703429996967, 0.161203339696, 0.703429996967, 0.178136661649, + 0.703429996967, 0.195069998503, 0.703309714794, 0.212173715234, + 0.703252196312, 0.229173704982, 0.703195989132, 0.246162503958, + 0.703141212463, 0.263140231371, 0.703002929688, 0.280199319124, + 0.702920854092, 0.297179132700, 0.702728271484, 0.314246594906, + 0.702615916729, 0.331215083599, 0.702830016613, 0.347920000553, + 0.702830016613, 0.364803344011, 0.702583372593, 0.381830513477, + 0.702508926392, 0.398730546236, 0.702443778515, 0.415614247322, + 0.702408969402, 0.432476997375, 0.702369213104, 0.449335187674, + 0.702340185642, 0.466184973717, 0.702322602272, 0.483028948307, + 0.702316641808, 0.499870002270, 0.702322602272, 0.516711056232, + 0.702340185642, 0.533555030823, 0.702369213104, 0.550404787064, + 0.702408969402, 0.567263007164, 0.702443778515, 0.584125757217, + 0.702508926392, 0.601009488106, 0.702583372593, 0.617909491062, + 0.702830016613, 0.634936690331, 0.702830016613, 0.651820003986, + 0.702615916729, 0.668524920940, 0.702728271484, 0.685493409634, + 0.702920854092, 0.702560901642, 0.703002929688, 0.719540655613, + 0.703141212463, 0.736599743366, 0.703195989132, 0.753577470779, + 0.703252196312, 0.770566284657, 0.703309714794, 0.787566304207, + 0.703429996967, 0.804669976234, 0.703429996967, 0.821603357792, + 0.703429996967, 0.838536679745, 0.703429996967, 0.855470001698, + 0.703429996967, 0.872403323650, 0.703429996967, 0.889336645603, + 0.703429996967, 0.906270027161, 0.703429996967, 0.923203349113, + 0.703429996967, 0.940136671066, 0.703429996967, 0.957069993019, + 0.703429996967, 0.974003314972, 0.703429996967, 0.990936636925, + 0.703429996967, 1.007869958878, 0.720363318920, -0.008129999973, + 0.720363318920, 0.008803333156, 0.720363318920, 0.025736667216, + 0.720363318920, 0.042670000345, 0.720363318920, 0.059603333473, + 0.720363318920, 0.076536670327, 0.720363318920, 0.093469999731, + 0.720363318920, 0.110403336585, 0.720363318920, 0.127336665988, + 0.720363318920, 0.144270002842, 0.720363318920, 0.161203339696, + 0.720363318920, 0.178136661649, 0.720363318920, 0.195069998503, + 0.720363318920, 0.212003335357, 0.720218181610, 0.229115337133, + 0.720159113407, 0.246105611324, 0.720101773739, 0.263085007668, + 0.720046281815, 0.280053704977, 0.719900667667, 0.297097057104, + 0.719819009304, 0.314063906670, 0.719625711441, 0.331104040146, + 0.719516694546, 0.348056137562, 0.719713330269, 0.364803344011, + 0.719713330269, 0.381686657667, 0.719713330269, 0.398570001125, + 0.719452261925, 0.415553748608, 0.719396889210, 0.432434052229, + 0.719352841377, 0.449303179979, 0.719320833683, 0.466163724661, + 0.719301462173, 0.483018338680, 0.719294905663, 0.499870002270, + 0.719301462173, 0.516721665859, 0.719320833683, 0.533576309681, + 0.719352841377, 0.550436794758, 0.719396889210, 0.567305982113, + 0.719452261925, 0.584186255932, 0.719713330269, 0.601170003414, + 0.719713330269, 0.618053317070, 0.719713330269, 0.634936690331, + 0.719516694546, 0.651683866978, 0.719625711441, 0.668635964394, + 0.719819009304, 0.685676097870, 0.719900667667, 0.702642917633, + 0.720046281815, 0.719686329365, 0.720101773739, 0.736654996872, + 0.720159113407, 0.753634393215, 0.720218181610, 0.770624637604, + 0.720363318920, 0.787736654282, 0.720363318920, 0.804669976234, + 0.720363318920, 0.821603357792, 0.720363318920, 0.838536679745, + 0.720363318920, 0.855470001698, 0.720363318920, 0.872403323650, + 0.720363318920, 0.889336645603, 0.720363318920, 0.906270027161, + 0.720363318920, 0.923203349113, 0.720363318920, 0.940136671066, + 0.720363318920, 0.957069993019, 0.720363318920, 0.974003314972, + 0.720363318920, 0.990936636925, 0.720363318920, 1.007869958878, + 0.737296640873, -0.008129999973, 0.737296640873, 0.008803333156, + 0.737296640873, 0.025736667216, 0.737296640873, 0.042670000345, + 0.737296640873, 0.059603333473, 0.737296640873, 0.076536670327, + 0.737296640873, 0.093469999731, 0.737296640873, 0.110403336585, + 0.737296640873, 0.127336665988, 0.737296640873, 0.144270002842, + 0.737296640873, 0.161203339696, 0.737296640873, 0.178136661649, + 0.737296640873, 0.195069998503, 0.737296640873, 0.212003335357, + 0.737296640873, 0.228936672211, 0.737132251263, 0.246046155691, + 0.737072587013, 0.263027429581, 0.737015008926, 0.279998213053, + 0.736959755421, 0.296958774328, 0.736813366413, 0.313983052969, + 0.736733734608, 0.330938756466, 0.736550509930, 0.347949653864, + 0.736447215080, 0.364888727665, 0.736353278160, 0.381808370352, + 0.736596643925, 0.398570001125, 0.736596643925, 0.415453344584, + 0.736596643925, 0.432336658239, 0.736596643925, 0.449220001698, + 0.736596643925, 0.466103345156, 0.736335098743, 0.483005344868, + 0.736328601837, 0.499870002270, 0.736335098743, 0.516734659672, + 0.736596643925, 0.533636689186, 0.736596643925, 0.550520002842, + 0.736596643925, 0.567403316498, 0.736596643925, 0.584286689758, + 0.736596643925, 0.601170003414, 0.736353278160, 0.617931604385, + 0.736447215080, 0.634851276875, 0.736550509930, 0.651790320873, + 0.736733734608, 0.668801248074, 0.736813366413, 0.685756921768, + 0.736959755421, 0.702781200409, 0.737015008926, 0.719741761684, + 0.737072587013, 0.736712574959, 0.737132251263, 0.753693819046, + 0.737296640873, 0.770803332329, 0.737296640873, 0.787736654282, + 0.737296640873, 0.804669976234, 0.737296640873, 0.821603357792, + 0.737296640873, 0.838536679745, 0.737296640873, 0.855470001698, + 0.737296640873, 0.872403323650, 0.737296640873, 0.889336645603, + 0.737296640873, 0.906270027161, 0.737296640873, 0.923203349113, + 0.737296640873, 0.940136671066, 0.737296640873, 0.957069993019, + 0.737296640873, 0.974003314972, 0.737296640873, 0.990936636925, + 0.737296640873, 1.007869958878, 0.754230022430, -0.008129999973, + 0.754230022430, 0.008803333156, 0.754230022430, 0.025736667216, + 0.754230022430, 0.042670000345, 0.754230022430, 0.059603333473, + 0.754230022430, 0.076536670327, 0.754230022430, 0.093469999731, + 0.754230022430, 0.110403336585, 0.754230022430, 0.127336665988, + 0.754230022430, 0.144270002842, 0.754230022430, 0.161203339696, + 0.754230022430, 0.178136661649, 0.754230022430, 0.195069998503, + 0.754230022430, 0.212003335357, 0.754230022430, 0.228936672211, + 0.754230022430, 0.245869994164, 0.754053831100, 0.262967735529, + 0.753994405270, 0.279940843582, 0.753937482834, 0.296903997660, + 0.753883421421, 0.313857495785, 0.753743946552, 0.330860704184, + 0.753667891026, 0.347807288170, 0.753597795963, 0.364740520716, + 0.753411293030, 0.381718724966, 0.753326535225, 0.398631393909, + 0.753253102303, 0.415528953075, 0.753191828728, 0.432413518429, + 0.753480017185, 0.449220001698, 0.753480017185, 0.466103345156, + 0.753480017185, 0.482986658812, 0.753480017185, 0.499870002270, + 0.753480017185, 0.516753315926, 0.753480017185, 0.533636689186, + 0.753480017185, 0.550520002842, 0.753191828728, 0.567326486111, + 0.753253102303, 0.584211051464, 0.753326535225, 0.601108610630, + 0.753411293030, 0.618021249771, 0.753597795963, 0.634999454021, + 0.753667891026, 0.651932716370, 0.753743946552, 0.668879270554, + 0.753883421421, 0.685882508755, 0.753937482834, 0.702835977077, + 0.753994405270, 0.719799160957, 0.754053831100, 0.736772239208, + 0.754230022430, 0.753870010376, 0.754230022430, 0.770803332329, + 0.754230022430, 0.787736654282, 0.754230022430, 0.804669976234, + 0.754230022430, 0.821603357792, 0.754230022430, 0.838536679745, + 0.754230022430, 0.855470001698, 0.754230022430, 0.872403323650, + 0.754230022430, 0.889336645603, 0.754230022430, 0.906270027161, + 0.754230022430, 0.923203349113, 0.754230022430, 0.940136671066, + 0.754230022430, 0.957069993019, 0.754230022430, 0.974003314972, + 0.754230022430, 0.990936636925, 0.754230022430, 1.007869958878, + 0.771163344383, -0.008129999973, 0.771163344383, 0.008803333156, + 0.771163344383, 0.025736667216, 0.771163344383, 0.042670000345, + 0.771163344383, 0.059603333473, 0.771163344383, 0.076536670327, + 0.771163344383, 0.093469999731, 0.771163344383, 0.110403336585, + 0.771163344383, 0.127336665988, 0.771163344383, 0.144270002842, + 0.771163344383, 0.161203339696, 0.771163344383, 0.178136661649, + 0.771163344383, 0.195069998503, 0.771163344383, 0.212003335357, + 0.771163344383, 0.228936672211, 0.771163344383, 0.245869994164, + 0.771163344383, 0.262803345919, 0.770984649658, 0.279881834984, + 0.770926296711, 0.296847790480, 0.770870983601, 0.313804328442, + 0.770818948746, 0.330751895905, 0.770770549774, 0.347690939903, + 0.770623743534, 0.364673107862, 0.770559370518, 0.381600886583, + 0.770502328873, 0.398517876863, 0.770337879658, 0.415461301804, + 0.770276248455, 0.432358443737, 0.770227670670, 0.449245423079, + 0.770192563534, 0.466124683619, 0.770171344280, 0.482998669147, + 0.770164251328, 0.499870002270, 0.770171344280, 0.516741335392, + 0.770192563534, 0.533615291119, 0.770227670670, 0.550494551659, + 0.770276248455, 0.567381560802, 0.770337879658, 0.584278702736, + 0.770502328873, 0.601222097874, 0.770559370518, 0.618139088154, + 0.770623743534, 0.635066866875, 0.770770549774, 0.652049064636, + 0.770818948746, 0.668988108635, 0.770870983601, 0.685935676098, + 0.770926296711, 0.702892243862, 0.770984649658, 0.719858169556, + 0.771163344383, 0.736936688423, 0.771163344383, 0.753870010376, + 0.771163344383, 0.770803332329, 0.771163344383, 0.787736654282, + 0.771163344383, 0.804669976234, 0.771163344383, 0.821603357792, + 0.771163344383, 0.838536679745, 0.771163344383, 0.855470001698, + 0.771163344383, 0.872403323650, 0.771163344383, 0.889336645603, + 0.771163344383, 0.906270027161, 0.771163344383, 0.923203349113, + 0.771163344383, 0.940136671066, 0.771163344383, 0.957069993019, + 0.771163344383, 0.974003314972, 0.771163344383, 0.990936636925, + 0.771163344383, 1.007869958878, 0.788096666336, -0.008129999973, + 0.788096666336, 0.008803333156, 0.788096666336, 0.025736667216, + 0.788096666336, 0.042670000345, 0.788096666336, 0.059603333473, + 0.788096666336, 0.076536670327, 0.788096666336, 0.093469999731, + 0.788096666336, 0.110403336585, 0.788096666336, 0.127336665988, + 0.788096666336, 0.144270002842, 0.788096666336, 0.161203339696, + 0.788096666336, 0.178136661649, 0.788096666336, 0.195069998503, + 0.788096666336, 0.212003335357, 0.788096666336, 0.228936672211, + 0.788096666336, 0.245869994164, 0.788096666336, 0.262803345919, + 0.788096666336, 0.279736667871, 0.787926256657, 0.296790271997, + 0.787869870663, 0.313750088215, 0.787816941738, 0.330701231956, + 0.787767767906, 0.347644120455, 0.787722766399, 0.364579290152, + 0.787682116032, 0.381507366896, 0.787545800209, 0.398464411497, + 0.787496209145, 0.415379941463, 0.787454962730, 0.432287663221, + 0.787422478199, 0.449188977480, 0.787399053574, 0.466085404158, + 0.787384867668, 0.482978522778, 0.787380158901, 0.499870002270, + 0.787384867668, 0.516761481762, 0.787399053574, 0.533654570580, + 0.787422478199, 0.550551056862, 0.787454962730, 0.567452371120, + 0.787496209145, 0.584360063076, 0.787545800209, 0.601275563240, + 0.787682116032, 0.618232607841, 0.787722766399, 0.635160684586, + 0.787767767906, 0.652095913887, 0.787816941738, 0.669038772583, + 0.787869870663, 0.685989916325, 0.787926256657, 0.702949702740, + 0.788096666336, 0.720003306866, 0.788096666336, 0.736936688423, + 0.788096666336, 0.753870010376, 0.788096666336, 0.770803332329, + 0.788096666336, 0.787736654282, 0.788096666336, 0.804669976234, + 0.788096666336, 0.821603357792, 0.788096666336, 0.838536679745, + 0.788096666336, 0.855470001698, 0.788096666336, 0.872403323650, + 0.788096666336, 0.889336645603, 0.788096666336, 0.906270027161, + 0.788096666336, 0.923203349113, 0.788096666336, 0.940136671066, + 0.788096666336, 0.957069993019, 0.788096666336, 0.974003314972, + 0.788096666336, 0.990936636925, 0.788096666336, 1.007869958878, + 0.805029988289, -0.008129999973, 0.805029988289, 0.008803333156, + 0.805029988289, 0.025736667216, 0.805029988289, 0.042670000345, + 0.805029988289, 0.059603333473, 0.805029988289, 0.076536670327, + 0.805029988289, 0.093469999731, 0.805029988289, 0.110403336585, + 0.805029988289, 0.127336665988, 0.805029988289, 0.144270002842, + 0.805029988289, 0.161203339696, 0.805029988289, 0.178136661649, + 0.805029988289, 0.195069998503, 0.805029988289, 0.212003335357, + 0.805029988289, 0.228936672211, 0.805029988289, 0.245869994164, + 0.805029988289, 0.262803345919, 0.805029988289, 0.279736667871, + 0.805029988289, 0.296669989824, 0.805029988289, 0.313603341579, + 0.804826378822, 0.330649763346, 0.804776608944, 0.347596675158, + 0.804731070995, 0.364536195993, 0.804690063000, 0.381468862295, + 0.804653882980, 0.398395389318, 0.804622709751, 0.415316462517, + 0.804596841335, 0.432232916355, 0.804484367371, 0.449160933495, + 0.804460883141, 0.466066569090, 0.804446756840, 0.482969075441, + 0.804441988468, 0.499870002270, 0.804446756840, 0.516770958900, + 0.804460883141, 0.533673405647, 0.804484367371, 0.550579071045, + 0.804596841335, 0.567507088184, 0.804622709751, 0.584423542023, + 0.804653882980, 0.601344645023, 0.804690063000, 0.618271112442, + 0.804731070995, 0.635203838348, 0.804776608944, 0.652143299580, + 0.804826378822, 0.669090211391, 0.805029988289, 0.686136662960, + 0.805029988289, 0.703069984913, 0.805029988289, 0.720003306866, + 0.805029988289, 0.736936688423, 0.805029988289, 0.753870010376, + 0.805029988289, 0.770803332329, 0.805029988289, 0.787736654282, + 0.805029988289, 0.804669976234, 0.805029988289, 0.821603357792, + 0.805029988289, 0.838536679745, 0.805029988289, 0.855470001698, + 0.805029988289, 0.872403323650, 0.805029988289, 0.889336645603, + 0.805029988289, 0.906270027161, 0.805029988289, 0.923203349113, + 0.805029988289, 0.940136671066, 0.805029988289, 0.957069993019, + 0.805029988289, 0.974003314972, 0.805029988289, 0.990936636925, + 0.805029988289, 1.007869958878, 0.821963310242, -0.008129999973, + 0.821963310242, 0.008803333156, 0.821963310242, 0.025736667216, + 0.821963310242, 0.042670000345, 0.821963310242, 0.059603333473, + 0.821963310242, 0.076536670327, 0.821963310242, 0.093469999731, + 0.821963310242, 0.110403336585, 0.821963310242, 0.127336665988, + 0.821963310242, 0.144270002842, 0.821963310242, 0.161203339696, + 0.821963310242, 0.178136661649, 0.821963310242, 0.195069998503, + 0.821963310242, 0.212003335357, 0.821963310242, 0.228936672211, + 0.821963310242, 0.245869994164, 0.821963310242, 0.262803345919, + 0.821963310242, 0.279736667871, 0.821963310242, 0.296669989824, + 0.821963310242, 0.313603341579, 0.821963310242, 0.330536663532, + 0.821797132492, 0.347548723221, 0.821751177311, 0.364492684603, + 0.821709811687, 0.381430059671, 0.821673393250, 0.398361563683, + 0.821642100811, 0.415287882090, 0.821616172791, 0.432209759951, + 0.821595788002, 0.449128031731, 0.821581065655, 0.466043561697, + 0.821572244167, 0.482957243919, 0.821569263935, 0.499870002270, + 0.821572244167, 0.516782760620, 0.821581065655, 0.533696413040, + 0.821595788002, 0.550611972809, 0.821616172791, 0.567530214787, + 0.821642100811, 0.584452152252, 0.821673393250, 0.601378440857, + 0.821709811687, 0.618309915066, 0.821751177311, 0.635247349739, + 0.821797132492, 0.652191281319, 0.821963310242, 0.669203341007, + 0.821963310242, 0.686136662960, 0.821963310242, 0.703069984913, + 0.821963310242, 0.720003306866, 0.821963310242, 0.736936688423, + 0.821963310242, 0.753870010376, 0.821963310242, 0.770803332329, + 0.821963310242, 0.787736654282, 0.821963310242, 0.804669976234, + 0.821963310242, 0.821603357792, 0.821963310242, 0.838536679745, + 0.821963310242, 0.855470001698, 0.821963310242, 0.872403323650, + 0.821963310242, 0.889336645603, 0.821963310242, 0.906270027161, + 0.821963310242, 0.923203349113, 0.821963310242, 0.940136671066, + 0.821963310242, 0.957069993019, 0.821963310242, 0.974003314972, + 0.821963310242, 0.990936636925, 0.821963310242, 1.007869958878, + 0.838896691799, -0.008129999973, 0.838896691799, 0.008803333156, + 0.838896691799, 0.025736667216, 0.838896691799, 0.042670000345, + 0.838896691799, 0.059603333473, 0.838896691799, 0.076536670327, + 0.838896691799, 0.093469999731, 0.838896691799, 0.110403336585, + 0.838896691799, 0.127336665988, 0.838896691799, 0.144270002842, + 0.838896691799, 0.161203339696, 0.838896691799, 0.178136661649, + 0.838896691799, 0.195069998503, 0.838896691799, 0.212003335357, + 0.838896691799, 0.228936672211, 0.838896691799, 0.245869994164, + 0.838896691799, 0.262803345919, 0.838896691799, 0.279736667871, + 0.838896691799, 0.296669989824, 0.838896691799, 0.313603341579, + 0.838896691799, 0.330536663532, 0.838896691799, 0.347469985485, + 0.838896691799, 0.364403337240, 0.838896691799, 0.381336659193, + 0.838704764843, 0.398327589035, 0.838673293591, 0.415259182453, + 0.838647305965, 0.432186543941, 0.838626861572, 0.449110478163, + 0.838612198830, 0.466031789780, 0.838603317738, 0.482951343060, + 0.838600397110, 0.499870002270, 0.838603317738, 0.516788661480, + 0.838612198830, 0.533708214760, 0.838626861572, 0.550629556179, + 0.838647305965, 0.567553460598, 0.838673293591, 0.584480822086, + 0.838704764843, 0.601412415504, 0.838896691799, 0.618403315544, + 0.838896691799, 0.635336637497, 0.838896691799, 0.652270019054, + 0.838896691799, 0.669203341007, 0.838896691799, 0.686136662960, + 0.838896691799, 0.703069984913, 0.838896691799, 0.720003306866, + 0.838896691799, 0.736936688423, 0.838896691799, 0.753870010376, + 0.838896691799, 0.770803332329, 0.838896691799, 0.787736654282, + 0.838896691799, 0.804669976234, 0.838896691799, 0.821603357792, + 0.838896691799, 0.838536679745, 0.838896691799, 0.855470001698, + 0.838896691799, 0.872403323650, 0.838896691799, 0.889336645603, + 0.838896691799, 0.906270027161, 0.838896691799, 0.923203349113, + 0.838896691799, 0.940136671066, 0.838896691799, 0.957069993019, + 0.838896691799, 0.974003314972, 0.838896691799, 0.990936636925, + 0.838896691799, 1.007869958878, 0.855830013752, -0.008129999973, + 0.855830013752, 0.008803333156, 0.855830013752, 0.025736667216, + 0.855830013752, 0.042670000345, 0.855830013752, 0.059603333473, + 0.855830013752, 0.076536670327, 0.855830013752, 0.093469999731, + 0.855830013752, 0.110403336585, 0.855830013752, 0.127336665988, + 0.855830013752, 0.144270002842, 0.855830013752, 0.161203339696, + 0.855830013752, 0.178136661649, 0.855830013752, 0.195069998503, + 0.855830013752, 0.212003335357, 0.855830013752, 0.228936672211, + 0.855830013752, 0.245869994164, 0.855830013752, 0.262803345919, + 0.855830013752, 0.279736667871, 0.855830013752, 0.296669989824, + 0.855830013752, 0.313603341579, 0.855830013752, 0.330536663532, + 0.855830013752, 0.347469985485, 0.855830013752, 0.364403337240, + 0.855830013752, 0.381336659193, 0.855830013752, 0.398270010948, + 0.855830013752, 0.415203332901, 0.855830013752, 0.432136654854, + 0.855830013752, 0.449070006609, 0.855830013752, 0.466003328562, + 0.855646312237, 0.482945412397, 0.855643332005, 0.499870002270, + 0.855646312237, 0.516794562340, 0.855830013752, 0.533736646175, + 0.855830013752, 0.550670027733, 0.855830013752, 0.567603349686, + 0.855830013752, 0.584536671638, 0.855830013752, 0.601469993591, + 0.855830013752, 0.618403315544, 0.855830013752, 0.635336637497, + 0.855830013752, 0.652270019054, 0.855830013752, 0.669203341007, + 0.855830013752, 0.686136662960, 0.855830013752, 0.703069984913, + 0.855830013752, 0.720003306866, 0.855830013752, 0.736936688423, + 0.855830013752, 0.753870010376, 0.855830013752, 0.770803332329, + 0.855830013752, 0.787736654282, 0.855830013752, 0.804669976234, + 0.855830013752, 0.821603357792, 0.855830013752, 0.838536679745, + 0.855830013752, 0.855470001698, 0.855830013752, 0.872403323650, + 0.855830013752, 0.889336645603, 0.855830013752, 0.906270027161, + 0.855830013752, 0.923203349113, 0.855830013752, 0.940136671066, + 0.855830013752, 0.957069993019, 0.855830013752, 0.974003314972, + 0.855830013752, 0.990936636925, 0.855830013752, 1.007869958878, + 0.872763335705, -0.008129999973, 0.872763335705, 0.008803333156, + 0.872763335705, 0.025736667216, 0.872763335705, 0.042670000345, + 0.872763335705, 0.059603333473, 0.872763335705, 0.076536670327, + 0.872763335705, 0.093469999731, 0.872763335705, 0.110403336585, + 0.872763335705, 0.127336665988, 0.872763335705, 0.144270002842, + 0.872763335705, 0.161203339696, 0.872763335705, 0.178136661649, + 0.872763335705, 0.195069998503, 0.872763335705, 0.212003335357, + 0.872763335705, 0.228936672211, 0.872763335705, 0.245869994164, + 0.872763335705, 0.262803345919, 0.872763335705, 0.279736667871, + 0.872763335705, 0.296669989824, 0.872763335705, 0.313603341579, + 0.872763335705, 0.330536663532, 0.872763335705, 0.347469985485, + 0.872763335705, 0.364403337240, 0.872763335705, 0.381336659193, + 0.872763335705, 0.398270010948, 0.872763335705, 0.415203332901, + 0.872763335705, 0.432136654854, 0.872763335705, 0.449070006609, + 0.872763335705, 0.466003328562, 0.872763335705, 0.482936680317, + 0.872763335705, 0.499870002270, 0.872763335705, 0.516803324223, + 0.872763335705, 0.533736646175, 0.872763335705, 0.550670027733, + 0.872763335705, 0.567603349686, 0.872763335705, 0.584536671638, + 0.872763335705, 0.601469993591, 0.872763335705, 0.618403315544, + 0.872763335705, 0.635336637497, 0.872763335705, 0.652270019054, + 0.872763335705, 0.669203341007, 0.872763335705, 0.686136662960, + 0.872763335705, 0.703069984913, 0.872763335705, 0.720003306866, + 0.872763335705, 0.736936688423, 0.872763335705, 0.753870010376, + 0.872763335705, 0.770803332329, 0.872763335705, 0.787736654282, + 0.872763335705, 0.804669976234, 0.872763335705, 0.821603357792, + 0.872763335705, 0.838536679745, 0.872763335705, 0.855470001698, + 0.872763335705, 0.872403323650, 0.872763335705, 0.889336645603, + 0.872763335705, 0.906270027161, 0.872763335705, 0.923203349113, + 0.872763335705, 0.940136671066, 0.872763335705, 0.957069993019, + 0.872763335705, 0.974003314972, 0.872763335705, 0.990936636925, + 0.872763335705, 1.007869958878, 0.889696657658, -0.008129999973, + 0.889696657658, 0.008803333156, 0.889696657658, 0.025736667216, + 0.889696657658, 0.042670000345, 0.889696657658, 0.059603333473, + 0.889696657658, 0.076536670327, 0.889696657658, 0.093469999731, + 0.889696657658, 0.110403336585, 0.889696657658, 0.127336665988, + 0.889696657658, 0.144270002842, 0.889696657658, 0.161203339696, + 0.889696657658, 0.178136661649, 0.889696657658, 0.195069998503, + 0.889696657658, 0.212003335357, 0.889696657658, 0.228936672211, + 0.889696657658, 0.245869994164, 0.889696657658, 0.262803345919, + 0.889696657658, 0.279736667871, 0.889696657658, 0.296669989824, + 0.889696657658, 0.313603341579, 0.889696657658, 0.330536663532, + 0.889696657658, 0.347469985485, 0.889696657658, 0.364403337240, + 0.889696657658, 0.381336659193, 0.889696657658, 0.398270010948, + 0.889696657658, 0.415203332901, 0.889696657658, 0.432136654854, + 0.889696657658, 0.449070006609, 0.889696657658, 0.466003328562, + 0.889696657658, 0.482936680317, 0.889696657658, 0.499870002270, + 0.889696657658, 0.516803324223, 0.889696657658, 0.533736646175, + 0.889696657658, 0.550670027733, 0.889696657658, 0.567603349686, + 0.889696657658, 0.584536671638, 0.889696657658, 0.601469993591, + 0.889696657658, 0.618403315544, 0.889696657658, 0.635336637497, + 0.889696657658, 0.652270019054, 0.889696657658, 0.669203341007, + 0.889696657658, 0.686136662960, 0.889696657658, 0.703069984913, + 0.889696657658, 0.720003306866, 0.889696657658, 0.736936688423, + 0.889696657658, 0.753870010376, 0.889696657658, 0.770803332329, + 0.889696657658, 0.787736654282, 0.889696657658, 0.804669976234, + 0.889696657658, 0.821603357792, 0.889696657658, 0.838536679745, + 0.889696657658, 0.855470001698, 0.889696657658, 0.872403323650, + 0.889696657658, 0.889336645603, 0.889696657658, 0.906270027161, + 0.889696657658, 0.923203349113, 0.889696657658, 0.940136671066, + 0.889696657658, 0.957069993019, 0.889696657658, 0.974003314972, + 0.889696657658, 0.990936636925, 0.889696657658, 1.007869958878, + 0.906629979610, -0.008129999973, 0.906629979610, 0.008803333156, + 0.906629979610, 0.025736667216, 0.906629979610, 0.042670000345, + 0.906629979610, 0.059603333473, 0.906629979610, 0.076536670327, + 0.906629979610, 0.093469999731, 0.906629979610, 0.110403336585, + 0.906629979610, 0.127336665988, 0.906629979610, 0.144270002842, + 0.906629979610, 0.161203339696, 0.906629979610, 0.178136661649, + 0.906629979610, 0.195069998503, 0.906629979610, 0.212003335357, + 0.906629979610, 0.228936672211, 0.906629979610, 0.245869994164, + 0.906629979610, 0.262803345919, 0.906629979610, 0.279736667871, + 0.906629979610, 0.296669989824, 0.906629979610, 0.313603341579, + 0.906629979610, 0.330536663532, 0.906629979610, 0.347469985485, + 0.906629979610, 0.364403337240, 0.906629979610, 0.381336659193, + 0.906629979610, 0.398270010948, 0.906629979610, 0.415203332901, + 0.906629979610, 0.432136654854, 0.906629979610, 0.449070006609, + 0.906629979610, 0.466003328562, 0.906629979610, 0.482936680317, + 0.906629979610, 0.499870002270, 0.906629979610, 0.516803324223, + 0.906629979610, 0.533736646175, 0.906629979610, 0.550670027733, + 0.906629979610, 0.567603349686, 0.906629979610, 0.584536671638, + 0.906629979610, 0.601469993591, 0.906629979610, 0.618403315544, + 0.906629979610, 0.635336637497, 0.906629979610, 0.652270019054, + 0.906629979610, 0.669203341007, 0.906629979610, 0.686136662960, + 0.906629979610, 0.703069984913, 0.906629979610, 0.720003306866, + 0.906629979610, 0.736936688423, 0.906629979610, 0.753870010376, + 0.906629979610, 0.770803332329, 0.906629979610, 0.787736654282, + 0.906629979610, 0.804669976234, 0.906629979610, 0.821603357792, + 0.906629979610, 0.838536679745, 0.906629979610, 0.855470001698, + 0.906629979610, 0.872403323650, 0.906629979610, 0.889336645603, + 0.906629979610, 0.906270027161, 0.906629979610, 0.923203349113, + 0.906629979610, 0.940136671066, 0.906629979610, 0.957069993019, + 0.906629979610, 0.974003314972, 0.906629979610, 0.990936636925, + 0.906629979610, 1.007869958878, 0.923563361168, -0.008129999973, + 0.923563361168, 0.008803333156, 0.923563361168, 0.025736667216, + 0.923563361168, 0.042670000345, 0.923563361168, 0.059603333473, + 0.923563361168, 0.076536670327, 0.923563361168, 0.093469999731, + 0.923563361168, 0.110403336585, 0.923563361168, 0.127336665988, + 0.923563361168, 0.144270002842, 0.923563361168, 0.161203339696, + 0.923563361168, 0.178136661649, 0.923563361168, 0.195069998503, + 0.923563361168, 0.212003335357, 0.923563361168, 0.228936672211, + 0.923563361168, 0.245869994164, 0.923563361168, 0.262803345919, + 0.923563361168, 0.279736667871, 0.923563361168, 0.296669989824, + 0.923563361168, 0.313603341579, 0.923563361168, 0.330536663532, + 0.923563361168, 0.347469985485, 0.923563361168, 0.364403337240, + 0.923563361168, 0.381336659193, 0.923563361168, 0.398270010948, + 0.923563361168, 0.415203332901, 0.923563361168, 0.432136654854, + 0.923563361168, 0.449070006609, 0.923563361168, 0.466003328562, + 0.923563361168, 0.482936680317, 0.923563361168, 0.499870002270, + 0.923563361168, 0.516803324223, 0.923563361168, 0.533736646175, + 0.923563361168, 0.550670027733, 0.923563361168, 0.567603349686, + 0.923563361168, 0.584536671638, 0.923563361168, 0.601469993591, + 0.923563361168, 0.618403315544, 0.923563361168, 0.635336637497, + 0.923563361168, 0.652270019054, 0.923563361168, 0.669203341007, + 0.923563361168, 0.686136662960, 0.923563361168, 0.703069984913, + 0.923563361168, 0.720003306866, 0.923563361168, 0.736936688423, + 0.923563361168, 0.753870010376, 0.923563361168, 0.770803332329, + 0.923563361168, 0.787736654282, 0.923563361168, 0.804669976234, + 0.923563361168, 0.821603357792, 0.923563361168, 0.838536679745, + 0.923563361168, 0.855470001698, 0.923563361168, 0.872403323650, + 0.923563361168, 0.889336645603, 0.923563361168, 0.906270027161, + 0.923563361168, 0.923203349113, 0.923563361168, 0.940136671066, + 0.923563361168, 0.957069993019, 0.923563361168, 0.974003314972, + 0.923563361168, 0.990936636925, 0.923563361168, 1.007869958878, + 0.940496683121, -0.008129999973, 0.940496683121, 0.008803333156, + 0.940496683121, 0.025736667216, 0.940496683121, 0.042670000345, + 0.940496683121, 0.059603333473, 0.940496683121, 0.076536670327, + 0.940496683121, 0.093469999731, 0.940496683121, 0.110403336585, + 0.940496683121, 0.127336665988, 0.940496683121, 0.144270002842, + 0.940496683121, 0.161203339696, 0.940496683121, 0.178136661649, + 0.940496683121, 0.195069998503, 0.940496683121, 0.212003335357, + 0.940496683121, 0.228936672211, 0.940496683121, 0.245869994164, + 0.940496683121, 0.262803345919, 0.940496683121, 0.279736667871, + 0.940496683121, 0.296669989824, 0.940496683121, 0.313603341579, + 0.940496683121, 0.330536663532, 0.940496683121, 0.347469985485, + 0.940496683121, 0.364403337240, 0.940496683121, 0.381336659193, + 0.940496683121, 0.398270010948, 0.940496683121, 0.415203332901, + 0.940496683121, 0.432136654854, 0.940496683121, 0.449070006609, + 0.940496683121, 0.466003328562, 0.940496683121, 0.482936680317, + 0.940496683121, 0.499870002270, 0.940496683121, 0.516803324223, + 0.940496683121, 0.533736646175, 0.940496683121, 0.550670027733, + 0.940496683121, 0.567603349686, 0.940496683121, 0.584536671638, + 0.940496683121, 0.601469993591, 0.940496683121, 0.618403315544, + 0.940496683121, 0.635336637497, 0.940496683121, 0.652270019054, + 0.940496683121, 0.669203341007, 0.940496683121, 0.686136662960, + 0.940496683121, 0.703069984913, 0.940496683121, 0.720003306866, + 0.940496683121, 0.736936688423, 0.940496683121, 0.753870010376, + 0.940496683121, 0.770803332329, 0.940496683121, 0.787736654282, + 0.940496683121, 0.804669976234, 0.940496683121, 0.821603357792, + 0.940496683121, 0.838536679745, 0.940496683121, 0.855470001698, + 0.940496683121, 0.872403323650, 0.940496683121, 0.889336645603, + 0.940496683121, 0.906270027161, 0.940496683121, 0.923203349113, + 0.940496683121, 0.940136671066, 0.940496683121, 0.957069993019, + 0.940496683121, 0.974003314972, 0.940496683121, 0.990936636925, + 0.940496683121, 1.007869958878, 0.957430005074, -0.008129999973, + 0.957430005074, 0.008803333156, 0.957430005074, 0.025736667216, + 0.957430005074, 0.042670000345, 0.957430005074, 0.059603333473, + 0.957430005074, 0.076536670327, 0.957430005074, 0.093469999731, + 0.957430005074, 0.110403336585, 0.957430005074, 0.127336665988, + 0.957430005074, 0.144270002842, 0.957430005074, 0.161203339696, + 0.957430005074, 0.178136661649, 0.957430005074, 0.195069998503, + 0.957430005074, 0.212003335357, 0.957430005074, 0.228936672211, + 0.957430005074, 0.245869994164, 0.957430005074, 0.262803345919, + 0.957430005074, 0.279736667871, 0.957430005074, 0.296669989824, + 0.957430005074, 0.313603341579, 0.957430005074, 0.330536663532, + 0.957430005074, 0.347469985485, 0.957430005074, 0.364403337240, + 0.957430005074, 0.381336659193, 0.957430005074, 0.398270010948, + 0.957430005074, 0.415203332901, 0.957430005074, 0.432136654854, + 0.957430005074, 0.449070006609, 0.957430005074, 0.466003328562, + 0.957430005074, 0.482936680317, 0.957430005074, 0.499870002270, + 0.957430005074, 0.516803324223, 0.957430005074, 0.533736646175, + 0.957430005074, 0.550670027733, 0.957430005074, 0.567603349686, + 0.957430005074, 0.584536671638, 0.957430005074, 0.601469993591, + 0.957430005074, 0.618403315544, 0.957430005074, 0.635336637497, + 0.957430005074, 0.652270019054, 0.957430005074, 0.669203341007, + 0.957430005074, 0.686136662960, 0.957430005074, 0.703069984913, + 0.957430005074, 0.720003306866, 0.957430005074, 0.736936688423, + 0.957430005074, 0.753870010376, 0.957430005074, 0.770803332329, + 0.957430005074, 0.787736654282, 0.957430005074, 0.804669976234, + 0.957430005074, 0.821603357792, 0.957430005074, 0.838536679745, + 0.957430005074, 0.855470001698, 0.957430005074, 0.872403323650, + 0.957430005074, 0.889336645603, 0.957430005074, 0.906270027161, + 0.957430005074, 0.923203349113, 0.957430005074, 0.940136671066, + 0.957430005074, 0.957069993019, 0.957430005074, 0.974003314972, + 0.957430005074, 0.990936636925, 0.957430005074, 1.007869958878, + 0.974363327026, -0.008129999973, 0.974363327026, 0.008803333156, + 0.974363327026, 0.025736667216, 0.974363327026, 0.042670000345, + 0.974363327026, 0.059603333473, 0.974363327026, 0.076536670327, + 0.974363327026, 0.093469999731, 0.974363327026, 0.110403336585, + 0.974363327026, 0.127336665988, 0.974363327026, 0.144270002842, + 0.974363327026, 0.161203339696, 0.974363327026, 0.178136661649, + 0.974363327026, 0.195069998503, 0.974363327026, 0.212003335357, + 0.974363327026, 0.228936672211, 0.974363327026, 0.245869994164, + 0.974363327026, 0.262803345919, 0.974363327026, 0.279736667871, + 0.974363327026, 0.296669989824, 0.974363327026, 0.313603341579, + 0.974363327026, 0.330536663532, 0.974363327026, 0.347469985485, + 0.974363327026, 0.364403337240, 0.974363327026, 0.381336659193, + 0.974363327026, 0.398270010948, 0.974363327026, 0.415203332901, + 0.974363327026, 0.432136654854, 0.974363327026, 0.449070006609, + 0.974363327026, 0.466003328562, 0.974363327026, 0.482936680317, + 0.974363327026, 0.499870002270, 0.974363327026, 0.516803324223, + 0.974363327026, 0.533736646175, 0.974363327026, 0.550670027733, + 0.974363327026, 0.567603349686, 0.974363327026, 0.584536671638, + 0.974363327026, 0.601469993591, 0.974363327026, 0.618403315544, + 0.974363327026, 0.635336637497, 0.974363327026, 0.652270019054, + 0.974363327026, 0.669203341007, 0.974363327026, 0.686136662960, + 0.974363327026, 0.703069984913, 0.974363327026, 0.720003306866, + 0.974363327026, 0.736936688423, 0.974363327026, 0.753870010376, + 0.974363327026, 0.770803332329, 0.974363327026, 0.787736654282, + 0.974363327026, 0.804669976234, 0.974363327026, 0.821603357792, + 0.974363327026, 0.838536679745, 0.974363327026, 0.855470001698, + 0.974363327026, 0.872403323650, 0.974363327026, 0.889336645603, + 0.974363327026, 0.906270027161, 0.974363327026, 0.923203349113, + 0.974363327026, 0.940136671066, 0.974363327026, 0.957069993019, + 0.974363327026, 0.974003314972, 0.974363327026, 0.990936636925, + 0.974363327026, 1.007869958878, 0.991296648979, -0.008129999973, + 0.991296648979, 0.008803333156, 0.991296648979, 0.025736667216, + 0.991296648979, 0.042670000345, 0.991296648979, 0.059603333473, + 0.991296648979, 0.076536670327, 0.991296648979, 0.093469999731, + 0.991296648979, 0.110403336585, 0.991296648979, 0.127336665988, + 0.991296648979, 0.144270002842, 0.991296648979, 0.161203339696, + 0.991296648979, 0.178136661649, 0.991296648979, 0.195069998503, + 0.991296648979, 0.212003335357, 0.991296648979, 0.228936672211, + 0.991296648979, 0.245869994164, 0.991296648979, 0.262803345919, + 0.991296648979, 0.279736667871, 0.991296648979, 0.296669989824, + 0.991296648979, 0.313603341579, 0.991296648979, 0.330536663532, + 0.991296648979, 0.347469985485, 0.991296648979, 0.364403337240, + 0.991296648979, 0.381336659193, 0.991296648979, 0.398270010948, + 0.991296648979, 0.415203332901, 0.991296648979, 0.432136654854, + 0.991296648979, 0.449070006609, 0.991296648979, 0.466003328562, + 0.991296648979, 0.482936680317, 0.991296648979, 0.499870002270, + 0.991296648979, 0.516803324223, 0.991296648979, 0.533736646175, + 0.991296648979, 0.550670027733, 0.991296648979, 0.567603349686, + 0.991296648979, 0.584536671638, 0.991296648979, 0.601469993591, + 0.991296648979, 0.618403315544, 0.991296648979, 0.635336637497, + 0.991296648979, 0.652270019054, 0.991296648979, 0.669203341007, + 0.991296648979, 0.686136662960, 0.991296648979, 0.703069984913, + 0.991296648979, 0.720003306866, 0.991296648979, 0.736936688423, + 0.991296648979, 0.753870010376, 0.991296648979, 0.770803332329, + 0.991296648979, 0.787736654282, 0.991296648979, 0.804669976234, + 0.991296648979, 0.821603357792, 0.991296648979, 0.838536679745, + 0.991296648979, 0.855470001698, 0.991296648979, 0.872403323650, + 0.991296648979, 0.889336645603, 0.991296648979, 0.906270027161, + 0.991296648979, 0.923203349113, 0.991296648979, 0.940136671066, + 0.991296648979, 0.957069993019, 0.991296648979, 0.974003314972, + 0.991296648979, 0.990936636925, 0.991296648979, 1.007869958878, + 1.008229970932, -0.008129999973, 1.008229970932, 0.008803333156, + 1.008229970932, 0.025736667216, 1.008229970932, 0.042670000345, + 1.008229970932, 0.059603333473, 1.008229970932, 0.076536670327, + 1.008229970932, 0.093469999731, 1.008229970932, 0.110403336585, + 1.008229970932, 0.127336665988, 1.008229970932, 0.144270002842, + 1.008229970932, 0.161203339696, 1.008229970932, 0.178136661649, + 1.008229970932, 0.195069998503, 1.008229970932, 0.212003335357, + 1.008229970932, 0.228936672211, 1.008229970932, 0.245869994164, + 1.008229970932, 0.262803345919, 1.008229970932, 0.279736667871, + 1.008229970932, 0.296669989824, 1.008229970932, 0.313603341579, + 1.008229970932, 0.330536663532, 1.008229970932, 0.347469985485, + 1.008229970932, 0.364403337240, 1.008229970932, 0.381336659193, + 1.008229970932, 0.398270010948, 1.008229970932, 0.415203332901, + 1.008229970932, 0.432136654854, 1.008229970932, 0.449070006609, + 1.008229970932, 0.466003328562, 1.008229970932, 0.482936680317, + 1.008229970932, 0.499870002270, 1.008229970932, 0.516803324223, + 1.008229970932, 0.533736646175, 1.008229970932, 0.550670027733, + 1.008229970932, 0.567603349686, 1.008229970932, 0.584536671638, + 1.008229970932, 0.601469993591, 1.008229970932, 0.618403315544, + 1.008229970932, 0.635336637497, 1.008229970932, 0.652270019054, + 1.008229970932, 0.669203341007, 1.008229970932, 0.686136662960, + 1.008229970932, 0.703069984913, 1.008229970932, 0.720003306866, + 1.008229970932, 0.736936688423, 1.008229970932, 0.753870010376, + 1.008229970932, 0.770803332329, 1.008229970932, 0.787736654282, + 1.008229970932, 0.804669976234, 1.008229970932, 0.821603357792, + 1.008229970932, 0.838536679745, 1.008229970932, 0.855470001698, + 1.008229970932, 0.872403323650, 1.008229970932, 0.889336645603, + 1.008229970932, 0.906270027161, 1.008229970932, 0.923203349113, + 1.008229970932, 0.940136671066, 1.008229970932, 0.957069993019, + 1.008229970932, 0.974003314972, 1.008229970932, 0.990936636925, + 1.008229970932, 1.007869958878, +}; + +} /* namespace Pdraw */ + +#endif /* USE_GLES2 */ diff --git a/libpdraw/src/pdraw_gles2_hmd_texcoords_cockpitglasses_red.cpp b/libpdraw/src/pdraw_gles2_hmd_texcoords_cockpitglasses_red.cpp index edccebf..7ba1dbe 100644 --- a/libpdraw/src/pdraw_gles2_hmd_texcoords_cockpitglasses_red.cpp +++ b/libpdraw/src/pdraw_gles2_hmd_texcoords_cockpitglasses_red.cpp @@ -1,1907 +1,1907 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * OpenGL ES 2.0 HMD distortion correction - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * Extracted from FPVToolbox by Frédéric Bertolus - * https://github.com/niavok/fpvtoolbox - * and translated from Java to C++ - */ - -#ifdef USE_GLES2 - -namespace Pdraw { - -extern const float pdraw_gles2HmdTexCoordsCockpitglassesRed[7442] = { - 0.005400000140, 0.005200000014, 0.005400000140, 0.021696666256, - 0.005400000140, 0.038193333894, 0.005400000140, 0.054689999670, - 0.005400000140, 0.071186669171, 0.005400000140, 0.087683334947, - 0.005400000140, 0.104180000722, 0.005400000140, 0.120676666498, - 0.005400000140, 0.137173339725, 0.005400000140, 0.153669998050, - 0.005400000140, 0.170166671276, 0.005400000140, 0.186663329601, - 0.005400000140, 0.203160002828, 0.005400000140, 0.219656661153, - 0.005400000140, 0.236153334379, 0.005400000140, 0.252649992704, - 0.005400000140, 0.269146680832, 0.005400000140, 0.285643339157, - 0.005400000140, 0.302139997482, 0.005400000140, 0.318636655807, - 0.005400000140, 0.335133343935, 0.005400000140, 0.351630002260, - 0.005400000140, 0.368126660585, 0.005400000140, 0.384623318911, - 0.005400000140, 0.401120007038, 0.005400000140, 0.417616665363, - 0.005400000140, 0.434113323689, 0.005400000140, 0.450610011816, - 0.005400000140, 0.467106670141, 0.005400000140, 0.483603328466, - 0.005400000140, 0.500100016594, 0.005400000140, 0.516596674919, - 0.005400000140, 0.533093333244, 0.005400000140, 0.549589991570, - 0.005400000140, 0.566086649895, 0.005400000140, 0.582583308220, - 0.005400000140, 0.599080026150, 0.005400000140, 0.615576684475, - 0.005400000140, 0.632073342800, 0.005400000140, 0.648570001125, - 0.005400000140, 0.665066659451, 0.005400000140, 0.681563317776, - 0.005400000140, 0.698059976101, 0.005400000140, 0.714556694031, - 0.005400000140, 0.731053352356, 0.005400000140, 0.747550010681, - 0.005400000140, 0.764046669006, 0.005400000140, 0.780543327332, - 0.005400000140, 0.797039985657, 0.005400000140, 0.813536643982, - 0.005400000140, 0.830033361912, 0.005400000140, 0.846530020237, - 0.005400000140, 0.863026678562, 0.005400000140, 0.879523336887, - 0.005400000140, 0.896019995213, 0.005400000140, 0.912516653538, - 0.005400000140, 0.929013311863, 0.005400000140, 0.945510029793, - 0.005400000140, 0.962006688118, 0.005400000140, 0.978503346443, - 0.005400000140, 0.995000004768, 0.021896665916, 0.005200000014, - 0.021896665916, 0.021696666256, 0.021896665916, 0.038193333894, - 0.021896665916, 0.054689999670, 0.021896665916, 0.071186669171, - 0.021896665916, 0.087683334947, 0.021896665916, 0.104180000722, - 0.021896665916, 0.120676666498, 0.021896665916, 0.137173339725, - 0.021896665916, 0.153669998050, 0.021896665916, 0.170166671276, - 0.021896665916, 0.186663329601, 0.021896665916, 0.203160002828, - 0.021896665916, 0.219656661153, 0.021896665916, 0.236153334379, - 0.021896665916, 0.252649992704, 0.021896665916, 0.269146680832, - 0.021896665916, 0.285643339157, 0.021896665916, 0.302139997482, - 0.021896665916, 0.318636655807, 0.021896665916, 0.335133343935, - 0.021896665916, 0.351630002260, 0.021896665916, 0.368126660585, - 0.021896665916, 0.384623318911, 0.021896665916, 0.401120007038, - 0.021896665916, 0.417616665363, 0.021896665916, 0.434113323689, - 0.021896665916, 0.450610011816, 0.021896665916, 0.467106670141, - 0.021896665916, 0.483603328466, 0.021896665916, 0.500100016594, - 0.021896665916, 0.516596674919, 0.021896665916, 0.533093333244, - 0.021896665916, 0.549589991570, 0.021896665916, 0.566086649895, - 0.021896665916, 0.582583308220, 0.021896665916, 0.599080026150, - 0.021896665916, 0.615576684475, 0.021896665916, 0.632073342800, - 0.021896665916, 0.648570001125, 0.021896665916, 0.665066659451, - 0.021896665916, 0.681563317776, 0.021896665916, 0.698059976101, - 0.021896665916, 0.714556694031, 0.021896665916, 0.731053352356, - 0.021896665916, 0.747550010681, 0.021896665916, 0.764046669006, - 0.021896665916, 0.780543327332, 0.021896665916, 0.797039985657, - 0.021896665916, 0.813536643982, 0.021896665916, 0.830033361912, - 0.021896665916, 0.846530020237, 0.021896665916, 0.863026678562, - 0.021896665916, 0.879523336887, 0.021896665916, 0.896019995213, - 0.021896665916, 0.912516653538, 0.021896665916, 0.929013311863, - 0.021896665916, 0.945510029793, 0.021896665916, 0.962006688118, - 0.021896665916, 0.978503346443, 0.021896665916, 0.995000004768, - 0.038393333554, 0.005200000014, 0.038393333554, 0.021696666256, - 0.038393333554, 0.038193333894, 0.038393333554, 0.054689999670, - 0.038393333554, 0.071186669171, 0.038393333554, 0.087683334947, - 0.038393333554, 0.104180000722, 0.038393333554, 0.120676666498, - 0.038393333554, 0.137173339725, 0.038393333554, 0.153669998050, - 0.038393333554, 0.170166671276, 0.038393333554, 0.186663329601, - 0.038393333554, 0.203160002828, 0.038393333554, 0.219656661153, - 0.038393333554, 0.236153334379, 0.038393333554, 0.252649992704, - 0.038393333554, 0.269146680832, 0.038393333554, 0.285643339157, - 0.038393333554, 0.302139997482, 0.038393333554, 0.318636655807, - 0.038393333554, 0.335133343935, 0.038393333554, 0.351630002260, - 0.038393333554, 0.368126660585, 0.038393333554, 0.384623318911, - 0.038393333554, 0.401120007038, 0.038393333554, 0.417616665363, - 0.038393333554, 0.434113323689, 0.038393333554, 0.450610011816, - 0.038393333554, 0.467106670141, 0.038393333554, 0.483603328466, - 0.038393333554, 0.500100016594, 0.038393333554, 0.516596674919, - 0.038393333554, 0.533093333244, 0.038393333554, 0.549589991570, - 0.038393333554, 0.566086649895, 0.038393333554, 0.582583308220, - 0.038393333554, 0.599080026150, 0.038393333554, 0.615576684475, - 0.038393333554, 0.632073342800, 0.038393333554, 0.648570001125, - 0.038393333554, 0.665066659451, 0.038393333554, 0.681563317776, - 0.038393333554, 0.698059976101, 0.038393333554, 0.714556694031, - 0.038393333554, 0.731053352356, 0.038393333554, 0.747550010681, - 0.038393333554, 0.764046669006, 0.038393333554, 0.780543327332, - 0.038393333554, 0.797039985657, 0.038393333554, 0.813536643982, - 0.038393333554, 0.830033361912, 0.038393333554, 0.846530020237, - 0.038393333554, 0.863026678562, 0.038393333554, 0.879523336887, - 0.038393333554, 0.896019995213, 0.038393333554, 0.912516653538, - 0.038393333554, 0.929013311863, 0.038393333554, 0.945510029793, - 0.038393333554, 0.962006688118, 0.038393333554, 0.978503346443, - 0.038393333554, 0.995000004768, 0.054889999330, 0.005200000014, - 0.054889999330, 0.021696666256, 0.054889999330, 0.038193333894, - 0.054889999330, 0.054689999670, 0.054889999330, 0.071186669171, - 0.054889999330, 0.087683334947, 0.054889999330, 0.104180000722, - 0.054889999330, 0.120676666498, 0.054889999330, 0.137173339725, - 0.054889999330, 0.153669998050, 0.054889999330, 0.170166671276, - 0.054889999330, 0.186663329601, 0.054889999330, 0.203160002828, - 0.054889999330, 0.219656661153, 0.054889999330, 0.236153334379, - 0.054889999330, 0.252649992704, 0.054889999330, 0.269146680832, - 0.054889999330, 0.285643339157, 0.054889999330, 0.302139997482, - 0.054889999330, 0.318636655807, 0.054889999330, 0.335133343935, - 0.054889999330, 0.351630002260, 0.054889999330, 0.368126660585, - 0.054889999330, 0.384623318911, 0.054889999330, 0.401120007038, - 0.054889999330, 0.417616665363, 0.054889999330, 0.434113323689, - 0.054889999330, 0.450610011816, 0.054889999330, 0.467106670141, - 0.054889999330, 0.483603328466, 0.054889999330, 0.500100016594, - 0.054889999330, 0.516596674919, 0.054889999330, 0.533093333244, - 0.054889999330, 0.549589991570, 0.054889999330, 0.566086649895, - 0.054889999330, 0.582583308220, 0.054889999330, 0.599080026150, - 0.054889999330, 0.615576684475, 0.054889999330, 0.632073342800, - 0.054889999330, 0.648570001125, 0.054889999330, 0.665066659451, - 0.054889999330, 0.681563317776, 0.054889999330, 0.698059976101, - 0.054889999330, 0.714556694031, 0.054889999330, 0.731053352356, - 0.054889999330, 0.747550010681, 0.054889999330, 0.764046669006, - 0.054889999330, 0.780543327332, 0.054889999330, 0.797039985657, - 0.054889999330, 0.813536643982, 0.054889999330, 0.830033361912, - 0.054889999330, 0.846530020237, 0.054889999330, 0.863026678562, - 0.054889999330, 0.879523336887, 0.054889999330, 0.896019995213, - 0.054889999330, 0.912516653538, 0.054889999330, 0.929013311863, - 0.054889999330, 0.945510029793, 0.054889999330, 0.962006688118, - 0.054889999330, 0.978503346443, 0.054889999330, 0.995000004768, - 0.071386665106, 0.005200000014, 0.071386665106, 0.021696666256, - 0.071386665106, 0.038193333894, 0.071386665106, 0.054689999670, - 0.071386665106, 0.071186669171, 0.071386665106, 0.087683334947, - 0.071386665106, 0.104180000722, 0.071386665106, 0.120676666498, - 0.071386665106, 0.137173339725, 0.071386665106, 0.153669998050, - 0.071386665106, 0.170166671276, 0.071386665106, 0.186663329601, - 0.071386665106, 0.203160002828, 0.071386665106, 0.219656661153, - 0.071386665106, 0.236153334379, 0.071386665106, 0.252649992704, - 0.071386665106, 0.269146680832, 0.071386665106, 0.285643339157, - 0.071386665106, 0.302139997482, 0.071386665106, 0.318636655807, - 0.071386665106, 0.335133343935, 0.071386665106, 0.351630002260, - 0.071386665106, 0.368126660585, 0.071386665106, 0.384623318911, - 0.071386665106, 0.401120007038, 0.071386665106, 0.417616665363, - 0.071386665106, 0.434113323689, 0.071386665106, 0.450610011816, - 0.071386665106, 0.467106670141, 0.071386665106, 0.483603328466, - 0.071386665106, 0.500100016594, 0.071386665106, 0.516596674919, - 0.071386665106, 0.533093333244, 0.071386665106, 0.549589991570, - 0.071386665106, 0.566086649895, 0.071386665106, 0.582583308220, - 0.071386665106, 0.599080026150, 0.071386665106, 0.615576684475, - 0.071386665106, 0.632073342800, 0.071386665106, 0.648570001125, - 0.071386665106, 0.665066659451, 0.071386665106, 0.681563317776, - 0.071386665106, 0.698059976101, 0.071386665106, 0.714556694031, - 0.071386665106, 0.731053352356, 0.071386665106, 0.747550010681, - 0.071386665106, 0.764046669006, 0.071386665106, 0.780543327332, - 0.071386665106, 0.797039985657, 0.071386665106, 0.813536643982, - 0.071386665106, 0.830033361912, 0.071386665106, 0.846530020237, - 0.071386665106, 0.863026678562, 0.071386665106, 0.879523336887, - 0.071386665106, 0.896019995213, 0.071386665106, 0.912516653538, - 0.071386665106, 0.929013311863, 0.071386665106, 0.945510029793, - 0.071386665106, 0.962006688118, 0.071386665106, 0.978503346443, - 0.071386665106, 0.995000004768, 0.087883330882, 0.005200000014, - 0.087883330882, 0.021696666256, 0.087883330882, 0.038193333894, - 0.087883330882, 0.054689999670, 0.087883330882, 0.071186669171, - 0.087883330882, 0.087683334947, 0.087883330882, 0.104180000722, - 0.087883330882, 0.120676666498, 0.087883330882, 0.137173339725, - 0.087883330882, 0.153669998050, 0.087883330882, 0.170166671276, - 0.087883330882, 0.186663329601, 0.087883330882, 0.203160002828, - 0.087883330882, 0.219656661153, 0.087883330882, 0.236153334379, - 0.087883330882, 0.252649992704, 0.087883330882, 0.269146680832, - 0.087883330882, 0.285643339157, 0.087883330882, 0.302139997482, - 0.087883330882, 0.318636655807, 0.087883330882, 0.335133343935, - 0.087883330882, 0.351630002260, 0.087883330882, 0.368126660585, - 0.087883330882, 0.384623318911, 0.087883330882, 0.401120007038, - 0.087883330882, 0.417616665363, 0.087883330882, 0.434113323689, - 0.087883330882, 0.450610011816, 0.087883330882, 0.467106670141, - 0.087883330882, 0.483603328466, 0.087883330882, 0.500100016594, - 0.087883330882, 0.516596674919, 0.087883330882, 0.533093333244, - 0.087883330882, 0.549589991570, 0.087883330882, 0.566086649895, - 0.087883330882, 0.582583308220, 0.087883330882, 0.599080026150, - 0.087883330882, 0.615576684475, 0.087883330882, 0.632073342800, - 0.087883330882, 0.648570001125, 0.087883330882, 0.665066659451, - 0.087883330882, 0.681563317776, 0.087883330882, 0.698059976101, - 0.087883330882, 0.714556694031, 0.087883330882, 0.731053352356, - 0.087883330882, 0.747550010681, 0.087883330882, 0.764046669006, - 0.087883330882, 0.780543327332, 0.087883330882, 0.797039985657, - 0.087883330882, 0.813536643982, 0.087883330882, 0.830033361912, - 0.087883330882, 0.846530020237, 0.087883330882, 0.863026678562, - 0.087883330882, 0.879523336887, 0.087883330882, 0.896019995213, - 0.087883330882, 0.912516653538, 0.087883330882, 0.929013311863, - 0.087883330882, 0.945510029793, 0.087883330882, 0.962006688118, - 0.087883330882, 0.978503346443, 0.087883330882, 0.995000004768, - 0.104379996657, 0.005200000014, 0.104379996657, 0.021696666256, - 0.104379996657, 0.038193333894, 0.104379996657, 0.054689999670, - 0.104379996657, 0.071186669171, 0.104379996657, 0.087683334947, - 0.104379996657, 0.104180000722, 0.104379996657, 0.120676666498, - 0.104379996657, 0.137173339725, 0.104379996657, 0.153669998050, - 0.104379996657, 0.170166671276, 0.104379996657, 0.186663329601, - 0.104379996657, 0.203160002828, 0.104379996657, 0.219656661153, - 0.104379996657, 0.236153334379, 0.104379996657, 0.252649992704, - 0.104379996657, 0.269146680832, 0.104379996657, 0.285643339157, - 0.104379996657, 0.302139997482, 0.104379996657, 0.318636655807, - 0.104379996657, 0.335133343935, 0.104379996657, 0.351630002260, - 0.104379996657, 0.368126660585, 0.104379996657, 0.384623318911, - 0.104379996657, 0.401120007038, 0.104379996657, 0.417616665363, - 0.104379996657, 0.434113323689, 0.104379996657, 0.450610011816, - 0.104379996657, 0.467106670141, 0.104379996657, 0.483603328466, - 0.104379996657, 0.500100016594, 0.104379996657, 0.516596674919, - 0.104379996657, 0.533093333244, 0.104379996657, 0.549589991570, - 0.104379996657, 0.566086649895, 0.104379996657, 0.582583308220, - 0.104379996657, 0.599080026150, 0.104379996657, 0.615576684475, - 0.104379996657, 0.632073342800, 0.104379996657, 0.648570001125, - 0.104379996657, 0.665066659451, 0.104379996657, 0.681563317776, - 0.104379996657, 0.698059976101, 0.104379996657, 0.714556694031, - 0.104379996657, 0.731053352356, 0.104379996657, 0.747550010681, - 0.104379996657, 0.764046669006, 0.104379996657, 0.780543327332, - 0.104379996657, 0.797039985657, 0.104379996657, 0.813536643982, - 0.104379996657, 0.830033361912, 0.104379996657, 0.846530020237, - 0.104379996657, 0.863026678562, 0.104379996657, 0.879523336887, - 0.104379996657, 0.896019995213, 0.104379996657, 0.912516653538, - 0.104379996657, 0.929013311863, 0.104379996657, 0.945510029793, - 0.104379996657, 0.962006688118, 0.104379996657, 0.978503346443, - 0.104379996657, 0.995000004768, 0.120876669884, 0.005200000014, - 0.120876669884, 0.021696666256, 0.120876669884, 0.038193333894, - 0.120876669884, 0.054689999670, 0.120876669884, 0.071186669171, - 0.120876669884, 0.087683334947, 0.120876669884, 0.104180000722, - 0.120876669884, 0.120676666498, 0.120876669884, 0.137173339725, - 0.120876669884, 0.153669998050, 0.120876669884, 0.170166671276, - 0.120876669884, 0.186663329601, 0.120876669884, 0.203160002828, - 0.120876669884, 0.219656661153, 0.120876669884, 0.236153334379, - 0.120876669884, 0.252649992704, 0.120876669884, 0.269146680832, - 0.120876669884, 0.285643339157, 0.120876669884, 0.302139997482, - 0.120876669884, 0.318636655807, 0.120876669884, 0.335133343935, - 0.120876669884, 0.351630002260, 0.120876669884, 0.368126660585, - 0.120876669884, 0.384623318911, 0.120876669884, 0.401120007038, - 0.120876669884, 0.417616665363, 0.120876669884, 0.434113323689, - 0.120876669884, 0.450610011816, 0.120876669884, 0.467106670141, - 0.120876669884, 0.483603328466, 0.120876669884, 0.500100016594, - 0.120876669884, 0.516596674919, 0.120876669884, 0.533093333244, - 0.120876669884, 0.549589991570, 0.120876669884, 0.566086649895, - 0.120876669884, 0.582583308220, 0.120876669884, 0.599080026150, - 0.120876669884, 0.615576684475, 0.120876669884, 0.632073342800, - 0.120876669884, 0.648570001125, 0.120876669884, 0.665066659451, - 0.120876669884, 0.681563317776, 0.120876669884, 0.698059976101, - 0.120876669884, 0.714556694031, 0.120876669884, 0.731053352356, - 0.120876669884, 0.747550010681, 0.120876669884, 0.764046669006, - 0.120876669884, 0.780543327332, 0.120876669884, 0.797039985657, - 0.120876669884, 0.813536643982, 0.120876669884, 0.830033361912, - 0.120876669884, 0.846530020237, 0.120876669884, 0.863026678562, - 0.120876669884, 0.879523336887, 0.120876669884, 0.896019995213, - 0.120876669884, 0.912516653538, 0.120876669884, 0.929013311863, - 0.120876669884, 0.945510029793, 0.120876669884, 0.962006688118, - 0.120876669884, 0.978503346443, 0.120876669884, 0.995000004768, - 0.137373328209, 0.005200000014, 0.137373328209, 0.021696666256, - 0.137373328209, 0.038193333894, 0.137373328209, 0.054689999670, - 0.137373328209, 0.071186669171, 0.137373328209, 0.087683334947, - 0.137373328209, 0.104180000722, 0.137373328209, 0.120676666498, - 0.137373328209, 0.137173339725, 0.137373328209, 0.153669998050, - 0.137373328209, 0.170166671276, 0.137373328209, 0.186663329601, - 0.137373328209, 0.203160002828, 0.137373328209, 0.219656661153, - 0.137373328209, 0.236153334379, 0.137373328209, 0.252649992704, - 0.137373328209, 0.269146680832, 0.137373328209, 0.285643339157, - 0.137373328209, 0.302139997482, 0.137373328209, 0.318636655807, - 0.137373328209, 0.335133343935, 0.137373328209, 0.351630002260, - 0.137373328209, 0.368126660585, 0.137373328209, 0.384623318911, - 0.137373328209, 0.401120007038, 0.137373328209, 0.417616665363, - 0.137373328209, 0.434113323689, 0.137373328209, 0.450610011816, - 0.137373328209, 0.467106670141, 0.137373328209, 0.483603328466, - 0.137373328209, 0.500100016594, 0.137373328209, 0.516596674919, - 0.137373328209, 0.533093333244, 0.137373328209, 0.549589991570, - 0.137373328209, 0.566086649895, 0.137373328209, 0.582583308220, - 0.137373328209, 0.599080026150, 0.137373328209, 0.615576684475, - 0.137373328209, 0.632073342800, 0.137373328209, 0.648570001125, - 0.137373328209, 0.665066659451, 0.137373328209, 0.681563317776, - 0.137373328209, 0.698059976101, 0.137373328209, 0.714556694031, - 0.137373328209, 0.731053352356, 0.137373328209, 0.747550010681, - 0.137373328209, 0.764046669006, 0.137373328209, 0.780543327332, - 0.137373328209, 0.797039985657, 0.137373328209, 0.813536643982, - 0.137373328209, 0.830033361912, 0.137373328209, 0.846530020237, - 0.137373328209, 0.863026678562, 0.137373328209, 0.879523336887, - 0.137373328209, 0.896019995213, 0.137373328209, 0.912516653538, - 0.137373328209, 0.929013311863, 0.137373328209, 0.945510029793, - 0.137373328209, 0.962006688118, 0.137373328209, 0.978503346443, - 0.137373328209, 0.995000004768, 0.153870001435, 0.005200000014, - 0.153870001435, 0.021696666256, 0.153870001435, 0.038193333894, - 0.153870001435, 0.054689999670, 0.153870001435, 0.071186669171, - 0.153870001435, 0.087683334947, 0.153870001435, 0.104180000722, - 0.153870001435, 0.120676666498, 0.153870001435, 0.137173339725, - 0.153870001435, 0.153669998050, 0.153870001435, 0.170166671276, - 0.153870001435, 0.186663329601, 0.153870001435, 0.203160002828, - 0.153870001435, 0.219656661153, 0.153870001435, 0.236153334379, - 0.153870001435, 0.252649992704, 0.153870001435, 0.269146680832, - 0.153870001435, 0.285643339157, 0.153870001435, 0.302139997482, - 0.153870001435, 0.318636655807, 0.153870001435, 0.335133343935, - 0.153870001435, 0.351630002260, 0.153870001435, 0.368126660585, - 0.153870001435, 0.384623318911, 0.153870001435, 0.401120007038, - 0.153870001435, 0.417616665363, 0.153870001435, 0.434113323689, - 0.153870001435, 0.450610011816, 0.153870001435, 0.467106670141, - 0.153759777546, 0.483598083258, 0.153758004308, 0.500100016594, - 0.153759777546, 0.516601920128, 0.153870001435, 0.533093333244, - 0.153870001435, 0.549589991570, 0.153870001435, 0.566086649895, - 0.153870001435, 0.582583308220, 0.153870001435, 0.599080026150, - 0.153870001435, 0.615576684475, 0.153870001435, 0.632073342800, - 0.153870001435, 0.648570001125, 0.153870001435, 0.665066659451, - 0.153870001435, 0.681563317776, 0.153870001435, 0.698059976101, - 0.153870001435, 0.714556694031, 0.153870001435, 0.731053352356, - 0.153870001435, 0.747550010681, 0.153870001435, 0.764046669006, - 0.153870001435, 0.780543327332, 0.153870001435, 0.797039985657, - 0.153870001435, 0.813536643982, 0.153870001435, 0.830033361912, - 0.153870001435, 0.846530020237, 0.153870001435, 0.863026678562, - 0.153870001435, 0.879523336887, 0.153870001435, 0.896019995213, - 0.153870001435, 0.912516653538, 0.153870001435, 0.929013311863, - 0.153870001435, 0.945510029793, 0.153870001435, 0.962006688118, - 0.153870001435, 0.978503346443, 0.153870001435, 0.995000004768, - 0.170366659760, 0.005200000014, 0.170366659760, 0.021696666256, - 0.170366659760, 0.038193333894, 0.170366659760, 0.054689999670, - 0.170366659760, 0.071186669171, 0.170366659760, 0.087683334947, - 0.170366659760, 0.104180000722, 0.170366659760, 0.120676666498, - 0.170366659760, 0.137173339725, 0.170366659760, 0.153669998050, - 0.170366659760, 0.170166671276, 0.170366659760, 0.186663329601, - 0.170366659760, 0.203160002828, 0.170366659760, 0.219656661153, - 0.170366659760, 0.236153334379, 0.170366659760, 0.252649992704, - 0.170366659760, 0.269146680832, 0.170366659760, 0.285643339157, - 0.170366659760, 0.302139997482, 0.170366659760, 0.318636655807, - 0.170366659760, 0.335133343935, 0.170366659760, 0.351630002260, - 0.170366659760, 0.368126660585, 0.170366659760, 0.384623318911, - 0.170251503587, 0.401085466146, 0.170232653618, 0.417583167553, - 0.170217052102, 0.434083402157, 0.170204803348, 0.450585722923, - 0.170195981860, 0.467089593410, 0.170190662146, 0.483594536781, - 0.170188888907, 0.500100016594, 0.170190662146, 0.516605496407, - 0.170195981860, 0.533110380173, 0.170204803348, 0.549614250660, - 0.170217052102, 0.566116571426, 0.170232653618, 0.582616806030, - 0.170251503587, 0.599114537239, 0.170366659760, 0.615576684475, - 0.170366659760, 0.632073342800, 0.170366659760, 0.648570001125, - 0.170366659760, 0.665066659451, 0.170366659760, 0.681563317776, - 0.170366659760, 0.698059976101, 0.170366659760, 0.714556694031, - 0.170366659760, 0.731053352356, 0.170366659760, 0.747550010681, - 0.170366659760, 0.764046669006, 0.170366659760, 0.780543327332, - 0.170366659760, 0.797039985657, 0.170366659760, 0.813536643982, - 0.170366659760, 0.830033361912, 0.170366659760, 0.846530020237, - 0.170366659760, 0.863026678562, 0.170366659760, 0.879523336887, - 0.170366659760, 0.896019995213, 0.170366659760, 0.912516653538, - 0.170366659760, 0.929013311863, 0.170366659760, 0.945510029793, - 0.170366659760, 0.962006688118, 0.170366659760, 0.978503346443, - 0.170366659760, 0.995000004768, 0.186863332987, 0.005200000014, - 0.186863332987, 0.021696666256, 0.186863332987, 0.038193333894, - 0.186863332987, 0.054689999670, 0.186863332987, 0.071186669171, - 0.186863332987, 0.087683334947, 0.186863332987, 0.104180000722, - 0.186863332987, 0.120676666498, 0.186863332987, 0.137173339725, - 0.186863332987, 0.153669998050, 0.186863332987, 0.170166671276, - 0.186863332987, 0.186663329601, 0.186863332987, 0.203160002828, - 0.186863332987, 0.219656661153, 0.186863332987, 0.236153334379, - 0.186863332987, 0.252649992704, 0.186863332987, 0.269146680832, - 0.186863332987, 0.285643339157, 0.186863332987, 0.302139997482, - 0.186863332987, 0.318636655807, 0.186863332987, 0.335133343935, - 0.186763614416, 0.351582765579, 0.186736032367, 0.368073076010, - 0.186711221933, 0.384567290545, 0.186689361930, 0.401065051556, - 0.186703175306, 0.417574524879, 0.186692789197, 0.434077441692, - 0.186684638262, 0.450581789017, 0.186678767204, 0.467087239027, - 0.186675220728, 0.483593434095, 0.186674043536, 0.500100016594, - 0.186675220728, 0.516606569290, 0.186678767204, 0.533112764359, - 0.186684638262, 0.549618244171, 0.186692789197, 0.566122591496, - 0.186703175306, 0.582625508308, 0.186689361930, 0.599134922028, - 0.186711221933, 0.615632712841, 0.186736032367, 0.632126927376, - 0.186763614416, 0.648617267609, 0.186863332987, 0.665066659451, - 0.186863332987, 0.681563317776, 0.186863332987, 0.698059976101, - 0.186863332987, 0.714556694031, 0.186863332987, 0.731053352356, - 0.186863332987, 0.747550010681, 0.186863332987, 0.764046669006, - 0.186863332987, 0.780543327332, 0.186863332987, 0.797039985657, - 0.186863332987, 0.813536643982, 0.186863332987, 0.830033361912, - 0.186863332987, 0.846530020237, 0.186863332987, 0.863026678562, - 0.186863332987, 0.879523336887, 0.186863332987, 0.896019995213, - 0.186863332987, 0.912516653538, 0.186863332987, 0.929013311863, - 0.186863332987, 0.945510029793, 0.186863332987, 0.962006688118, - 0.186863332987, 0.978503346443, 0.186863332987, 0.995000004768, - 0.203360006213, 0.005200000014, 0.203360006213, 0.021696666256, - 0.203360006213, 0.038193333894, 0.203360006213, 0.054689999670, - 0.203360006213, 0.071186669171, 0.203360006213, 0.087683334947, - 0.203360006213, 0.104180000722, 0.203360006213, 0.120676666498, - 0.203360006213, 0.137173339725, 0.203360006213, 0.153669998050, - 0.203360006213, 0.170166671276, 0.203360006213, 0.186663329601, - 0.203360006213, 0.203160002828, 0.203360006213, 0.219656661153, - 0.203360006213, 0.236153334379, 0.203360006213, 0.252649992704, - 0.203360006213, 0.269146680832, 0.203360006213, 0.285643339157, - 0.203360006213, 0.302139997482, 0.203360006213, 0.318636655807, - 0.203237846494, 0.335065454245, 0.203207969666, 0.351553976536, - 0.203180655837, 0.368046969175, 0.203194037080, 0.384558796883, - 0.203179538250, 0.401059836149, 0.203167080879, 0.417563080788, - 0.203156739473, 0.434068173170, 0.203056484461, 0.450559407473, - 0.203041821718, 0.467071324587, 0.203032955527, 0.483585178852, - 0.203030005097, 0.500100016594, 0.203032955527, 0.516614854336, - 0.203041821718, 0.533128678799, 0.203056484461, 0.549640595913, - 0.203156739473, 0.566131830215, 0.203167080879, 0.582636892796, - 0.203179538250, 0.599140167236, 0.203194037080, 0.615641236305, - 0.203180655837, 0.632153034210, 0.203207969666, 0.648645997047, - 0.203237846494, 0.665134549141, 0.203360006213, 0.681563317776, - 0.203360006213, 0.698059976101, 0.203360006213, 0.714556694031, - 0.203360006213, 0.731053352356, 0.203360006213, 0.747550010681, - 0.203360006213, 0.764046669006, 0.203360006213, 0.780543327332, - 0.203360006213, 0.797039985657, 0.203360006213, 0.813536643982, - 0.203360006213, 0.830033361912, 0.203360006213, 0.846530020237, - 0.203360006213, 0.863026678562, 0.203360006213, 0.879523336887, - 0.203360006213, 0.896019995213, 0.203360006213, 0.912516653538, - 0.203360006213, 0.929013311863, 0.203360006213, 0.945510029793, - 0.203360006213, 0.962006688118, 0.203360006213, 0.978503346443, - 0.203360006213, 0.995000004768, 0.219856664538, 0.005200000014, - 0.219856664538, 0.021696666256, 0.219856664538, 0.038193333894, - 0.219856664538, 0.054689999670, 0.219856664538, 0.071186669171, - 0.219856664538, 0.087683334947, 0.219856664538, 0.104180000722, - 0.219856664538, 0.120676666498, 0.219856664538, 0.137173339725, - 0.219856664538, 0.153669998050, 0.219856664538, 0.170166671276, - 0.219856664538, 0.186663329601, 0.219856664538, 0.203160002828, - 0.219856664538, 0.219656661153, 0.219856664538, 0.236153334379, - 0.219856664538, 0.252649992704, 0.219856664538, 0.269146680832, - 0.219856664538, 0.285643339157, 0.219754427671, 0.302067846060, - 0.219720572233, 0.318548619747, 0.219688817859, 0.335034608841, - 0.219696775079, 0.351545363665, 0.219678759575, 0.368042945862, - 0.219662502408, 0.384543389082, 0.219547793269, 0.401010990143, - 0.219516798854, 0.417516708374, 0.219491034746, 0.434027314186, - 0.219470724463, 0.450541883707, 0.219456076622, 0.467059522867, - 0.219447225332, 0.483579248190, 0.219444260001, 0.500100016594, - 0.219447225332, 0.516620755196, 0.219456076622, 0.533140480518, - 0.219470724463, 0.549658119678, 0.219491034746, 0.566172719002, - 0.219516798854, 0.582683265209, 0.219547793269, 0.599188983440, - 0.219662502408, 0.615656614304, 0.219678759575, 0.632157027721, - 0.219696775079, 0.648654639721, 0.219688817859, 0.665165424347, - 0.219720572233, 0.681651413441, 0.219754427671, 0.698132157326, - 0.219856664538, 0.714556694031, 0.219856664538, 0.731053352356, - 0.219856664538, 0.747550010681, 0.219856664538, 0.764046669006, - 0.219856664538, 0.780543327332, 0.219856664538, 0.797039985657, - 0.219856664538, 0.813536643982, 0.219856664538, 0.830033361912, - 0.219856664538, 0.846530020237, 0.219856664538, 0.863026678562, - 0.219856664538, 0.879523336887, 0.219856664538, 0.896019995213, - 0.219856664538, 0.912516653538, 0.219856664538, 0.929013311863, - 0.219856664538, 0.945510029793, 0.219856664538, 0.962006688118, - 0.219856664538, 0.978503346443, 0.219856664538, 0.995000004768, - 0.236353337765, 0.005200000014, 0.236353337765, 0.021696666256, - 0.236353337765, 0.038193333894, 0.236353337765, 0.054689999670, - 0.236353337765, 0.071186669171, 0.236353337765, 0.087683334947, - 0.236353337765, 0.104180000722, 0.236353337765, 0.120676666498, - 0.236353337765, 0.137173339725, 0.236353337765, 0.153669998050, - 0.236353337765, 0.170166671276, 0.236353337765, 0.186663329601, - 0.236353337765, 0.203160002828, 0.236353337765, 0.219656661153, - 0.236353337765, 0.236153334379, 0.236353337765, 0.252649992704, - 0.236353337765, 0.269146680832, 0.236246123910, 0.285556226969, - 0.236211106181, 0.302033334970, 0.236209720373, 0.318537920713, - 0.236188918352, 0.335030585527, 0.236169561744, 0.351526618004, - 0.236049428582, 0.367974728346, 0.236009195447, 0.384472787380, - 0.235973536968, 0.400977581739, 0.235827565193, 0.417452365160, - 0.235781371593, 0.433970332146, 0.235744923353, 0.450495928526, - 0.235718578100, 0.467027336359, 0.235702663660, 0.483562678099, - 0.235697329044, 0.500100016594, 0.235702663660, 0.516637325287, - 0.235718578100, 0.533172667027, 0.235744923353, 0.549704074860, - 0.235781371593, 0.566229641438, 0.235827565193, 0.582747638226, - 0.235973536968, 0.599222421646, 0.236009195447, 0.615727245808, - 0.236049428582, 0.632225275040, 0.236169561744, 0.648673355579, - 0.236188918352, 0.665169417858, 0.236209720373, 0.681662082672, - 0.236211106181, 0.698166668415, 0.236246123910, 0.714643776417, - 0.236353337765, 0.731053352356, 0.236353337765, 0.747550010681, - 0.236353337765, 0.764046669006, 0.236353337765, 0.780543327332, - 0.236353337765, 0.797039985657, 0.236353337765, 0.813536643982, - 0.236353337765, 0.830033361912, 0.236353337765, 0.846530020237, - 0.236353337765, 0.863026678562, 0.236353337765, 0.879523336887, - 0.236353337765, 0.896019995213, 0.236353337765, 0.912516653538, - 0.236353337765, 0.929013311863, 0.236353337765, 0.945510029793, - 0.236353337765, 0.962006688118, 0.236353337765, 0.978503346443, - 0.236353337765, 0.995000004768, 0.252849996090, 0.005200000014, - 0.252849996090, 0.021696666256, 0.252849996090, 0.038193333894, - 0.252849996090, 0.054689999670, 0.252849996090, 0.071186669171, - 0.252849996090, 0.087683334947, 0.252849996090, 0.104180000722, - 0.252849996090, 0.120676666498, 0.252849996090, 0.137173339725, - 0.252849996090, 0.153669998050, 0.252849996090, 0.170166671276, - 0.252849996090, 0.186663329601, 0.252849996090, 0.203160002828, - 0.252849996090, 0.219656661153, 0.252849996090, 0.236153334379, - 0.252849996090, 0.252649992704, 0.252744317055, 0.269048035145, - 0.252708643675, 0.285520821810, 0.252707988024, 0.302026391029, - 0.252686381340, 0.318516671658, 0.252577453852, 0.334951639175, - 0.252529919147, 0.351437956095, 0.252486109734, 0.367932587862, - 0.252323478460, 0.384377628565, 0.252259880304, 0.400883942842, - 0.252204835415, 0.417401611805, 0.252158880234, 0.433929026127, - 0.252403050661, 0.450520604849, 0.252405971289, 0.467047452927, - 0.252407729626, 0.483573853970, 0.252408325672, 0.500100016594, - 0.252407729626, 0.516626179218, 0.252405971289, 0.533152520657, - 0.252403050661, 0.549679398537, 0.252158880234, 0.566270947456, - 0.252204835415, 0.582798421383, 0.252259880304, 0.599316060543, - 0.252323478460, 0.615822374821, 0.252486109734, 0.632267415524, - 0.252529919147, 0.648762047291, 0.252577453852, 0.665248334408, - 0.252686381340, 0.681683301926, 0.252707988024, 0.698173582554, - 0.252708643675, 0.714679181576, 0.252744317055, 0.731151998043, - 0.252849996090, 0.747550010681, 0.252849996090, 0.764046669006, - 0.252849996090, 0.780543327332, 0.252849996090, 0.797039985657, - 0.252849996090, 0.813536643982, 0.252849996090, 0.830033361912, - 0.252849996090, 0.846530020237, 0.252849996090, 0.863026678562, - 0.252849996090, 0.879523336887, 0.252849996090, 0.896019995213, - 0.252849996090, 0.912516653538, 0.252849996090, 0.929013311863, - 0.252849996090, 0.945510029793, 0.252849996090, 0.962006688118, - 0.252849996090, 0.978503346443, 0.252849996090, 0.995000004768, - 0.269346654415, 0.005200000014, 0.269346654415, 0.021696666256, - 0.269346654415, 0.038193333894, 0.269346654415, 0.054689999670, - 0.269346654415, 0.071186669171, 0.269346654415, 0.087683334947, - 0.269346654415, 0.104180000722, 0.269346654415, 0.120676666498, - 0.269346654415, 0.137173339725, 0.269346654415, 0.153669998050, - 0.269346654415, 0.170166671276, 0.269346654415, 0.186663329601, - 0.269346654415, 0.203160002828, 0.269346654415, 0.219656661153, - 0.269346654415, 0.236153334379, 0.269248008728, 0.252544313669, - 0.269212216139, 0.269012212753, 0.269210666418, 0.285517036915, - 0.269188582897, 0.302004486322, 0.269073784351, 0.318422257900, - 0.269024014473, 0.334902882576, 0.268868744373, 0.351322770119, - 0.268791258335, 0.367809295654, 0.268720775843, 0.384310394526, - 0.268930613995, 0.400941699743, 0.268936663866, 0.417470246553, - 0.268941730261, 0.433997631073, 0.268945753574, 0.450524091721, - 0.268948674202, 0.467049807310, 0.268950432539, 0.483575046062, - 0.268951028585, 0.500100016594, 0.268950432539, 0.516624987125, - 0.268948674202, 0.533150196075, 0.268945753574, 0.549675881863, - 0.268941730261, 0.566202342510, 0.268936663866, 0.582729756832, - 0.268930613995, 0.599258303642, 0.268720775843, 0.615889608860, - 0.268791258335, 0.632390737534, 0.268868744373, 0.648877263069, - 0.269024014473, 0.665297150612, 0.269073784351, 0.681777715683, - 0.269188582897, 0.698195517063, 0.269210666418, 0.714682936668, - 0.269212216139, 0.731187760830, 0.269248008728, 0.747655689716, - 0.269346654415, 0.764046669006, 0.269346654415, 0.780543327332, - 0.269346654415, 0.797039985657, 0.269346654415, 0.813536643982, - 0.269346654415, 0.830033361912, 0.269346654415, 0.846530020237, - 0.269346654415, 0.863026678562, 0.269346654415, 0.879523336887, - 0.269346654415, 0.896019995213, 0.269346654415, 0.912516653538, - 0.269346654415, 0.929013311863, 0.269346654415, 0.945510029793, - 0.269346654415, 0.962006688118, 0.269346654415, 0.978503346443, - 0.269346654415, 0.995000004768, 0.285843342543, 0.005200000014, - 0.285843342543, 0.021696666256, 0.285843342543, 0.038193333894, - 0.285843342543, 0.054689999670, 0.285843342543, 0.071186669171, - 0.285843342543, 0.087683334947, 0.285843342543, 0.104180000722, - 0.285843342543, 0.120676666498, 0.285843342543, 0.137173339725, - 0.285843342543, 0.153669998050, 0.285843342543, 0.170166671276, - 0.285843342543, 0.186663329601, 0.285843342543, 0.203160002828, - 0.285843342543, 0.219656661153, 0.285756230354, 0.236046120524, - 0.285720825195, 0.252508640289, 0.285717040300, 0.269010663033, - 0.285694867373, 0.285494863987, 0.285581260920, 0.301898092031, - 0.285530239344, 0.318371742964, 0.285365968943, 0.334766119719, - 0.285284191370, 0.351242899895, 0.285456478596, 0.367888599634, - 0.285464167595, 0.384419173002, 0.285471051931, 0.400948196650, - 0.285477072001, 0.417475789785, 0.285482108593, 0.434002190828, - 0.285486102104, 0.450527548790, 0.285489022732, 0.467052161694, - 0.285490781069, 0.483576208353, 0.285491377115, 0.500100016594, - 0.285490781069, 0.516623795033, 0.285489022732, 0.533147871494, - 0.285486102104, 0.549672424793, 0.285482108593, 0.566197812557, - 0.285477072001, 0.582724213600, 0.285471051931, 0.599251806736, - 0.285464167595, 0.615780830383, 0.285456478596, 0.632311403751, - 0.285284191370, 0.648957133293, 0.285365968943, 0.665433883667, - 0.285530239344, 0.681828260422, 0.285581260920, 0.698301911354, - 0.285694867373, 0.714705169201, 0.285717040300, 0.731189310551, - 0.285720825195, 0.747691392899, 0.285756230354, 0.764153897762, - 0.285843342543, 0.780543327332, 0.285843342543, 0.797039985657, - 0.285843342543, 0.813536643982, 0.285843342543, 0.830033361912, - 0.285843342543, 0.846530020237, 0.285843342543, 0.863026678562, - 0.285843342543, 0.879523336887, 0.285843342543, 0.896019995213, - 0.285843342543, 0.912516653538, 0.285843342543, 0.929013311863, - 0.285843342543, 0.945510029793, 0.285843342543, 0.962006688118, - 0.285843342543, 0.978503346443, 0.285843342543, 0.995000004768, - 0.302340000868, 0.005200000014, 0.302340000868, 0.021696666256, - 0.302340000868, 0.038193333894, 0.302340000868, 0.054689999670, - 0.302340000868, 0.071186669171, 0.302340000868, 0.087683334947, - 0.302340000868, 0.104180000722, 0.302340000868, 0.120676666498, - 0.302340000868, 0.137173339725, 0.302340000868, 0.153669998050, - 0.302340000868, 0.170166671276, 0.302340000868, 0.186663329601, - 0.302340000868, 0.203160002828, 0.302267849445, 0.219554439187, - 0.302233338356, 0.236011117697, 0.302226394415, 0.252508014441, - 0.302204489708, 0.268988579512, 0.302098095417, 0.285381257534, - 0.302046805620, 0.301846802235, 0.301883697510, 0.318218380213, - 0.301799416542, 0.334682852030, 0.301986664534, 0.351365000010, - 0.301994889975, 0.367896586657, 0.302002429962, 0.384426414967, - 0.302009195089, 0.400954604149, 0.302015125751, 0.417481303215, - 0.301899492741, 0.433966487646, 0.301879584789, 0.450494885445, - 0.301865100861, 0.467027515173, 0.301856279373, 0.483563035727, - 0.301853328943, 0.500100016594, 0.301856279373, 0.516636967659, - 0.301865100861, 0.533172488213, 0.301879584789, 0.549705088139, - 0.301899492741, 0.566233515739, 0.302015125751, 0.582718729973, - 0.302009195089, 0.599245429039, 0.302002429962, 0.615773618221, - 0.301994889975, 0.632303416729, 0.301986664534, 0.648835003376, - 0.301799416542, 0.665517151356, 0.301883697510, 0.681981623173, - 0.302046805620, 0.698353230953, 0.302098095417, 0.714818716049, - 0.302204489708, 0.731211423874, 0.302226394415, 0.747691988945, - 0.302233338356, 0.764188885689, 0.302267849445, 0.780645549297, - 0.302340000868, 0.797039985657, 0.302340000868, 0.813536643982, - 0.302340000868, 0.830033361912, 0.302340000868, 0.846530020237, - 0.302340000868, 0.863026678562, 0.302340000868, 0.879523336887, - 0.302340000868, 0.896019995213, 0.302340000868, 0.912516653538, - 0.302340000868, 0.929013311863, 0.302340000868, 0.945510029793, - 0.302340000868, 0.962006688118, 0.302340000868, 0.978503346443, - 0.302340000868, 0.995000004768, 0.318836659193, 0.005200000014, - 0.318836659193, 0.021696666256, 0.318836659193, 0.038193333894, - 0.318836659193, 0.054689999670, 0.318836659193, 0.071186669171, - 0.318836659193, 0.087683334947, 0.318836659193, 0.104180000722, - 0.318836659193, 0.120676666498, 0.318836659193, 0.137173339725, - 0.318836659193, 0.153669998050, 0.318836659193, 0.170166671276, - 0.318836659193, 0.186663329601, 0.318836659193, 0.203160002828, - 0.318748593330, 0.219520568848, 0.318737924099, 0.236009716988, - 0.318716675043, 0.252486377954, 0.318622261286, 0.268873780966, - 0.318571716547, 0.285330235958, 0.318418383598, 0.301683694124, - 0.318333625793, 0.318133622408, 0.318514525890, 0.334840476513, - 0.318523049355, 0.351373404264, 0.318531006575, 0.367904365063, - 0.318538337946, 0.384433507919, 0.318425089121, 0.400895506144, - 0.318395972252, 0.417416363955, 0.318371295929, 0.433944106102, - 0.318351566792, 0.450477689505, 0.318356364965, 0.467019349337, - 0.318349331617, 0.483559042215, 0.318346977234, 0.500100016594, - 0.318349331617, 0.516640961170, 0.318356364965, 0.533180654049, - 0.318351566792, 0.549722313881, 0.318371295929, 0.566255867481, - 0.318395972252, 0.582783639431, 0.318425089121, 0.599304497242, - 0.318538337946, 0.615766525269, 0.318531006575, 0.632295608521, - 0.318523049355, 0.648826599121, 0.318514525890, 0.665359497070, - 0.318333625793, 0.682066380978, 0.318418383598, 0.698516309261, - 0.318571716547, 0.714869797230, 0.318622261286, 0.731326222420, - 0.318716675043, 0.747713625431, 0.318737924099, 0.764190256596, - 0.318748593330, 0.780679404736, 0.318836659193, 0.797039985657, - 0.318836659193, 0.813536643982, 0.318836659193, 0.830033361912, - 0.318836659193, 0.846530020237, 0.318836659193, 0.863026678562, - 0.318836659193, 0.879523336887, 0.318836659193, 0.896019995213, - 0.318836659193, 0.912516653538, 0.318836659193, 0.929013311863, - 0.318836659193, 0.945510029793, 0.318836659193, 0.962006688118, - 0.318836659193, 0.978503346443, 0.318836659193, 0.995000004768, - 0.335333347321, 0.005200000014, 0.335333347321, 0.021696666256, - 0.335333347321, 0.038193333894, 0.335333347321, 0.054689999670, - 0.335333347321, 0.071186669171, 0.335333347321, 0.087683334947, - 0.335333347321, 0.104180000722, 0.335333347321, 0.120676666498, - 0.335333347321, 0.137173339725, 0.335333347321, 0.153669998050, - 0.335333347321, 0.170166671276, 0.335333347321, 0.186663329601, - 0.335265457630, 0.203037843108, 0.335234612226, 0.219488814473, - 0.335230559111, 0.235988914967, 0.335151642561, 0.252377480268, - 0.335102856159, 0.268824011087, 0.334966123104, 0.285165965557, - 0.334882855415, 0.301599413157, 0.335040479898, 0.318314522505, - 0.335049062967, 0.334849059582, 0.335057228804, 0.351381480694, - 0.335064888000, 0.367911905050, 0.334940016270, 0.384348005056, - 0.334907740355, 0.400864630938, 0.334896683693, 0.417398363352, - 0.334877252579, 0.433930903673, 0.334861606359, 0.450468480587, - 0.334850132465, 0.467010021210, 0.334843099117, 0.483554303646, - 0.334840744734, 0.500100016594, 0.334843099117, 0.516645669937, - 0.334850132465, 0.533189952374, 0.334861606359, 0.549731492996, - 0.334877252579, 0.566269099712, 0.334896683693, 0.582801640034, - 0.334907740355, 0.599335372448, 0.334940016270, 0.615851998329, - 0.335064888000, 0.632288098335, 0.335057228804, 0.648818492889, - 0.335049062967, 0.665350973606, 0.335040479898, 0.681885480881, - 0.334882855415, 0.698600590229, 0.334966123104, 0.715034008026, - 0.335102856159, 0.731375992298, 0.335151642561, 0.747822523117, - 0.335230559111, 0.764211058617, 0.335234612226, 0.780711174011, - 0.335265457630, 0.797162175179, 0.335333347321, 0.813536643982, - 0.335333347321, 0.830033361912, 0.335333347321, 0.846530020237, - 0.335333347321, 0.863026678562, 0.335333347321, 0.879523336887, - 0.335333347321, 0.896019995213, 0.335333347321, 0.912516653538, - 0.335333347321, 0.929013311863, 0.335333347321, 0.945510029793, - 0.335333347321, 0.962006688118, 0.335333347321, 0.978503346443, - 0.335333347321, 0.995000004768, 0.351830005646, 0.005200000014, - 0.351830005646, 0.021696666256, 0.351830005646, 0.038193333894, - 0.351830005646, 0.054689999670, 0.351830005646, 0.071186669171, - 0.351830005646, 0.087683334947, 0.351830005646, 0.104180000722, - 0.351830005646, 0.120676666498, 0.351830005646, 0.137173339725, - 0.351830005646, 0.153669998050, 0.351830005646, 0.170166671276, - 0.351782768965, 0.186563611031, 0.351753979921, 0.203007981181, - 0.351745367050, 0.219496786594, 0.351726621389, 0.235969558358, - 0.351637959480, 0.252329915762, 0.351522743702, 0.268668740988, - 0.351442903280, 0.285084187984, 0.351565003395, 0.301786661148, - 0.351573407650, 0.318323045969, 0.351581484079, 0.334857225418, - 0.351589232683, 0.351389229298, 0.351467221975, 0.367804199457, - 0.351433098316, 0.384314626455, 0.351421505213, 0.400847673416, - 0.351399272680, 0.417377382517, 0.351380228996, 0.433913439512, - 0.351515233517, 0.450505077839, 0.351526618004, 0.467039257288, - 0.351533651352, 0.483570396900, 0.351536005735, 0.500100016594, - 0.351533651352, 0.516629576683, 0.351526618004, 0.533160746098, - 0.351515233517, 0.549694895744, 0.351380228996, 0.566286563873, - 0.351399272680, 0.582822620869, 0.351421505213, 0.599352300167, - 0.351433098316, 0.615885376930, 0.351467221975, 0.632395803928, - 0.351589232683, 0.648810744286, 0.351581484079, 0.665342807770, - 0.351573407650, 0.681876957417, 0.351565003395, 0.698413312435, - 0.351442903280, 0.715115845203, 0.351522743702, 0.731531262398, - 0.351637959480, 0.747870087624, 0.351726621389, 0.764230430126, - 0.351745367050, 0.780703246593, 0.351753979921, 0.797192037106, - 0.351782768965, 0.813636422157, 0.351830005646, 0.830033361912, - 0.351830005646, 0.846530020237, 0.351830005646, 0.863026678562, - 0.351830005646, 0.879523336887, 0.351830005646, 0.896019995213, - 0.351830005646, 0.912516653538, 0.351830005646, 0.929013311863, - 0.351830005646, 0.945510029793, 0.351830005646, 0.962006688118, - 0.351830005646, 0.978503346443, 0.351830005646, 0.995000004768, - 0.368326663971, 0.005200000014, 0.368326663971, 0.021696666256, - 0.368326663971, 0.038193333894, 0.368326663971, 0.054689999670, - 0.368326663971, 0.071186669171, 0.368326663971, 0.087683334947, - 0.368326663971, 0.104180000722, 0.368326663971, 0.120676666498, - 0.368326663971, 0.137173339725, 0.368326663971, 0.153669998050, - 0.368326663971, 0.170166671276, 0.368273049593, 0.186536028981, - 0.368246942759, 0.202980652452, 0.368242949247, 0.219478771091, - 0.368174701929, 0.235849425197, 0.368132591248, 0.252286106348, - 0.368009299040, 0.268591254950, 0.368088603020, 0.285256475210, - 0.368096590042, 0.301794886589, 0.368104368448, 0.318331003189, - 0.368111908436, 0.334864884615, 0.368004202843, 0.351267218590, - 0.367969691753, 0.367769688368, 0.367956489325, 0.384299427271, - 0.367932587862, 0.400824457407, 0.368048876524, 0.417443037033, - 0.368067443371, 0.433983713388, 0.368082612753, 0.450518488884, - 0.367806136608, 0.466976523399, 0.367779970169, 0.483534991741, - 0.367771118879, 0.500100016594, 0.367779970169, 0.516664981842, - 0.367806136608, 0.533223450184, 0.368082612753, 0.549681544304, - 0.368067443371, 0.566216289997, 0.368048876524, 0.582756936550, - 0.367932587862, 0.599375545979, 0.367956489325, 0.615900576115, - 0.367969691753, 0.632430315018, 0.368004202843, 0.648932754993, - 0.368111908436, 0.665335118771, 0.368104368448, 0.681868970394, - 0.368096590042, 0.698405086994, 0.368088603020, 0.714943528175, - 0.368009299040, 0.731608748436, 0.368132591248, 0.747913897038, - 0.368174701929, 0.764350593090, 0.368242949247, 0.780721247196, - 0.368246942759, 0.797219336033, 0.368273049593, 0.813663959503, - 0.368326663971, 0.830033361912, 0.368326663971, 0.846530020237, - 0.368326663971, 0.863026678562, 0.368326663971, 0.879523336887, - 0.368326663971, 0.896019995213, 0.368326663971, 0.912516653538, - 0.368326663971, 0.929013311863, 0.368326663971, 0.945510029793, - 0.368326663971, 0.962006688118, 0.368326663971, 0.978503346443, - 0.368326663971, 0.995000004768, 0.384823322296, 0.005200000014, - 0.384823322296, 0.021696666256, 0.384823322296, 0.038193333894, - 0.384823322296, 0.054689999670, 0.384823322296, 0.071186669171, - 0.384823322296, 0.087683334947, 0.384823322296, 0.104180000722, - 0.384823322296, 0.120676666498, 0.384823322296, 0.137173339725, - 0.384823322296, 0.153669998050, 0.384823322296, 0.170166671276, - 0.384767293930, 0.186511233449, 0.384758800268, 0.202994033694, - 0.384743392467, 0.219462499022, 0.384672790766, 0.235809206963, - 0.384577631950, 0.252123475075, 0.384510397911, 0.268520772457, - 0.384619176388, 0.285264164209, 0.384626418352, 0.301802426577, - 0.384633481503, 0.318338364363, 0.384548008442, 0.334740012884, - 0.384514629841, 0.351233094931, 0.384499430656, 0.367756485939, - 0.384475171566, 0.384275197983, 0.384587377310, 0.400917768478, - 0.384607851505, 0.417462766171, 0.384344965219, 0.433839976788, - 0.384289413691, 0.450381159782, 0.384247630835, 0.466942191124, - 0.384221613407, 0.483517378569, 0.384224712849, 0.500100016594, - 0.384221613407, 0.516682624817, 0.384247630835, 0.533257842064, - 0.384289413691, 0.549818813801, 0.384344965219, 0.566359996796, - 0.384607851505, 0.582737267017, 0.384587377310, 0.599282264709, - 0.384475171566, 0.615924835205, 0.384499430656, 0.632443487644, - 0.384514629841, 0.648966908455, 0.384548008442, 0.665459990501, - 0.384633481503, 0.681861639023, 0.384626418352, 0.698397576809, - 0.384619176388, 0.714935839176, 0.384510397911, 0.731679201126, - 0.384577631950, 0.748076558113, 0.384672790766, 0.764390826225, - 0.384743392467, 0.780737519264, 0.384758800268, 0.797205984592, - 0.384767293930, 0.813688755035, 0.384823322296, 0.830033361912, - 0.384823322296, 0.846530020237, 0.384823322296, 0.863026678562, - 0.384823322296, 0.879523336887, 0.384823322296, 0.896019995213, - 0.384823322296, 0.912516653538, 0.384823322296, 0.929013311863, - 0.384823322296, 0.945510029793, 0.384823322296, 0.962006688118, - 0.384823322296, 0.978503346443, 0.384823322296, 0.995000004768, - 0.401320010424, 0.005200000014, 0.401320010424, 0.021696666256, - 0.401320010424, 0.038193333894, 0.401320010424, 0.054689999670, - 0.401320010424, 0.071186669171, 0.401320010424, 0.087683334947, - 0.401320010424, 0.104180000722, 0.401320010424, 0.120676666498, - 0.401320010424, 0.137173339725, 0.401320010424, 0.153669998050, - 0.401285439730, 0.170051515102, 0.401265054941, 0.186489373446, - 0.401259839535, 0.202979549766, 0.401210993528, 0.219347789884, - 0.401177585125, 0.235773533583, 0.401083946228, 0.252059876919, - 0.401141673326, 0.268730610609, 0.401148170233, 0.285271078348, - 0.401154607534, 0.301809191704, 0.401095509529, 0.318225115538, - 0.401064634323, 0.334707736969, 0.401047676802, 0.351221501827, - 0.401024430990, 0.367732584476, 0.401117742062, 0.384387373924, - 0.401138633490, 0.400938630104, 0.400883078575, 0.417252570391, - 0.400819182396, 0.433779448271, 0.400777846575, 0.450338929892, - 0.400739639997, 0.466913223267, 0.400715559721, 0.483502596617, - 0.400707334280, 0.500100016594, 0.400715559721, 0.516697406769, - 0.400739639997, 0.533286809921, 0.400777846575, 0.549861073494, - 0.400819182396, 0.566420555115, 0.400883078575, 0.582947432995, - 0.401138633490, 0.599261343479, 0.401117742062, 0.615812599659, - 0.401024430990, 0.632467389107, 0.401047676802, 0.648978471756, - 0.401064634323, 0.665492236614, 0.401095509529, 0.681974887848, - 0.401154607534, 0.698390781879, 0.401148170233, 0.714928925037, - 0.401141673326, 0.731469392776, 0.401083946228, 0.748140096664, - 0.401177585125, 0.764426469803, 0.401210993528, 0.780852198601, - 0.401259839535, 0.797220468521, 0.401265054941, 0.813710629940, - 0.401285439730, 0.830148518085, 0.401320010424, 0.846530020237, - 0.401320010424, 0.863026678562, 0.401320010424, 0.879523336887, - 0.401320010424, 0.896019995213, 0.401320010424, 0.912516653538, - 0.401320010424, 0.929013311863, 0.401320010424, 0.945510029793, - 0.401320010424, 0.962006688118, 0.401320010424, 0.978503346443, - 0.401320010424, 0.995000004768, 0.417816668749, 0.005200000014, - 0.417816668749, 0.021696666256, 0.417816668749, 0.038193333894, - 0.417816668749, 0.054689999670, 0.417816668749, 0.071186669171, - 0.417816668749, 0.087683334947, 0.417816668749, 0.104180000722, - 0.417816668749, 0.120676666498, 0.417816668749, 0.137173339725, - 0.417816668749, 0.153669998050, 0.417783170938, 0.170032665133, - 0.417774528265, 0.186503171921, 0.417763084173, 0.202967077494, - 0.417716711760, 0.219316795468, 0.417652368546, 0.235627561808, - 0.417601615191, 0.252004832029, 0.417670249939, 0.268736660480, - 0.417675793171, 0.285277068615, 0.417681306601, 0.301815122366, - 0.417616337538, 0.318195968866, 0.417598336935, 0.334696710110, - 0.417577385902, 0.351199269295, 0.417643040419, 0.367848873138, - 0.417662769556, 0.384407877922, 0.417452573776, 0.400683104992, - 0.417386859655, 0.417186886072, 0.417339563370, 0.433731645346, - 0.417292088270, 0.450295239687, 0.417440444231, 0.466956168413, - 0.417448908091, 0.483529776335, 0.417451858521, 0.500100016594, - 0.417448908091, 0.516670227051, 0.417440444231, 0.533243834972, - 0.417292088270, 0.549904763699, 0.417339563370, 0.566468358040, - 0.417386859655, 0.583013117313, 0.417452573776, 0.599516928196, - 0.417662769556, 0.615792155266, 0.417643040419, 0.632351100445, - 0.417577385902, 0.649000704288, 0.417598336935, 0.665503323078, - 0.417616337538, 0.682004034519, 0.417681306601, 0.698384881020, - 0.417675793171, 0.714922904968, 0.417670249939, 0.731463313103, - 0.417601615191, 0.748195171356, 0.417652368546, 0.764572441578, - 0.417716711760, 0.780883193016, 0.417763084173, 0.797232925892, - 0.417774528265, 0.813696801662, 0.417783170938, 0.830167353153, - 0.417816668749, 0.846530020237, 0.417816668749, 0.863026678562, - 0.417816668749, 0.879523336887, 0.417816668749, 0.896019995213, - 0.417816668749, 0.912516653538, 0.417816668749, 0.929013311863, - 0.417816668749, 0.945510029793, 0.417816668749, 0.962006688118, - 0.417816668749, 0.978503346443, 0.417816668749, 0.995000004768, - 0.434313327074, 0.005200000014, 0.434313327074, 0.021696666256, - 0.434313327074, 0.038193333894, 0.434313327074, 0.054689999670, - 0.434313327074, 0.071186669171, 0.434313327074, 0.087683334947, - 0.434313327074, 0.104180000722, 0.434313327074, 0.120676666498, - 0.434313327074, 0.137173339725, 0.434313327074, 0.153669998050, - 0.434283405542, 0.170017048717, 0.434277445078, 0.186492800713, - 0.434268176556, 0.202956736088, 0.434227287769, 0.219291031361, - 0.434170335531, 0.235581368208, 0.434129029512, 0.251958876848, - 0.434197634459, 0.268741726875, 0.434202194214, 0.285282105207, - 0.434166491032, 0.301699489355, 0.434144109488, 0.318171292543, - 0.434130907059, 0.334677249193, 0.434113442898, 0.351180225611, - 0.434183716774, 0.367867439985, 0.434039980173, 0.384144961834, - 0.433979451656, 0.400619179010, 0.433931648731, 0.417139559984, - 0.433882117271, 0.433682113886, 0.434021472931, 0.450391113758, - 0.434033989906, 0.466966986656, 0.433963954449, 0.483515977859, - 0.433960437775, 0.500100016594, 0.433963954449, 0.516683995724, - 0.434033989906, 0.533232986927, 0.434021472931, 0.549808859825, - 0.433882117271, 0.566517889500, 0.433931648731, 0.583060443401, - 0.433979451656, 0.599580824375, 0.434039980173, 0.616055011749, - 0.434183716774, 0.632332563400, 0.434113442898, 0.649019777775, - 0.434130907059, 0.665522754192, 0.434144109488, 0.682028710842, - 0.434166491032, 0.698500514030, 0.434202194214, 0.714917898178, - 0.434197634459, 0.731458246708, 0.434129029512, 0.748241126537, - 0.434170335531, 0.764618635178, 0.434227287769, 0.780908942223, - 0.434268176556, 0.797243237495, 0.434277445078, 0.813707232475, - 0.434283405542, 0.830182969570, 0.434313327074, 0.846530020237, - 0.434313327074, 0.863026678562, 0.434313327074, 0.879523336887, - 0.434313327074, 0.896019995213, 0.434313327074, 0.912516653538, - 0.434313327074, 0.929013311863, 0.434313327074, 0.945510029793, - 0.434313327074, 0.962006688118, 0.434313327074, 0.978503346443, - 0.434313327074, 0.995000004768, 0.450809985399, 0.005200000014, - 0.450809985399, 0.021696666256, 0.450809985399, 0.038193333894, - 0.450809985399, 0.054689999670, 0.450809985399, 0.071186669171, - 0.450809985399, 0.087683334947, 0.450809985399, 0.104180000722, - 0.450809985399, 0.120676666498, 0.450809985399, 0.137173339725, - 0.450809985399, 0.153669998050, 0.450785726309, 0.170004799962, - 0.450781792402, 0.186484634876, 0.450759410858, 0.202856481075, - 0.450741887093, 0.219270721078, 0.450695931911, 0.235544919968, - 0.450720608234, 0.252203047276, 0.450724095106, 0.268745750189, - 0.450727552176, 0.285286098719, 0.450694888830, 0.301679581404, - 0.450677692890, 0.318151563406, 0.450668483973, 0.334661602974, - 0.450705081224, 0.351315230131, 0.450718492270, 0.367882639170, - 0.450581163168, 0.384089410305, 0.450538933277, 0.400577843189, - 0.450495243073, 0.417092084885, 0.450591117144, 0.433821469545, - 0.450604587793, 0.450404584408, 0.450536906719, 0.466924607754, - 0.450527459383, 0.483509153128, 0.450524002314, 0.500100016594, - 0.450527459383, 0.516690850258, 0.450536906719, 0.533275365829, - 0.450604587793, 0.549795448780, 0.450591117144, 0.566378533840, - 0.450495243073, 0.583107888699, 0.450538933277, 0.599622189999, - 0.450581163168, 0.616110563278, 0.450718492270, 0.632317364216, - 0.450705081224, 0.648884773254, 0.450668483973, 0.665538370609, - 0.450677692890, 0.682048439980, 0.450694888830, 0.698520421982, - 0.450727552176, 0.714913904667, 0.450724095106, 0.731454253197, - 0.450720608234, 0.747996926308, 0.450695931911, 0.764655053616, - 0.450741887093, 0.780929267406, 0.450759410858, 0.797343492508, - 0.450781792402, 0.813715338707, 0.450785726309, 0.830195188522, - 0.450809985399, 0.846530020237, 0.450809985399, 0.863026678562, - 0.450809985399, 0.879523336887, 0.450809985399, 0.896019995213, - 0.450809985399, 0.912516653538, 0.450809985399, 0.929013311863, - 0.450809985399, 0.945510029793, 0.450809985399, 0.962006688118, - 0.450809985399, 0.978503346443, 0.450809985399, 0.995000004768, - 0.467306673527, 0.005200000014, 0.467306673527, 0.021696666256, - 0.467306673527, 0.038193333894, 0.467306673527, 0.054689999670, - 0.467306673527, 0.071186669171, 0.467306673527, 0.087683334947, - 0.467306673527, 0.104180000722, 0.467306673527, 0.120676666498, - 0.467306673527, 0.137173339725, 0.467306673527, 0.153669998050, - 0.467289596796, 0.169995978475, 0.467287242413, 0.186478763819, - 0.467271298170, 0.202841818333, 0.467259526253, 0.219256073236, - 0.467227309942, 0.235518589616, 0.467247456312, 0.252205967903, - 0.467249810696, 0.268748670816, 0.467252165079, 0.285289019346, - 0.467227518559, 0.301665097475, 0.467219352722, 0.318156361580, - 0.467210024595, 0.334650129080, 0.467239260674, 0.351326644421, - 0.467176526785, 0.367606133223, 0.467142194510, 0.384047627449, - 0.467113226652, 0.400539636612, 0.467156171799, 0.417240440845, - 0.467166990042, 0.433833986521, 0.467124611139, 0.450336933136, - 0.467113554478, 0.466913551092, 0.466898351908, 0.483399182558, - 0.466870367527, 0.500100016594, 0.466898351908, 0.516800820827, - 0.467113554478, 0.533286452293, 0.467124611139, 0.549863100052, - 0.467166990042, 0.566366016865, 0.467156171799, 0.582959532738, - 0.467113226652, 0.599660336971, 0.467142194510, 0.616152346134, - 0.467176526785, 0.632593870163, 0.467239260674, 0.648873388767, - 0.467210024595, 0.665549874306, 0.467219352722, 0.682043612003, - 0.467227518559, 0.698534905910, 0.467252165079, 0.714910984039, - 0.467249810696, 0.731451332569, 0.467247456312, 0.747994005680, - 0.467227309942, 0.764681398869, 0.467259526253, 0.780943930149, - 0.467271298170, 0.797358155251, 0.467287242413, 0.813721239567, - 0.467289596796, 0.830204010010, 0.467306673527, 0.846530020237, - 0.467306673527, 0.863026678562, 0.467306673527, 0.879523336887, - 0.467306673527, 0.896019995213, 0.467306673527, 0.912516653538, - 0.467306673527, 0.929013311863, 0.467306673527, 0.945510029793, - 0.467306673527, 0.962006688118, 0.467306673527, 0.978503346443, - 0.467306673527, 0.995000004768, 0.483803331852, 0.005200000014, - 0.483803331852, 0.021696666256, 0.483803331852, 0.038193333894, - 0.483803331852, 0.054689999670, 0.483803331852, 0.071186669171, - 0.483803331852, 0.087683334947, 0.483803331852, 0.104180000722, - 0.483803331852, 0.120676666498, 0.483803331852, 0.137173339725, - 0.483798086643, 0.153559774160, 0.483794540167, 0.169990658760, - 0.483793437481, 0.186475217342, 0.483785152435, 0.202832967043, - 0.483779251575, 0.219247221947, 0.483762651682, 0.235502660275, - 0.483773857355, 0.252207756042, 0.483775019646, 0.268750458956, - 0.483776211739, 0.285290777683, 0.483763039112, 0.301656305790, - 0.483759015799, 0.318149328232, 0.483754307032, 0.334643095732, - 0.483770400286, 0.351333647966, 0.483734995127, 0.367579966784, - 0.483717381954, 0.384021610022, 0.483702600002, 0.400515586138, - 0.483729779720, 0.417248904705, 0.483715981245, 0.433763951063, - 0.483709156513, 0.450327455997, 0.483599185944, 0.466698348522, - 0.483550459146, 0.483350485563, 0.483633339405, 0.500100016594, - 0.483550459146, 0.516849517822, 0.483599185944, 0.533501625061, - 0.483709156513, 0.549872517586, 0.483715981245, 0.566436052322, - 0.483729779720, 0.582951068878, 0.483702600002, 0.599684417248, - 0.483717381954, 0.616178393364, 0.483734995127, 0.632620036602, - 0.483770400286, 0.648866355419, 0.483754307032, 0.665556907654, - 0.483759015799, 0.682050645351, 0.483763039112, 0.698543727398, - 0.483776211739, 0.714909195900, 0.483775019646, 0.731449544430, - 0.483773857355, 0.747992277145, 0.483762651682, 0.764697313309, - 0.483779251575, 0.780952751637, 0.483785152435, 0.797367036343, - 0.483793437481, 0.813724756241, 0.483794540167, 0.830209314823, - 0.483798086643, 0.846640229225, 0.483803331852, 0.863026678562, - 0.483803331852, 0.879523336887, 0.483803331852, 0.896019995213, - 0.483803331852, 0.912516653538, 0.483803331852, 0.929013311863, - 0.483803331852, 0.945510029793, 0.483803331852, 0.962006688118, - 0.483803331852, 0.978503346443, 0.483803331852, 0.995000004768, - 0.500299990177, 0.005200000014, 0.500299990177, 0.021696666256, - 0.500299990177, 0.038193333894, 0.500299990177, 0.054689999670, - 0.500299990177, 0.071186669171, 0.500299990177, 0.087683334947, - 0.500299990177, 0.104180000722, 0.500299990177, 0.120676666498, - 0.500299990177, 0.137173339725, 0.500299990177, 0.153558000922, - 0.500299990177, 0.169988885522, 0.500299990177, 0.186474040151, - 0.500299990177, 0.202830001712, 0.500299990177, 0.219244256616, - 0.500299990177, 0.235497340560, 0.500299990177, 0.252208322287, - 0.500299990177, 0.268751025200, 0.500299990177, 0.285291373730, - 0.500299990177, 0.301653325558, 0.500299990177, 0.318146973848, - 0.500299990177, 0.334640741348, 0.500299990177, 0.351336002350, - 0.500299990177, 0.367571115494, 0.500299990177, 0.384024709463, - 0.500299990177, 0.400507330894, 0.500299990177, 0.417251855135, - 0.500299990177, 0.433760434389, 0.500299990177, 0.450323998928, - 0.500299990177, 0.466670364141, 0.500299990177, 0.483433336020, - 0.500299990177, 0.500100016594, 0.500299990177, 0.516766667366, - 0.500299990177, 0.533529639244, 0.500299990177, 0.549875974655, - 0.500299990177, 0.566439568996, 0.500299990177, 0.582948148251, - 0.500299990177, 0.599692642689, 0.500299990177, 0.616175293922, - 0.500299990177, 0.632628917694, 0.500299990177, 0.648863971233, - 0.500299990177, 0.665559232235, 0.500299990177, 0.682053029537, - 0.500299990177, 0.698546648026, 0.500299990177, 0.714908599854, - 0.500299990177, 0.731448948383, 0.500299990177, 0.747991681099, - 0.500299990177, 0.764702677727, 0.500299990177, 0.780955731869, - 0.500299990177, 0.797370016575, 0.500299990177, 0.813725948334, - 0.500299990177, 0.830211102962, 0.500299990177, 0.846642017365, - 0.500299990177, 0.863026678562, 0.500299990177, 0.879523336887, - 0.500299990177, 0.896019995213, 0.500299990177, 0.912516653538, - 0.500299990177, 0.929013311863, 0.500299990177, 0.945510029793, - 0.500299990177, 0.962006688118, 0.500299990177, 0.978503346443, - 0.500299990177, 0.995000004768, 0.516796648502, 0.005200000014, - 0.516796648502, 0.021696666256, 0.516796648502, 0.038193333894, - 0.516796648502, 0.054689999670, 0.516796648502, 0.071186669171, - 0.516796648502, 0.087683334947, 0.516796648502, 0.104180000722, - 0.516796648502, 0.120676666498, 0.516796648502, 0.137173339725, - 0.516801893711, 0.153559774160, 0.516805469990, 0.169990658760, - 0.516806542873, 0.186475217342, 0.516814827919, 0.202832967043, - 0.516820728779, 0.219247221947, 0.516837358475, 0.235502660275, - 0.516826152802, 0.252207756042, 0.516824960709, 0.268750458956, - 0.516823768616, 0.285290777683, 0.516837000847, 0.301656305790, - 0.516840994358, 0.318149328232, 0.516845703125, 0.334643095732, - 0.516829609871, 0.351333647966, 0.516865015030, 0.367579966784, - 0.516882598400, 0.384021610022, 0.516897380352, 0.400515586138, - 0.516870200634, 0.417248904705, 0.516884028912, 0.433763951063, - 0.516890823841, 0.450327455997, 0.517000854015, 0.466698348522, - 0.517049551010, 0.483350485563, 0.516966640949, 0.500100016594, - 0.517049551010, 0.516849517822, 0.517000854015, 0.533501625061, - 0.516890823841, 0.549872517586, 0.516884028912, 0.566436052322, - 0.516870200634, 0.582951068878, 0.516897380352, 0.599684417248, - 0.516882598400, 0.616178393364, 0.516865015030, 0.632620036602, - 0.516829609871, 0.648866355419, 0.516845703125, 0.665556907654, - 0.516840994358, 0.682050645351, 0.516837000847, 0.698543727398, - 0.516823768616, 0.714909195900, 0.516824960709, 0.731449544430, - 0.516826152802, 0.747992277145, 0.516837358475, 0.764697313309, - 0.516820728779, 0.780952751637, 0.516814827919, 0.797367036343, - 0.516806542873, 0.813724756241, 0.516805469990, 0.830209314823, - 0.516801893711, 0.846640229225, 0.516796648502, 0.863026678562, - 0.516796648502, 0.879523336887, 0.516796648502, 0.896019995213, - 0.516796648502, 0.912516653538, 0.516796648502, 0.929013311863, - 0.516796648502, 0.945510029793, 0.516796648502, 0.962006688118, - 0.516796648502, 0.978503346443, 0.516796648502, 0.995000004768, - 0.533293306828, 0.005200000014, 0.533293306828, 0.021696666256, - 0.533293306828, 0.038193333894, 0.533293306828, 0.054689999670, - 0.533293306828, 0.071186669171, 0.533293306828, 0.087683334947, - 0.533293306828, 0.104180000722, 0.533293306828, 0.120676666498, - 0.533293306828, 0.137173339725, 0.533293306828, 0.153669998050, - 0.533310413361, 0.169995978475, 0.533312737942, 0.186478763819, - 0.533328711987, 0.202841818333, 0.533340454102, 0.219256073236, - 0.533372700214, 0.235518589616, 0.533352553844, 0.252205967903, - 0.533350169659, 0.268748670816, 0.533347845078, 0.285289019346, - 0.533372461796, 0.301665097475, 0.533380687237, 0.318156361580, - 0.533389985561, 0.334650129080, 0.533360719681, 0.351326644421, - 0.533423483372, 0.367606133223, 0.533457815647, 0.384047627449, - 0.533486783504, 0.400539636612, 0.533443808556, 0.417240440845, - 0.533433020115, 0.433833986521, 0.533475399017, 0.450336933136, - 0.533486425877, 0.466913551092, 0.533701658249, 0.483399182558, - 0.533729612827, 0.500100016594, 0.533701658249, 0.516800820827, - 0.533486425877, 0.533286452293, 0.533475399017, 0.549863100052, - 0.533433020115, 0.566366016865, 0.533443808556, 0.582959532738, - 0.533486783504, 0.599660336971, 0.533457815647, 0.616152346134, - 0.533423483372, 0.632593870163, 0.533360719681, 0.648873388767, - 0.533389985561, 0.665549874306, 0.533380687237, 0.682043612003, - 0.533372461796, 0.698534905910, 0.533347845078, 0.714910984039, - 0.533350169659, 0.731451332569, 0.533352553844, 0.747994005680, - 0.533372700214, 0.764681398869, 0.533340454102, 0.780943930149, - 0.533328711987, 0.797358155251, 0.533312737942, 0.813721239567, - 0.533310413361, 0.830204010010, 0.533293306828, 0.846530020237, - 0.533293306828, 0.863026678562, 0.533293306828, 0.879523336887, - 0.533293306828, 0.896019995213, 0.533293306828, 0.912516653538, - 0.533293306828, 0.929013311863, 0.533293306828, 0.945510029793, - 0.533293306828, 0.962006688118, 0.533293306828, 0.978503346443, - 0.533293306828, 0.995000004768, 0.549790024757, 0.005200000014, - 0.549790024757, 0.021696666256, 0.549790024757, 0.038193333894, - 0.549790024757, 0.054689999670, 0.549790024757, 0.071186669171, - 0.549790024757, 0.087683334947, 0.549790024757, 0.104180000722, - 0.549790024757, 0.120676666498, 0.549790024757, 0.137173339725, - 0.549790024757, 0.153669998050, 0.549814283848, 0.170004799962, - 0.549818217754, 0.186484634876, 0.549840569496, 0.202856481075, - 0.549858093262, 0.219270721078, 0.549904048443, 0.235544919968, - 0.549879372120, 0.252203047276, 0.549875915051, 0.268745750189, - 0.549872457981, 0.285286098719, 0.549905121326, 0.301679581404, - 0.549922287464, 0.318151563406, 0.549931526184, 0.334661602974, - 0.549894928932, 0.351315230131, 0.549881517887, 0.367882639170, - 0.550018846989, 0.384089410305, 0.550061106682, 0.400577843189, - 0.550104737282, 0.417092084885, 0.550008893013, 0.433821469545, - 0.549995422363, 0.450404584408, 0.550063073635, 0.466924607754, - 0.550072550774, 0.483509153128, 0.550076007843, 0.500100016594, - 0.550072550774, 0.516690850258, 0.550063073635, 0.533275365829, - 0.549995422363, 0.549795448780, 0.550008893013, 0.566378533840, - 0.550104737282, 0.583107888699, 0.550061106682, 0.599622189999, - 0.550018846989, 0.616110563278, 0.549881517887, 0.632317364216, - 0.549894928932, 0.648884773254, 0.549931526184, 0.665538370609, - 0.549922287464, 0.682048439980, 0.549905121326, 0.698520421982, - 0.549872457981, 0.714913904667, 0.549875915051, 0.731454253197, - 0.549879372120, 0.747996926308, 0.549904048443, 0.764655053616, - 0.549858093262, 0.780929267406, 0.549840569496, 0.797343492508, - 0.549818217754, 0.813715338707, 0.549814283848, 0.830195188522, - 0.549790024757, 0.846530020237, 0.549790024757, 0.863026678562, - 0.549790024757, 0.879523336887, 0.549790024757, 0.896019995213, - 0.549790024757, 0.912516653538, 0.549790024757, 0.929013311863, - 0.549790024757, 0.945510029793, 0.549790024757, 0.962006688118, - 0.549790024757, 0.978503346443, 0.549790024757, 0.995000004768, - 0.566286683083, 0.005200000014, 0.566286683083, 0.021696666256, - 0.566286683083, 0.038193333894, 0.566286683083, 0.054689999670, - 0.566286683083, 0.071186669171, 0.566286683083, 0.087683334947, - 0.566286683083, 0.104180000722, 0.566286683083, 0.120676666498, - 0.566286683083, 0.137173339725, 0.566286683083, 0.153669998050, - 0.566316604614, 0.170017048717, 0.566322565079, 0.186492800713, - 0.566331863403, 0.202956736088, 0.566372692585, 0.219291031361, - 0.566429674625, 0.235581368208, 0.566470980644, 0.251958876848, - 0.566402375698, 0.268741726875, 0.566397786140, 0.285282105207, - 0.566433489323, 0.301699489355, 0.566455900669, 0.318171292543, - 0.566469073296, 0.334677249193, 0.566486597061, 0.351180225611, - 0.566416263580, 0.367867439985, 0.566560029984, 0.384144961834, - 0.566620528698, 0.400619179010, 0.566668331623, 0.417139559984, - 0.566717863083, 0.433682113886, 0.566578507423, 0.450391113758, - 0.566565990448, 0.466966986656, 0.566636025906, 0.483515977859, - 0.566639542580, 0.500100016594, 0.566636025906, 0.516683995724, - 0.566565990448, 0.533232986927, 0.566578507423, 0.549808859825, - 0.566717863083, 0.566517889500, 0.566668331623, 0.583060443401, - 0.566620528698, 0.599580824375, 0.566560029984, 0.616055011749, - 0.566416263580, 0.632332563400, 0.566486597061, 0.649019777775, - 0.566469073296, 0.665522754192, 0.566455900669, 0.682028710842, - 0.566433489323, 0.698500514030, 0.566397786140, 0.714917898178, - 0.566402375698, 0.731458246708, 0.566470980644, 0.748241126537, - 0.566429674625, 0.764618635178, 0.566372692585, 0.780908942223, - 0.566331863403, 0.797243237495, 0.566322565079, 0.813707232475, - 0.566316604614, 0.830182969570, 0.566286683083, 0.846530020237, - 0.566286683083, 0.863026678562, 0.566286683083, 0.879523336887, - 0.566286683083, 0.896019995213, 0.566286683083, 0.912516653538, - 0.566286683083, 0.929013311863, 0.566286683083, 0.945510029793, - 0.566286683083, 0.962006688118, 0.566286683083, 0.978503346443, - 0.566286683083, 0.995000004768, 0.582783341408, 0.005200000014, - 0.582783341408, 0.021696666256, 0.582783341408, 0.038193333894, - 0.582783341408, 0.054689999670, 0.582783341408, 0.071186669171, - 0.582783341408, 0.087683334947, 0.582783341408, 0.104180000722, - 0.582783341408, 0.120676666498, 0.582783341408, 0.137173339725, - 0.582783341408, 0.153669998050, 0.582816839218, 0.170032665133, - 0.582825481892, 0.186503171921, 0.582836925983, 0.202967077494, - 0.582883298397, 0.219316795468, 0.582947611809, 0.235627561808, - 0.582998394966, 0.252004832029, 0.582929790020, 0.268736660480, - 0.582924187183, 0.285277068615, 0.582918703556, 0.301815122366, - 0.582983672619, 0.318195968866, 0.583001673222, 0.334696710110, - 0.583022594452, 0.351199269295, 0.582956969738, 0.367848873138, - 0.582937240601, 0.384407877922, 0.583147406578, 0.400683104992, - 0.583213150501, 0.417186886072, 0.583260416985, 0.433731645346, - 0.583307921886, 0.450295239687, 0.583159565926, 0.466956168413, - 0.583151102066, 0.483529776335, 0.583148121834, 0.500100016594, - 0.583151102066, 0.516670227051, 0.583159565926, 0.533243834972, - 0.583307921886, 0.549904763699, 0.583260416985, 0.566468358040, - 0.583213150501, 0.583013117313, 0.583147406578, 0.599516928196, - 0.582937240601, 0.615792155266, 0.582956969738, 0.632351100445, - 0.583022594452, 0.649000704288, 0.583001673222, 0.665503323078, - 0.582983672619, 0.682004034519, 0.582918703556, 0.698384881020, - 0.582924187183, 0.714922904968, 0.582929790020, 0.731463313103, - 0.582998394966, 0.748195171356, 0.582947611809, 0.764572441578, - 0.582883298397, 0.780883193016, 0.582836925983, 0.797232925892, - 0.582825481892, 0.813696801662, 0.582816839218, 0.830167353153, - 0.582783341408, 0.846530020237, 0.582783341408, 0.863026678562, - 0.582783341408, 0.879523336887, 0.582783341408, 0.896019995213, - 0.582783341408, 0.912516653538, 0.582783341408, 0.929013311863, - 0.582783341408, 0.945510029793, 0.582783341408, 0.962006688118, - 0.582783341408, 0.978503346443, 0.582783341408, 0.995000004768, - 0.599279999733, 0.005200000014, 0.599279999733, 0.021696666256, - 0.599279999733, 0.038193333894, 0.599279999733, 0.054689999670, - 0.599279999733, 0.071186669171, 0.599279999733, 0.087683334947, - 0.599279999733, 0.104180000722, 0.599279999733, 0.120676666498, - 0.599279999733, 0.137173339725, 0.599279999733, 0.153669998050, - 0.599314570427, 0.170051515102, 0.599334955215, 0.186489373446, - 0.599340140820, 0.202979549766, 0.599389016628, 0.219347789884, - 0.599422454834, 0.235773533583, 0.599516034126, 0.252059876919, - 0.599458336830, 0.268730610609, 0.599451839924, 0.285271078348, - 0.599445402622, 0.301809191704, 0.599504470825, 0.318225115538, - 0.599535346031, 0.334707736969, 0.599552333355, 0.351221501827, - 0.599575579166, 0.367732584476, 0.599482238293, 0.384387373924, - 0.599461376667, 0.400938630104, 0.599716901779, 0.417252570391, - 0.599780797958, 0.433779448271, 0.599822163582, 0.450338929892, - 0.599860370159, 0.466913223267, 0.599884450436, 0.483502596617, - 0.599892675877, 0.500100016594, 0.599884450436, 0.516697406769, - 0.599860370159, 0.533286809921, 0.599822163582, 0.549861073494, - 0.599780797958, 0.566420555115, 0.599716901779, 0.582947432995, - 0.599461376667, 0.599261343479, 0.599482238293, 0.615812599659, - 0.599575579166, 0.632467389107, 0.599552333355, 0.648978471756, - 0.599535346031, 0.665492236614, 0.599504470825, 0.681974887848, - 0.599445402622, 0.698390781879, 0.599451839924, 0.714928925037, - 0.599458336830, 0.731469392776, 0.599516034126, 0.748140096664, - 0.599422454834, 0.764426469803, 0.599389016628, 0.780852198601, - 0.599340140820, 0.797220468521, 0.599334955215, 0.813710629940, - 0.599314570427, 0.830148518085, 0.599279999733, 0.846530020237, - 0.599279999733, 0.863026678562, 0.599279999733, 0.879523336887, - 0.599279999733, 0.896019995213, 0.599279999733, 0.912516653538, - 0.599279999733, 0.929013311863, 0.599279999733, 0.945510029793, - 0.599279999733, 0.962006688118, 0.599279999733, 0.978503346443, - 0.599279999733, 0.995000004768, 0.615776658058, 0.005200000014, - 0.615776658058, 0.021696666256, 0.615776658058, 0.038193333894, - 0.615776658058, 0.054689999670, 0.615776658058, 0.071186669171, - 0.615776658058, 0.087683334947, 0.615776658058, 0.104180000722, - 0.615776658058, 0.120676666498, 0.615776658058, 0.137173339725, - 0.615776658058, 0.153669998050, 0.615776658058, 0.170166671276, - 0.615832686424, 0.186511233449, 0.615841209888, 0.202994033694, - 0.615856587887, 0.219462499022, 0.615927219391, 0.235809206963, - 0.616022408009, 0.252123475075, 0.616089642048, 0.268520772457, - 0.615980803967, 0.285264164209, 0.615973591805, 0.301802426577, - 0.615966498852, 0.318338364363, 0.616051971912, 0.334740012884, - 0.616085350513, 0.351233094931, 0.616100549698, 0.367756485939, - 0.616124808788, 0.384275197983, 0.616012632847, 0.400917768478, - 0.615992128849, 0.417462766171, 0.616255044937, 0.433839976788, - 0.616310596466, 0.450381159782, 0.616352379322, 0.466942191124, - 0.616378366947, 0.483517378569, 0.616375267506, 0.500100016594, - 0.616378366947, 0.516682624817, 0.616352379322, 0.533257842064, - 0.616310596466, 0.549818813801, 0.616255044937, 0.566359996796, - 0.615992128849, 0.582737267017, 0.616012632847, 0.599282264709, - 0.616124808788, 0.615924835205, 0.616100549698, 0.632443487644, - 0.616085350513, 0.648966908455, 0.616051971912, 0.665459990501, - 0.615966498852, 0.681861639023, 0.615973591805, 0.698397576809, - 0.615980803967, 0.714935839176, 0.616089642048, 0.731679201126, - 0.616022408009, 0.748076558113, 0.615927219391, 0.764390826225, - 0.615856587887, 0.780737519264, 0.615841209888, 0.797205984592, - 0.615832686424, 0.813688755035, 0.615776658058, 0.830033361912, - 0.615776658058, 0.846530020237, 0.615776658058, 0.863026678562, - 0.615776658058, 0.879523336887, 0.615776658058, 0.896019995213, - 0.615776658058, 0.912516653538, 0.615776658058, 0.929013311863, - 0.615776658058, 0.945510029793, 0.615776658058, 0.962006688118, - 0.615776658058, 0.978503346443, 0.615776658058, 0.995000004768, - 0.632273316383, 0.005200000014, 0.632273316383, 0.021696666256, - 0.632273316383, 0.038193333894, 0.632273316383, 0.054689999670, - 0.632273316383, 0.071186669171, 0.632273316383, 0.087683334947, - 0.632273316383, 0.104180000722, 0.632273316383, 0.120676666498, - 0.632273316383, 0.137173339725, 0.632273316383, 0.153669998050, - 0.632273316383, 0.170166671276, 0.632326960564, 0.186536028981, - 0.632353067398, 0.202980652452, 0.632357060909, 0.219478771091, - 0.632425308228, 0.235849425197, 0.632467389107, 0.252286106348, - 0.632590711117, 0.268591254950, 0.632511377335, 0.285256475210, - 0.632503390312, 0.301794886589, 0.632495641708, 0.318331003189, - 0.632488071918, 0.334864884615, 0.632595777512, 0.351267218590, - 0.632630288601, 0.367769688368, 0.632643520832, 0.384299427271, - 0.632667422295, 0.400824457407, 0.632551133633, 0.417443037033, - 0.632532536983, 0.433983713388, 0.632517397404, 0.450518488884, - 0.632793843746, 0.466976523399, 0.632820010185, 0.483534991741, - 0.632828891277, 0.500100016594, 0.632820010185, 0.516664981842, - 0.632793843746, 0.533223450184, 0.632517397404, 0.549681544304, - 0.632532536983, 0.566216289997, 0.632551133633, 0.582756936550, - 0.632667422295, 0.599375545979, 0.632643520832, 0.615900576115, - 0.632630288601, 0.632430315018, 0.632595777512, 0.648932754993, - 0.632488071918, 0.665335118771, 0.632495641708, 0.681868970394, - 0.632503390312, 0.698405086994, 0.632511377335, 0.714943528175, - 0.632590711117, 0.731608748436, 0.632467389107, 0.747913897038, - 0.632425308228, 0.764350593090, 0.632357060909, 0.780721247196, - 0.632353067398, 0.797219336033, 0.632326960564, 0.813663959503, - 0.632273316383, 0.830033361912, 0.632273316383, 0.846530020237, - 0.632273316383, 0.863026678562, 0.632273316383, 0.879523336887, - 0.632273316383, 0.896019995213, 0.632273316383, 0.912516653538, - 0.632273316383, 0.929013311863, 0.632273316383, 0.945510029793, - 0.632273316383, 0.962006688118, 0.632273316383, 0.978503346443, - 0.632273316383, 0.995000004768, 0.648769974709, 0.005200000014, - 0.648769974709, 0.021696666256, 0.648769974709, 0.038193333894, - 0.648769974709, 0.054689999670, 0.648769974709, 0.071186669171, - 0.648769974709, 0.087683334947, 0.648769974709, 0.104180000722, - 0.648769974709, 0.120676666498, 0.648769974709, 0.137173339725, - 0.648769974709, 0.153669998050, 0.648769974709, 0.170166671276, - 0.648817241192, 0.186563611031, 0.648846030235, 0.203007981181, - 0.648854672909, 0.219496786594, 0.648873388767, 0.235969558358, - 0.648962020874, 0.252329915762, 0.649077236652, 0.268668740988, - 0.649157106876, 0.285084187984, 0.649034976959, 0.301786661148, - 0.649026572704, 0.318323045969, 0.649018526077, 0.334857225418, - 0.649010777473, 0.351389229298, 0.649132788181, 0.367804199457, - 0.649166882038, 0.384314626455, 0.649178504944, 0.400847673416, - 0.649200737476, 0.417377382517, 0.649219810963, 0.433913439512, - 0.649084746838, 0.450505077839, 0.649073362350, 0.467039257288, - 0.649066388607, 0.483570396900, 0.649064004421, 0.500100016594, - 0.649066388607, 0.516629576683, 0.649073362350, 0.533160746098, - 0.649084746838, 0.549694895744, 0.649219810963, 0.566286563873, - 0.649200737476, 0.582822620869, 0.649178504944, 0.599352300167, - 0.649166882038, 0.615885376930, 0.649132788181, 0.632395803928, - 0.649010777473, 0.648810744286, 0.649018526077, 0.665342807770, - 0.649026572704, 0.681876957417, 0.649034976959, 0.698413312435, - 0.649157106876, 0.715115845203, 0.649077236652, 0.731531262398, - 0.648962020874, 0.747870087624, 0.648873388767, 0.764230430126, - 0.648854672909, 0.780703246593, 0.648846030235, 0.797192037106, - 0.648817241192, 0.813636422157, 0.648769974709, 0.830033361912, - 0.648769974709, 0.846530020237, 0.648769974709, 0.863026678562, - 0.648769974709, 0.879523336887, 0.648769974709, 0.896019995213, - 0.648769974709, 0.912516653538, 0.648769974709, 0.929013311863, - 0.648769974709, 0.945510029793, 0.648769974709, 0.962006688118, - 0.648769974709, 0.978503346443, 0.648769974709, 0.995000004768, - 0.665266692638, 0.005200000014, 0.665266692638, 0.021696666256, - 0.665266692638, 0.038193333894, 0.665266692638, 0.054689999670, - 0.665266692638, 0.071186669171, 0.665266692638, 0.087683334947, - 0.665266692638, 0.104180000722, 0.665266692638, 0.120676666498, - 0.665266692638, 0.137173339725, 0.665266692638, 0.153669998050, - 0.665266692638, 0.170166671276, 0.665266692638, 0.186663329601, - 0.665334522724, 0.203037843108, 0.665365397930, 0.219488814473, - 0.665369451046, 0.235988914967, 0.665448367596, 0.252377480268, - 0.665497124195, 0.268824011087, 0.665633857250, 0.285165965557, - 0.665717124939, 0.301599413157, 0.665559530258, 0.318314522505, - 0.665550947189, 0.334849059582, 0.665542781353, 0.351381480694, - 0.665535092354, 0.367911905050, 0.665659964085, 0.384348005056, - 0.665692269802, 0.400864630938, 0.665703296661, 0.417398363352, - 0.665722727776, 0.433930903673, 0.665738403797, 0.450468480587, - 0.665749847889, 0.467010021210, 0.665756881237, 0.483554303646, - 0.665759265423, 0.500100016594, 0.665756881237, 0.516645669937, - 0.665749847889, 0.533189952374, 0.665738403797, 0.549731492996, - 0.665722727776, 0.566269099712, 0.665703296661, 0.582801640034, - 0.665692269802, 0.599335372448, 0.665659964085, 0.615851998329, - 0.665535092354, 0.632288098335, 0.665542781353, 0.648818492889, - 0.665550947189, 0.665350973606, 0.665559530258, 0.681885480881, - 0.665717124939, 0.698600590229, 0.665633857250, 0.715034008026, - 0.665497124195, 0.731375992298, 0.665448367596, 0.747822523117, - 0.665369451046, 0.764211058617, 0.665365397930, 0.780711174011, - 0.665334522724, 0.797162175179, 0.665266692638, 0.813536643982, - 0.665266692638, 0.830033361912, 0.665266692638, 0.846530020237, - 0.665266692638, 0.863026678562, 0.665266692638, 0.879523336887, - 0.665266692638, 0.896019995213, 0.665266692638, 0.912516653538, - 0.665266692638, 0.929013311863, 0.665266692638, 0.945510029793, - 0.665266692638, 0.962006688118, 0.665266692638, 0.978503346443, - 0.665266692638, 0.995000004768, 0.681763350964, 0.005200000014, - 0.681763350964, 0.021696666256, 0.681763350964, 0.038193333894, - 0.681763350964, 0.054689999670, 0.681763350964, 0.071186669171, - 0.681763350964, 0.087683334947, 0.681763350964, 0.104180000722, - 0.681763350964, 0.120676666498, 0.681763350964, 0.137173339725, - 0.681763350964, 0.153669998050, 0.681763350964, 0.170166671276, - 0.681763350964, 0.186663329601, 0.681763350964, 0.203160002828, - 0.681851387024, 0.219520568848, 0.681862056255, 0.236009716988, - 0.681883335114, 0.252486377954, 0.681977748871, 0.268873780966, - 0.682028293610, 0.285330235958, 0.682181596756, 0.301683694124, - 0.682266414165, 0.318133622408, 0.682085454464, 0.334840476513, - 0.682076931000, 0.351373404264, 0.682069003582, 0.367904365063, - 0.682061672211, 0.384433507919, 0.682174921036, 0.400895506144, - 0.682204008102, 0.417416363955, 0.682228684425, 0.433944106102, - 0.682248413563, 0.450477689505, 0.682243645191, 0.467019349337, - 0.682250678539, 0.483559042215, 0.682253062725, 0.500100016594, - 0.682250678539, 0.516640961170, 0.682243645191, 0.533180654049, - 0.682248413563, 0.549722313881, 0.682228684425, 0.566255867481, - 0.682204008102, 0.582783639431, 0.682174921036, 0.599304497242, - 0.682061672211, 0.615766525269, 0.682069003582, 0.632295608521, - 0.682076931000, 0.648826599121, 0.682085454464, 0.665359497070, - 0.682266414165, 0.682066380978, 0.682181596756, 0.698516309261, - 0.682028293610, 0.714869797230, 0.681977748871, 0.731326222420, - 0.681883335114, 0.747713625431, 0.681862056255, 0.764190256596, - 0.681851387024, 0.780679404736, 0.681763350964, 0.797039985657, - 0.681763350964, 0.813536643982, 0.681763350964, 0.830033361912, - 0.681763350964, 0.846530020237, 0.681763350964, 0.863026678562, - 0.681763350964, 0.879523336887, 0.681763350964, 0.896019995213, - 0.681763350964, 0.912516653538, 0.681763350964, 0.929013311863, - 0.681763350964, 0.945510029793, 0.681763350964, 0.962006688118, - 0.681763350964, 0.978503346443, 0.681763350964, 0.995000004768, - 0.698260009289, 0.005200000014, 0.698260009289, 0.021696666256, - 0.698260009289, 0.038193333894, 0.698260009289, 0.054689999670, - 0.698260009289, 0.071186669171, 0.698260009289, 0.087683334947, - 0.698260009289, 0.104180000722, 0.698260009289, 0.120676666498, - 0.698260009289, 0.137173339725, 0.698260009289, 0.153669998050, - 0.698260009289, 0.170166671276, 0.698260009289, 0.186663329601, - 0.698260009289, 0.203160002828, 0.698332190514, 0.219554439187, - 0.698366641998, 0.236011117697, 0.698373615742, 0.252508014441, - 0.698395490646, 0.268988579512, 0.698501944542, 0.285381257534, - 0.698553204536, 0.301846802235, 0.698716282845, 0.318218380213, - 0.698800563812, 0.334682852030, 0.698613345623, 0.351365000010, - 0.698605120182, 0.367896586657, 0.698597609997, 0.384426414967, - 0.698590815067, 0.400954604149, 0.698584914207, 0.417481303215, - 0.698700487614, 0.433966487646, 0.698720395565, 0.450494885445, - 0.698734879494, 0.467027515173, 0.698743700981, 0.483563035727, - 0.698746681213, 0.500100016594, 0.698743700981, 0.516636967659, - 0.698734879494, 0.533172488213, 0.698720395565, 0.549705088139, - 0.698700487614, 0.566233515739, 0.698584914207, 0.582718729973, - 0.698590815067, 0.599245429039, 0.698597609997, 0.615773618221, - 0.698605120182, 0.632303416729, 0.698613345623, 0.648835003376, - 0.698800563812, 0.665517151356, 0.698716282845, 0.681981623173, - 0.698553204536, 0.698353230953, 0.698501944542, 0.714818716049, - 0.698395490646, 0.731211423874, 0.698373615742, 0.747691988945, - 0.698366641998, 0.764188885689, 0.698332190514, 0.780645549297, - 0.698260009289, 0.797039985657, 0.698260009289, 0.813536643982, - 0.698260009289, 0.830033361912, 0.698260009289, 0.846530020237, - 0.698260009289, 0.863026678562, 0.698260009289, 0.879523336887, - 0.698260009289, 0.896019995213, 0.698260009289, 0.912516653538, - 0.698260009289, 0.929013311863, 0.698260009289, 0.945510029793, - 0.698260009289, 0.962006688118, 0.698260009289, 0.978503346443, - 0.698260009289, 0.995000004768, 0.714756667614, 0.005200000014, - 0.714756667614, 0.021696666256, 0.714756667614, 0.038193333894, - 0.714756667614, 0.054689999670, 0.714756667614, 0.071186669171, - 0.714756667614, 0.087683334947, 0.714756667614, 0.104180000722, - 0.714756667614, 0.120676666498, 0.714756667614, 0.137173339725, - 0.714756667614, 0.153669998050, 0.714756667614, 0.170166671276, - 0.714756667614, 0.186663329601, 0.714756667614, 0.203160002828, - 0.714756667614, 0.219656661153, 0.714843750000, 0.236046120524, - 0.714879155159, 0.252508640289, 0.714882969856, 0.269010663033, - 0.714905142784, 0.285494863987, 0.715018749237, 0.301898092031, - 0.715069770813, 0.318371742964, 0.715234041214, 0.334766119719, - 0.715315818787, 0.351242899895, 0.715143501759, 0.367888599634, - 0.715135812759, 0.384419173002, 0.715128958225, 0.400948196650, - 0.715122938156, 0.417475789785, 0.715117871761, 0.434002190828, - 0.715113878250, 0.450527548790, 0.715110957623, 0.467052161694, - 0.715109229088, 0.483576208353, 0.715108633041, 0.500100016594, - 0.715109229088, 0.516623795033, 0.715110957623, 0.533147871494, - 0.715113878250, 0.549672424793, 0.715117871761, 0.566197812557, - 0.715122938156, 0.582724213600, 0.715128958225, 0.599251806736, - 0.715135812759, 0.615780830383, 0.715143501759, 0.632311403751, - 0.715315818787, 0.648957133293, 0.715234041214, 0.665433883667, - 0.715069770813, 0.681828260422, 0.715018749237, 0.698301911354, - 0.714905142784, 0.714705169201, 0.714882969856, 0.731189310551, - 0.714879155159, 0.747691392899, 0.714843750000, 0.764153897762, - 0.714756667614, 0.780543327332, 0.714756667614, 0.797039985657, - 0.714756667614, 0.813536643982, 0.714756667614, 0.830033361912, - 0.714756667614, 0.846530020237, 0.714756667614, 0.863026678562, - 0.714756667614, 0.879523336887, 0.714756667614, 0.896019995213, - 0.714756667614, 0.912516653538, 0.714756667614, 0.929013311863, - 0.714756667614, 0.945510029793, 0.714756667614, 0.962006688118, - 0.714756667614, 0.978503346443, 0.714756667614, 0.995000004768, - 0.731253325939, 0.005200000014, 0.731253325939, 0.021696666256, - 0.731253325939, 0.038193333894, 0.731253325939, 0.054689999670, - 0.731253325939, 0.071186669171, 0.731253325939, 0.087683334947, - 0.731253325939, 0.104180000722, 0.731253325939, 0.120676666498, - 0.731253325939, 0.137173339725, 0.731253325939, 0.153669998050, - 0.731253325939, 0.170166671276, 0.731253325939, 0.186663329601, - 0.731253325939, 0.203160002828, 0.731253325939, 0.219656661153, - 0.731253325939, 0.236153334379, 0.731351971626, 0.252544313669, - 0.731387794018, 0.269012212753, 0.731389343739, 0.285517036915, - 0.731411457062, 0.302004486322, 0.731526196003, 0.318422257900, - 0.731575965881, 0.334902882576, 0.731731235981, 0.351322770119, - 0.731808722019, 0.367809295654, 0.731879234314, 0.384310394526, - 0.731669425964, 0.400941699743, 0.731663346291, 0.417470246553, - 0.731658279896, 0.433997631073, 0.731654226780, 0.450524091721, - 0.731651306152, 0.467049807310, 0.731649577618, 0.483575046062, - 0.731648981571, 0.500100016594, 0.731649577618, 0.516624987125, - 0.731651306152, 0.533150196075, 0.731654226780, 0.549675881863, - 0.731658279896, 0.566202342510, 0.731663346291, 0.582729756832, - 0.731669425964, 0.599258303642, 0.731879234314, 0.615889608860, - 0.731808722019, 0.632390737534, 0.731731235981, 0.648877263069, - 0.731575965881, 0.665297150612, 0.731526196003, 0.681777715683, - 0.731411457062, 0.698195517063, 0.731389343739, 0.714682936668, - 0.731387794018, 0.731187760830, 0.731351971626, 0.747655689716, - 0.731253325939, 0.764046669006, 0.731253325939, 0.780543327332, - 0.731253325939, 0.797039985657, 0.731253325939, 0.813536643982, - 0.731253325939, 0.830033361912, 0.731253325939, 0.846530020237, - 0.731253325939, 0.863026678562, 0.731253325939, 0.879523336887, - 0.731253325939, 0.896019995213, 0.731253325939, 0.912516653538, - 0.731253325939, 0.929013311863, 0.731253325939, 0.945510029793, - 0.731253325939, 0.962006688118, 0.731253325939, 0.978503346443, - 0.731253325939, 0.995000004768, 0.747749984264, 0.005200000014, - 0.747749984264, 0.021696666256, 0.747749984264, 0.038193333894, - 0.747749984264, 0.054689999670, 0.747749984264, 0.071186669171, - 0.747749984264, 0.087683334947, 0.747749984264, 0.104180000722, - 0.747749984264, 0.120676666498, 0.747749984264, 0.137173339725, - 0.747749984264, 0.153669998050, 0.747749984264, 0.170166671276, - 0.747749984264, 0.186663329601, 0.747749984264, 0.203160002828, - 0.747749984264, 0.219656661153, 0.747749984264, 0.236153334379, - 0.747749984264, 0.252649992704, 0.747855663300, 0.269048035145, - 0.747891366482, 0.285520821810, 0.747892022133, 0.302026391029, - 0.747913599014, 0.318516671658, 0.748022556305, 0.334951639175, - 0.748070061207, 0.351437956095, 0.748113870621, 0.367932587862, - 0.748276531696, 0.384377628565, 0.748340129852, 0.400883942842, - 0.748395204544, 0.417401611805, 0.748441159725, 0.433929026127, - 0.748196959496, 0.450520604849, 0.748194038868, 0.467047452927, - 0.748192250729, 0.483573853970, 0.748191654682, 0.500100016594, - 0.748192250729, 0.516626179218, 0.748194038868, 0.533152520657, - 0.748196959496, 0.549679398537, 0.748441159725, 0.566270947456, - 0.748395204544, 0.582798421383, 0.748340129852, 0.599316060543, - 0.748276531696, 0.615822374821, 0.748113870621, 0.632267415524, - 0.748070061207, 0.648762047291, 0.748022556305, 0.665248334408, - 0.747913599014, 0.681683301926, 0.747892022133, 0.698173582554, - 0.747891366482, 0.714679181576, 0.747855663300, 0.731151998043, - 0.747749984264, 0.747550010681, 0.747749984264, 0.764046669006, - 0.747749984264, 0.780543327332, 0.747749984264, 0.797039985657, - 0.747749984264, 0.813536643982, 0.747749984264, 0.830033361912, - 0.747749984264, 0.846530020237, 0.747749984264, 0.863026678562, - 0.747749984264, 0.879523336887, 0.747749984264, 0.896019995213, - 0.747749984264, 0.912516653538, 0.747749984264, 0.929013311863, - 0.747749984264, 0.945510029793, 0.747749984264, 0.962006688118, - 0.747749984264, 0.978503346443, 0.747749984264, 0.995000004768, - 0.764246642590, 0.005200000014, 0.764246642590, 0.021696666256, - 0.764246642590, 0.038193333894, 0.764246642590, 0.054689999670, - 0.764246642590, 0.071186669171, 0.764246642590, 0.087683334947, - 0.764246642590, 0.104180000722, 0.764246642590, 0.120676666498, - 0.764246642590, 0.137173339725, 0.764246642590, 0.153669998050, - 0.764246642590, 0.170166671276, 0.764246642590, 0.186663329601, - 0.764246642590, 0.203160002828, 0.764246642590, 0.219656661153, - 0.764246642590, 0.236153334379, 0.764246642590, 0.252649992704, - 0.764246642590, 0.269146680832, 0.764353871346, 0.285556226969, - 0.764388859272, 0.302033334970, 0.764390289783, 0.318537920713, - 0.764411091805, 0.335030585527, 0.764430463314, 0.351526618004, - 0.764550566673, 0.367974728346, 0.764590799809, 0.384472787380, - 0.764626443386, 0.400977581739, 0.764772415161, 0.417452365160, - 0.764818608761, 0.433970332146, 0.764855086803, 0.450495928526, - 0.764881432056, 0.467027336359, 0.764897346497, 0.483562678099, - 0.764902651310, 0.500100016594, 0.764897346497, 0.516637325287, - 0.764881432056, 0.533172667027, 0.764855086803, 0.549704074860, - 0.764818608761, 0.566229641438, 0.764772415161, 0.582747638226, - 0.764626443386, 0.599222421646, 0.764590799809, 0.615727245808, - 0.764550566673, 0.632225275040, 0.764430463314, 0.648673355579, - 0.764411091805, 0.665169417858, 0.764390289783, 0.681662082672, - 0.764388859272, 0.698166668415, 0.764353871346, 0.714643776417, - 0.764246642590, 0.731053352356, 0.764246642590, 0.747550010681, - 0.764246642590, 0.764046669006, 0.764246642590, 0.780543327332, - 0.764246642590, 0.797039985657, 0.764246642590, 0.813536643982, - 0.764246642590, 0.830033361912, 0.764246642590, 0.846530020237, - 0.764246642590, 0.863026678562, 0.764246642590, 0.879523336887, - 0.764246642590, 0.896019995213, 0.764246642590, 0.912516653538, - 0.764246642590, 0.929013311863, 0.764246642590, 0.945510029793, - 0.764246642590, 0.962006688118, 0.764246642590, 0.978503346443, - 0.764246642590, 0.995000004768, 0.780743360519, 0.005200000014, - 0.780743360519, 0.021696666256, 0.780743360519, 0.038193333894, - 0.780743360519, 0.054689999670, 0.780743360519, 0.071186669171, - 0.780743360519, 0.087683334947, 0.780743360519, 0.104180000722, - 0.780743360519, 0.120676666498, 0.780743360519, 0.137173339725, - 0.780743360519, 0.153669998050, 0.780743360519, 0.170166671276, - 0.780743360519, 0.186663329601, 0.780743360519, 0.203160002828, - 0.780743360519, 0.219656661153, 0.780743360519, 0.236153334379, - 0.780743360519, 0.252649992704, 0.780743360519, 0.269146680832, - 0.780743360519, 0.285643339157, 0.780845582485, 0.302067846060, - 0.780879437923, 0.318548619747, 0.780911207199, 0.335034608841, - 0.780903220177, 0.351545363665, 0.780921220779, 0.368042945862, - 0.780937492847, 0.384543389082, 0.781052231789, 0.401010990143, - 0.781083226204, 0.417516708374, 0.781108975410, 0.434027314186, - 0.781129300594, 0.450541883707, 0.781143903732, 0.467059522867, - 0.781152784824, 0.483579248190, 0.781155765057, 0.500100016594, - 0.781152784824, 0.516620755196, 0.781143903732, 0.533140480518, - 0.781129300594, 0.549658119678, 0.781108975410, 0.566172719002, - 0.781083226204, 0.582683265209, 0.781052231789, 0.599188983440, - 0.780937492847, 0.615656614304, 0.780921220779, 0.632157027721, - 0.780903220177, 0.648654639721, 0.780911207199, 0.665165424347, - 0.780879437923, 0.681651413441, 0.780845582485, 0.698132157326, - 0.780743360519, 0.714556694031, 0.780743360519, 0.731053352356, - 0.780743360519, 0.747550010681, 0.780743360519, 0.764046669006, - 0.780743360519, 0.780543327332, 0.780743360519, 0.797039985657, - 0.780743360519, 0.813536643982, 0.780743360519, 0.830033361912, - 0.780743360519, 0.846530020237, 0.780743360519, 0.863026678562, - 0.780743360519, 0.879523336887, 0.780743360519, 0.896019995213, - 0.780743360519, 0.912516653538, 0.780743360519, 0.929013311863, - 0.780743360519, 0.945510029793, 0.780743360519, 0.962006688118, - 0.780743360519, 0.978503346443, 0.780743360519, 0.995000004768, - 0.797240018845, 0.005200000014, 0.797240018845, 0.021696666256, - 0.797240018845, 0.038193333894, 0.797240018845, 0.054689999670, - 0.797240018845, 0.071186669171, 0.797240018845, 0.087683334947, - 0.797240018845, 0.104180000722, 0.797240018845, 0.120676666498, - 0.797240018845, 0.137173339725, 0.797240018845, 0.153669998050, - 0.797240018845, 0.170166671276, 0.797240018845, 0.186663329601, - 0.797240018845, 0.203160002828, 0.797240018845, 0.219656661153, - 0.797240018845, 0.236153334379, 0.797240018845, 0.252649992704, - 0.797240018845, 0.269146680832, 0.797240018845, 0.285643339157, - 0.797240018845, 0.302139997482, 0.797240018845, 0.318636655807, - 0.797362148762, 0.335065454245, 0.797392010689, 0.351553976536, - 0.797419369221, 0.368046969175, 0.797405958176, 0.384558796883, - 0.797420442104, 0.401059836149, 0.797432899475, 0.417563080788, - 0.797443270683, 0.434068173170, 0.797543525696, 0.450559407473, - 0.797558188438, 0.467071324587, 0.797567009926, 0.483585178852, - 0.797569990158, 0.500100016594, 0.797567009926, 0.516614854336, - 0.797558188438, 0.533128678799, 0.797543525696, 0.549640595913, - 0.797443270683, 0.566131830215, 0.797432899475, 0.582636892796, - 0.797420442104, 0.599140167236, 0.797405958176, 0.615641236305, - 0.797419369221, 0.632153034210, 0.797392010689, 0.648645997047, - 0.797362148762, 0.665134549141, 0.797240018845, 0.681563317776, - 0.797240018845, 0.698059976101, 0.797240018845, 0.714556694031, - 0.797240018845, 0.731053352356, 0.797240018845, 0.747550010681, - 0.797240018845, 0.764046669006, 0.797240018845, 0.780543327332, - 0.797240018845, 0.797039985657, 0.797240018845, 0.813536643982, - 0.797240018845, 0.830033361912, 0.797240018845, 0.846530020237, - 0.797240018845, 0.863026678562, 0.797240018845, 0.879523336887, - 0.797240018845, 0.896019995213, 0.797240018845, 0.912516653538, - 0.797240018845, 0.929013311863, 0.797240018845, 0.945510029793, - 0.797240018845, 0.962006688118, 0.797240018845, 0.978503346443, - 0.797240018845, 0.995000004768, 0.813736677170, 0.005200000014, - 0.813736677170, 0.021696666256, 0.813736677170, 0.038193333894, - 0.813736677170, 0.054689999670, 0.813736677170, 0.071186669171, - 0.813736677170, 0.087683334947, 0.813736677170, 0.104180000722, - 0.813736677170, 0.120676666498, 0.813736677170, 0.137173339725, - 0.813736677170, 0.153669998050, 0.813736677170, 0.170166671276, - 0.813736677170, 0.186663329601, 0.813736677170, 0.203160002828, - 0.813736677170, 0.219656661153, 0.813736677170, 0.236153334379, - 0.813736677170, 0.252649992704, 0.813736677170, 0.269146680832, - 0.813736677170, 0.285643339157, 0.813736677170, 0.302139997482, - 0.813736677170, 0.318636655807, 0.813736677170, 0.335133343935, - 0.813836395741, 0.351582765579, 0.813863992691, 0.368073076010, - 0.813888788223, 0.384567290545, 0.813910603523, 0.401065051556, - 0.813896834850, 0.417574524879, 0.813907206059, 0.434077441692, - 0.813915371895, 0.450581789017, 0.813921213150, 0.467087239027, - 0.813924789429, 0.483593434095, 0.813925981522, 0.500100016594, - 0.813924789429, 0.516606569290, 0.813921213150, 0.533112764359, - 0.813915371895, 0.549618244171, 0.813907206059, 0.566122591496, - 0.813896834850, 0.582625508308, 0.813910603523, 0.599134922028, - 0.813888788223, 0.615632712841, 0.813863992691, 0.632126927376, - 0.813836395741, 0.648617267609, 0.813736677170, 0.665066659451, - 0.813736677170, 0.681563317776, 0.813736677170, 0.698059976101, - 0.813736677170, 0.714556694031, 0.813736677170, 0.731053352356, - 0.813736677170, 0.747550010681, 0.813736677170, 0.764046669006, - 0.813736677170, 0.780543327332, 0.813736677170, 0.797039985657, - 0.813736677170, 0.813536643982, 0.813736677170, 0.830033361912, - 0.813736677170, 0.846530020237, 0.813736677170, 0.863026678562, - 0.813736677170, 0.879523336887, 0.813736677170, 0.896019995213, - 0.813736677170, 0.912516653538, 0.813736677170, 0.929013311863, - 0.813736677170, 0.945510029793, 0.813736677170, 0.962006688118, - 0.813736677170, 0.978503346443, 0.813736677170, 0.995000004768, - 0.830233335495, 0.005200000014, 0.830233335495, 0.021696666256, - 0.830233335495, 0.038193333894, 0.830233335495, 0.054689999670, - 0.830233335495, 0.071186669171, 0.830233335495, 0.087683334947, - 0.830233335495, 0.104180000722, 0.830233335495, 0.120676666498, - 0.830233335495, 0.137173339725, 0.830233335495, 0.153669998050, - 0.830233335495, 0.170166671276, 0.830233335495, 0.186663329601, - 0.830233335495, 0.203160002828, 0.830233335495, 0.219656661153, - 0.830233335495, 0.236153334379, 0.830233335495, 0.252649992704, - 0.830233335495, 0.269146680832, 0.830233335495, 0.285643339157, - 0.830233335495, 0.302139997482, 0.830233335495, 0.318636655807, - 0.830233335495, 0.335133343935, 0.830233335495, 0.351630002260, - 0.830233335495, 0.368126660585, 0.830233335495, 0.384623318911, - 0.830348491669, 0.401085466146, 0.830367326736, 0.417583167553, - 0.830382943153, 0.434083402157, 0.830395221710, 0.450585722923, - 0.830404043198, 0.467089593410, 0.830409348011, 0.483594536781, - 0.830411136150, 0.500100016594, 0.830409348011, 0.516605496407, - 0.830404043198, 0.533110380173, 0.830395221710, 0.549614250660, - 0.830382943153, 0.566116571426, 0.830367326736, 0.582616806030, - 0.830348491669, 0.599114537239, 0.830233335495, 0.615576684475, - 0.830233335495, 0.632073342800, 0.830233335495, 0.648570001125, - 0.830233335495, 0.665066659451, 0.830233335495, 0.681563317776, - 0.830233335495, 0.698059976101, 0.830233335495, 0.714556694031, - 0.830233335495, 0.731053352356, 0.830233335495, 0.747550010681, - 0.830233335495, 0.764046669006, 0.830233335495, 0.780543327332, - 0.830233335495, 0.797039985657, 0.830233335495, 0.813536643982, - 0.830233335495, 0.830033361912, 0.830233335495, 0.846530020237, - 0.830233335495, 0.863026678562, 0.830233335495, 0.879523336887, - 0.830233335495, 0.896019995213, 0.830233335495, 0.912516653538, - 0.830233335495, 0.929013311863, 0.830233335495, 0.945510029793, - 0.830233335495, 0.962006688118, 0.830233335495, 0.978503346443, - 0.830233335495, 0.995000004768, 0.846729993820, 0.005200000014, - 0.846729993820, 0.021696666256, 0.846729993820, 0.038193333894, - 0.846729993820, 0.054689999670, 0.846729993820, 0.071186669171, - 0.846729993820, 0.087683334947, 0.846729993820, 0.104180000722, - 0.846729993820, 0.120676666498, 0.846729993820, 0.137173339725, - 0.846729993820, 0.153669998050, 0.846729993820, 0.170166671276, - 0.846729993820, 0.186663329601, 0.846729993820, 0.203160002828, - 0.846729993820, 0.219656661153, 0.846729993820, 0.236153334379, - 0.846729993820, 0.252649992704, 0.846729993820, 0.269146680832, - 0.846729993820, 0.285643339157, 0.846729993820, 0.302139997482, - 0.846729993820, 0.318636655807, 0.846729993820, 0.335133343935, - 0.846729993820, 0.351630002260, 0.846729993820, 0.368126660585, - 0.846729993820, 0.384623318911, 0.846729993820, 0.401120007038, - 0.846729993820, 0.417616665363, 0.846729993820, 0.434113323689, - 0.846729993820, 0.450610011816, 0.846729993820, 0.467106670141, - 0.846840202808, 0.483598083258, 0.846841990948, 0.500100016594, - 0.846840202808, 0.516601920128, 0.846729993820, 0.533093333244, - 0.846729993820, 0.549589991570, 0.846729993820, 0.566086649895, - 0.846729993820, 0.582583308220, 0.846729993820, 0.599080026150, - 0.846729993820, 0.615576684475, 0.846729993820, 0.632073342800, - 0.846729993820, 0.648570001125, 0.846729993820, 0.665066659451, - 0.846729993820, 0.681563317776, 0.846729993820, 0.698059976101, - 0.846729993820, 0.714556694031, 0.846729993820, 0.731053352356, - 0.846729993820, 0.747550010681, 0.846729993820, 0.764046669006, - 0.846729993820, 0.780543327332, 0.846729993820, 0.797039985657, - 0.846729993820, 0.813536643982, 0.846729993820, 0.830033361912, - 0.846729993820, 0.846530020237, 0.846729993820, 0.863026678562, - 0.846729993820, 0.879523336887, 0.846729993820, 0.896019995213, - 0.846729993820, 0.912516653538, 0.846729993820, 0.929013311863, - 0.846729993820, 0.945510029793, 0.846729993820, 0.962006688118, - 0.846729993820, 0.978503346443, 0.846729993820, 0.995000004768, - 0.863226652145, 0.005200000014, 0.863226652145, 0.021696666256, - 0.863226652145, 0.038193333894, 0.863226652145, 0.054689999670, - 0.863226652145, 0.071186669171, 0.863226652145, 0.087683334947, - 0.863226652145, 0.104180000722, 0.863226652145, 0.120676666498, - 0.863226652145, 0.137173339725, 0.863226652145, 0.153669998050, - 0.863226652145, 0.170166671276, 0.863226652145, 0.186663329601, - 0.863226652145, 0.203160002828, 0.863226652145, 0.219656661153, - 0.863226652145, 0.236153334379, 0.863226652145, 0.252649992704, - 0.863226652145, 0.269146680832, 0.863226652145, 0.285643339157, - 0.863226652145, 0.302139997482, 0.863226652145, 0.318636655807, - 0.863226652145, 0.335133343935, 0.863226652145, 0.351630002260, - 0.863226652145, 0.368126660585, 0.863226652145, 0.384623318911, - 0.863226652145, 0.401120007038, 0.863226652145, 0.417616665363, - 0.863226652145, 0.434113323689, 0.863226652145, 0.450610011816, - 0.863226652145, 0.467106670141, 0.863226652145, 0.483603328466, - 0.863226652145, 0.500100016594, 0.863226652145, 0.516596674919, - 0.863226652145, 0.533093333244, 0.863226652145, 0.549589991570, - 0.863226652145, 0.566086649895, 0.863226652145, 0.582583308220, - 0.863226652145, 0.599080026150, 0.863226652145, 0.615576684475, - 0.863226652145, 0.632073342800, 0.863226652145, 0.648570001125, - 0.863226652145, 0.665066659451, 0.863226652145, 0.681563317776, - 0.863226652145, 0.698059976101, 0.863226652145, 0.714556694031, - 0.863226652145, 0.731053352356, 0.863226652145, 0.747550010681, - 0.863226652145, 0.764046669006, 0.863226652145, 0.780543327332, - 0.863226652145, 0.797039985657, 0.863226652145, 0.813536643982, - 0.863226652145, 0.830033361912, 0.863226652145, 0.846530020237, - 0.863226652145, 0.863026678562, 0.863226652145, 0.879523336887, - 0.863226652145, 0.896019995213, 0.863226652145, 0.912516653538, - 0.863226652145, 0.929013311863, 0.863226652145, 0.945510029793, - 0.863226652145, 0.962006688118, 0.863226652145, 0.978503346443, - 0.863226652145, 0.995000004768, 0.879723310471, 0.005200000014, - 0.879723310471, 0.021696666256, 0.879723310471, 0.038193333894, - 0.879723310471, 0.054689999670, 0.879723310471, 0.071186669171, - 0.879723310471, 0.087683334947, 0.879723310471, 0.104180000722, - 0.879723310471, 0.120676666498, 0.879723310471, 0.137173339725, - 0.879723310471, 0.153669998050, 0.879723310471, 0.170166671276, - 0.879723310471, 0.186663329601, 0.879723310471, 0.203160002828, - 0.879723310471, 0.219656661153, 0.879723310471, 0.236153334379, - 0.879723310471, 0.252649992704, 0.879723310471, 0.269146680832, - 0.879723310471, 0.285643339157, 0.879723310471, 0.302139997482, - 0.879723310471, 0.318636655807, 0.879723310471, 0.335133343935, - 0.879723310471, 0.351630002260, 0.879723310471, 0.368126660585, - 0.879723310471, 0.384623318911, 0.879723310471, 0.401120007038, - 0.879723310471, 0.417616665363, 0.879723310471, 0.434113323689, - 0.879723310471, 0.450610011816, 0.879723310471, 0.467106670141, - 0.879723310471, 0.483603328466, 0.879723310471, 0.500100016594, - 0.879723310471, 0.516596674919, 0.879723310471, 0.533093333244, - 0.879723310471, 0.549589991570, 0.879723310471, 0.566086649895, - 0.879723310471, 0.582583308220, 0.879723310471, 0.599080026150, - 0.879723310471, 0.615576684475, 0.879723310471, 0.632073342800, - 0.879723310471, 0.648570001125, 0.879723310471, 0.665066659451, - 0.879723310471, 0.681563317776, 0.879723310471, 0.698059976101, - 0.879723310471, 0.714556694031, 0.879723310471, 0.731053352356, - 0.879723310471, 0.747550010681, 0.879723310471, 0.764046669006, - 0.879723310471, 0.780543327332, 0.879723310471, 0.797039985657, - 0.879723310471, 0.813536643982, 0.879723310471, 0.830033361912, - 0.879723310471, 0.846530020237, 0.879723310471, 0.863026678562, - 0.879723310471, 0.879523336887, 0.879723310471, 0.896019995213, - 0.879723310471, 0.912516653538, 0.879723310471, 0.929013311863, - 0.879723310471, 0.945510029793, 0.879723310471, 0.962006688118, - 0.879723310471, 0.978503346443, 0.879723310471, 0.995000004768, - 0.896220028400, 0.005200000014, 0.896220028400, 0.021696666256, - 0.896220028400, 0.038193333894, 0.896220028400, 0.054689999670, - 0.896220028400, 0.071186669171, 0.896220028400, 0.087683334947, - 0.896220028400, 0.104180000722, 0.896220028400, 0.120676666498, - 0.896220028400, 0.137173339725, 0.896220028400, 0.153669998050, - 0.896220028400, 0.170166671276, 0.896220028400, 0.186663329601, - 0.896220028400, 0.203160002828, 0.896220028400, 0.219656661153, - 0.896220028400, 0.236153334379, 0.896220028400, 0.252649992704, - 0.896220028400, 0.269146680832, 0.896220028400, 0.285643339157, - 0.896220028400, 0.302139997482, 0.896220028400, 0.318636655807, - 0.896220028400, 0.335133343935, 0.896220028400, 0.351630002260, - 0.896220028400, 0.368126660585, 0.896220028400, 0.384623318911, - 0.896220028400, 0.401120007038, 0.896220028400, 0.417616665363, - 0.896220028400, 0.434113323689, 0.896220028400, 0.450610011816, - 0.896220028400, 0.467106670141, 0.896220028400, 0.483603328466, - 0.896220028400, 0.500100016594, 0.896220028400, 0.516596674919, - 0.896220028400, 0.533093333244, 0.896220028400, 0.549589991570, - 0.896220028400, 0.566086649895, 0.896220028400, 0.582583308220, - 0.896220028400, 0.599080026150, 0.896220028400, 0.615576684475, - 0.896220028400, 0.632073342800, 0.896220028400, 0.648570001125, - 0.896220028400, 0.665066659451, 0.896220028400, 0.681563317776, - 0.896220028400, 0.698059976101, 0.896220028400, 0.714556694031, - 0.896220028400, 0.731053352356, 0.896220028400, 0.747550010681, - 0.896220028400, 0.764046669006, 0.896220028400, 0.780543327332, - 0.896220028400, 0.797039985657, 0.896220028400, 0.813536643982, - 0.896220028400, 0.830033361912, 0.896220028400, 0.846530020237, - 0.896220028400, 0.863026678562, 0.896220028400, 0.879523336887, - 0.896220028400, 0.896019995213, 0.896220028400, 0.912516653538, - 0.896220028400, 0.929013311863, 0.896220028400, 0.945510029793, - 0.896220028400, 0.962006688118, 0.896220028400, 0.978503346443, - 0.896220028400, 0.995000004768, 0.912716686726, 0.005200000014, - 0.912716686726, 0.021696666256, 0.912716686726, 0.038193333894, - 0.912716686726, 0.054689999670, 0.912716686726, 0.071186669171, - 0.912716686726, 0.087683334947, 0.912716686726, 0.104180000722, - 0.912716686726, 0.120676666498, 0.912716686726, 0.137173339725, - 0.912716686726, 0.153669998050, 0.912716686726, 0.170166671276, - 0.912716686726, 0.186663329601, 0.912716686726, 0.203160002828, - 0.912716686726, 0.219656661153, 0.912716686726, 0.236153334379, - 0.912716686726, 0.252649992704, 0.912716686726, 0.269146680832, - 0.912716686726, 0.285643339157, 0.912716686726, 0.302139997482, - 0.912716686726, 0.318636655807, 0.912716686726, 0.335133343935, - 0.912716686726, 0.351630002260, 0.912716686726, 0.368126660585, - 0.912716686726, 0.384623318911, 0.912716686726, 0.401120007038, - 0.912716686726, 0.417616665363, 0.912716686726, 0.434113323689, - 0.912716686726, 0.450610011816, 0.912716686726, 0.467106670141, - 0.912716686726, 0.483603328466, 0.912716686726, 0.500100016594, - 0.912716686726, 0.516596674919, 0.912716686726, 0.533093333244, - 0.912716686726, 0.549589991570, 0.912716686726, 0.566086649895, - 0.912716686726, 0.582583308220, 0.912716686726, 0.599080026150, - 0.912716686726, 0.615576684475, 0.912716686726, 0.632073342800, - 0.912716686726, 0.648570001125, 0.912716686726, 0.665066659451, - 0.912716686726, 0.681563317776, 0.912716686726, 0.698059976101, - 0.912716686726, 0.714556694031, 0.912716686726, 0.731053352356, - 0.912716686726, 0.747550010681, 0.912716686726, 0.764046669006, - 0.912716686726, 0.780543327332, 0.912716686726, 0.797039985657, - 0.912716686726, 0.813536643982, 0.912716686726, 0.830033361912, - 0.912716686726, 0.846530020237, 0.912716686726, 0.863026678562, - 0.912716686726, 0.879523336887, 0.912716686726, 0.896019995213, - 0.912716686726, 0.912516653538, 0.912716686726, 0.929013311863, - 0.912716686726, 0.945510029793, 0.912716686726, 0.962006688118, - 0.912716686726, 0.978503346443, 0.912716686726, 0.995000004768, - 0.929213345051, 0.005200000014, 0.929213345051, 0.021696666256, - 0.929213345051, 0.038193333894, 0.929213345051, 0.054689999670, - 0.929213345051, 0.071186669171, 0.929213345051, 0.087683334947, - 0.929213345051, 0.104180000722, 0.929213345051, 0.120676666498, - 0.929213345051, 0.137173339725, 0.929213345051, 0.153669998050, - 0.929213345051, 0.170166671276, 0.929213345051, 0.186663329601, - 0.929213345051, 0.203160002828, 0.929213345051, 0.219656661153, - 0.929213345051, 0.236153334379, 0.929213345051, 0.252649992704, - 0.929213345051, 0.269146680832, 0.929213345051, 0.285643339157, - 0.929213345051, 0.302139997482, 0.929213345051, 0.318636655807, - 0.929213345051, 0.335133343935, 0.929213345051, 0.351630002260, - 0.929213345051, 0.368126660585, 0.929213345051, 0.384623318911, - 0.929213345051, 0.401120007038, 0.929213345051, 0.417616665363, - 0.929213345051, 0.434113323689, 0.929213345051, 0.450610011816, - 0.929213345051, 0.467106670141, 0.929213345051, 0.483603328466, - 0.929213345051, 0.500100016594, 0.929213345051, 0.516596674919, - 0.929213345051, 0.533093333244, 0.929213345051, 0.549589991570, - 0.929213345051, 0.566086649895, 0.929213345051, 0.582583308220, - 0.929213345051, 0.599080026150, 0.929213345051, 0.615576684475, - 0.929213345051, 0.632073342800, 0.929213345051, 0.648570001125, - 0.929213345051, 0.665066659451, 0.929213345051, 0.681563317776, - 0.929213345051, 0.698059976101, 0.929213345051, 0.714556694031, - 0.929213345051, 0.731053352356, 0.929213345051, 0.747550010681, - 0.929213345051, 0.764046669006, 0.929213345051, 0.780543327332, - 0.929213345051, 0.797039985657, 0.929213345051, 0.813536643982, - 0.929213345051, 0.830033361912, 0.929213345051, 0.846530020237, - 0.929213345051, 0.863026678562, 0.929213345051, 0.879523336887, - 0.929213345051, 0.896019995213, 0.929213345051, 0.912516653538, - 0.929213345051, 0.929013311863, 0.929213345051, 0.945510029793, - 0.929213345051, 0.962006688118, 0.929213345051, 0.978503346443, - 0.929213345051, 0.995000004768, 0.945710003376, 0.005200000014, - 0.945710003376, 0.021696666256, 0.945710003376, 0.038193333894, - 0.945710003376, 0.054689999670, 0.945710003376, 0.071186669171, - 0.945710003376, 0.087683334947, 0.945710003376, 0.104180000722, - 0.945710003376, 0.120676666498, 0.945710003376, 0.137173339725, - 0.945710003376, 0.153669998050, 0.945710003376, 0.170166671276, - 0.945710003376, 0.186663329601, 0.945710003376, 0.203160002828, - 0.945710003376, 0.219656661153, 0.945710003376, 0.236153334379, - 0.945710003376, 0.252649992704, 0.945710003376, 0.269146680832, - 0.945710003376, 0.285643339157, 0.945710003376, 0.302139997482, - 0.945710003376, 0.318636655807, 0.945710003376, 0.335133343935, - 0.945710003376, 0.351630002260, 0.945710003376, 0.368126660585, - 0.945710003376, 0.384623318911, 0.945710003376, 0.401120007038, - 0.945710003376, 0.417616665363, 0.945710003376, 0.434113323689, - 0.945710003376, 0.450610011816, 0.945710003376, 0.467106670141, - 0.945710003376, 0.483603328466, 0.945710003376, 0.500100016594, - 0.945710003376, 0.516596674919, 0.945710003376, 0.533093333244, - 0.945710003376, 0.549589991570, 0.945710003376, 0.566086649895, - 0.945710003376, 0.582583308220, 0.945710003376, 0.599080026150, - 0.945710003376, 0.615576684475, 0.945710003376, 0.632073342800, - 0.945710003376, 0.648570001125, 0.945710003376, 0.665066659451, - 0.945710003376, 0.681563317776, 0.945710003376, 0.698059976101, - 0.945710003376, 0.714556694031, 0.945710003376, 0.731053352356, - 0.945710003376, 0.747550010681, 0.945710003376, 0.764046669006, - 0.945710003376, 0.780543327332, 0.945710003376, 0.797039985657, - 0.945710003376, 0.813536643982, 0.945710003376, 0.830033361912, - 0.945710003376, 0.846530020237, 0.945710003376, 0.863026678562, - 0.945710003376, 0.879523336887, 0.945710003376, 0.896019995213, - 0.945710003376, 0.912516653538, 0.945710003376, 0.929013311863, - 0.945710003376, 0.945510029793, 0.945710003376, 0.962006688118, - 0.945710003376, 0.978503346443, 0.945710003376, 0.995000004768, - 0.962206661701, 0.005200000014, 0.962206661701, 0.021696666256, - 0.962206661701, 0.038193333894, 0.962206661701, 0.054689999670, - 0.962206661701, 0.071186669171, 0.962206661701, 0.087683334947, - 0.962206661701, 0.104180000722, 0.962206661701, 0.120676666498, - 0.962206661701, 0.137173339725, 0.962206661701, 0.153669998050, - 0.962206661701, 0.170166671276, 0.962206661701, 0.186663329601, - 0.962206661701, 0.203160002828, 0.962206661701, 0.219656661153, - 0.962206661701, 0.236153334379, 0.962206661701, 0.252649992704, - 0.962206661701, 0.269146680832, 0.962206661701, 0.285643339157, - 0.962206661701, 0.302139997482, 0.962206661701, 0.318636655807, - 0.962206661701, 0.335133343935, 0.962206661701, 0.351630002260, - 0.962206661701, 0.368126660585, 0.962206661701, 0.384623318911, - 0.962206661701, 0.401120007038, 0.962206661701, 0.417616665363, - 0.962206661701, 0.434113323689, 0.962206661701, 0.450610011816, - 0.962206661701, 0.467106670141, 0.962206661701, 0.483603328466, - 0.962206661701, 0.500100016594, 0.962206661701, 0.516596674919, - 0.962206661701, 0.533093333244, 0.962206661701, 0.549589991570, - 0.962206661701, 0.566086649895, 0.962206661701, 0.582583308220, - 0.962206661701, 0.599080026150, 0.962206661701, 0.615576684475, - 0.962206661701, 0.632073342800, 0.962206661701, 0.648570001125, - 0.962206661701, 0.665066659451, 0.962206661701, 0.681563317776, - 0.962206661701, 0.698059976101, 0.962206661701, 0.714556694031, - 0.962206661701, 0.731053352356, 0.962206661701, 0.747550010681, - 0.962206661701, 0.764046669006, 0.962206661701, 0.780543327332, - 0.962206661701, 0.797039985657, 0.962206661701, 0.813536643982, - 0.962206661701, 0.830033361912, 0.962206661701, 0.846530020237, - 0.962206661701, 0.863026678562, 0.962206661701, 0.879523336887, - 0.962206661701, 0.896019995213, 0.962206661701, 0.912516653538, - 0.962206661701, 0.929013311863, 0.962206661701, 0.945510029793, - 0.962206661701, 0.962006688118, 0.962206661701, 0.978503346443, - 0.962206661701, 0.995000004768, 0.978703320026, 0.005200000014, - 0.978703320026, 0.021696666256, 0.978703320026, 0.038193333894, - 0.978703320026, 0.054689999670, 0.978703320026, 0.071186669171, - 0.978703320026, 0.087683334947, 0.978703320026, 0.104180000722, - 0.978703320026, 0.120676666498, 0.978703320026, 0.137173339725, - 0.978703320026, 0.153669998050, 0.978703320026, 0.170166671276, - 0.978703320026, 0.186663329601, 0.978703320026, 0.203160002828, - 0.978703320026, 0.219656661153, 0.978703320026, 0.236153334379, - 0.978703320026, 0.252649992704, 0.978703320026, 0.269146680832, - 0.978703320026, 0.285643339157, 0.978703320026, 0.302139997482, - 0.978703320026, 0.318636655807, 0.978703320026, 0.335133343935, - 0.978703320026, 0.351630002260, 0.978703320026, 0.368126660585, - 0.978703320026, 0.384623318911, 0.978703320026, 0.401120007038, - 0.978703320026, 0.417616665363, 0.978703320026, 0.434113323689, - 0.978703320026, 0.450610011816, 0.978703320026, 0.467106670141, - 0.978703320026, 0.483603328466, 0.978703320026, 0.500100016594, - 0.978703320026, 0.516596674919, 0.978703320026, 0.533093333244, - 0.978703320026, 0.549589991570, 0.978703320026, 0.566086649895, - 0.978703320026, 0.582583308220, 0.978703320026, 0.599080026150, - 0.978703320026, 0.615576684475, 0.978703320026, 0.632073342800, - 0.978703320026, 0.648570001125, 0.978703320026, 0.665066659451, - 0.978703320026, 0.681563317776, 0.978703320026, 0.698059976101, - 0.978703320026, 0.714556694031, 0.978703320026, 0.731053352356, - 0.978703320026, 0.747550010681, 0.978703320026, 0.764046669006, - 0.978703320026, 0.780543327332, 0.978703320026, 0.797039985657, - 0.978703320026, 0.813536643982, 0.978703320026, 0.830033361912, - 0.978703320026, 0.846530020237, 0.978703320026, 0.863026678562, - 0.978703320026, 0.879523336887, 0.978703320026, 0.896019995213, - 0.978703320026, 0.912516653538, 0.978703320026, 0.929013311863, - 0.978703320026, 0.945510029793, 0.978703320026, 0.962006688118, - 0.978703320026, 0.978503346443, 0.978703320026, 0.995000004768, - 0.995199978352, 0.005200000014, 0.995199978352, 0.021696666256, - 0.995199978352, 0.038193333894, 0.995199978352, 0.054689999670, - 0.995199978352, 0.071186669171, 0.995199978352, 0.087683334947, - 0.995199978352, 0.104180000722, 0.995199978352, 0.120676666498, - 0.995199978352, 0.137173339725, 0.995199978352, 0.153669998050, - 0.995199978352, 0.170166671276, 0.995199978352, 0.186663329601, - 0.995199978352, 0.203160002828, 0.995199978352, 0.219656661153, - 0.995199978352, 0.236153334379, 0.995199978352, 0.252649992704, - 0.995199978352, 0.269146680832, 0.995199978352, 0.285643339157, - 0.995199978352, 0.302139997482, 0.995199978352, 0.318636655807, - 0.995199978352, 0.335133343935, 0.995199978352, 0.351630002260, - 0.995199978352, 0.368126660585, 0.995199978352, 0.384623318911, - 0.995199978352, 0.401120007038, 0.995199978352, 0.417616665363, - 0.995199978352, 0.434113323689, 0.995199978352, 0.450610011816, - 0.995199978352, 0.467106670141, 0.995199978352, 0.483603328466, - 0.995199978352, 0.500100016594, 0.995199978352, 0.516596674919, - 0.995199978352, 0.533093333244, 0.995199978352, 0.549589991570, - 0.995199978352, 0.566086649895, 0.995199978352, 0.582583308220, - 0.995199978352, 0.599080026150, 0.995199978352, 0.615576684475, - 0.995199978352, 0.632073342800, 0.995199978352, 0.648570001125, - 0.995199978352, 0.665066659451, 0.995199978352, 0.681563317776, - 0.995199978352, 0.698059976101, 0.995199978352, 0.714556694031, - 0.995199978352, 0.731053352356, 0.995199978352, 0.747550010681, - 0.995199978352, 0.764046669006, 0.995199978352, 0.780543327332, - 0.995199978352, 0.797039985657, 0.995199978352, 0.813536643982, - 0.995199978352, 0.830033361912, 0.995199978352, 0.846530020237, - 0.995199978352, 0.863026678562, 0.995199978352, 0.879523336887, - 0.995199978352, 0.896019995213, 0.995199978352, 0.912516653538, - 0.995199978352, 0.929013311863, 0.995199978352, 0.945510029793, - 0.995199978352, 0.962006688118, 0.995199978352, 0.978503346443, - 0.995199978352, 0.995000004768, -}; - -} /* namespace Pdraw */ - -#endif /* USE_GLES2 */ +/** + * Parrot Drones Awesome Video Viewer Library + * OpenGL ES 2.0 HMD distortion correction + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Extracted from FPVToolbox by Frédéric Bertolus + * https://github.com/niavok/fpvtoolbox + * and translated from Java to C++ + */ + +#ifdef USE_GLES2 + +namespace Pdraw { + +extern const float pdraw_gles2HmdTexCoordsCockpitglassesRed[7442] = { + 0.005400000140, 0.005200000014, 0.005400000140, 0.021696666256, + 0.005400000140, 0.038193333894, 0.005400000140, 0.054689999670, + 0.005400000140, 0.071186669171, 0.005400000140, 0.087683334947, + 0.005400000140, 0.104180000722, 0.005400000140, 0.120676666498, + 0.005400000140, 0.137173339725, 0.005400000140, 0.153669998050, + 0.005400000140, 0.170166671276, 0.005400000140, 0.186663329601, + 0.005400000140, 0.203160002828, 0.005400000140, 0.219656661153, + 0.005400000140, 0.236153334379, 0.005400000140, 0.252649992704, + 0.005400000140, 0.269146680832, 0.005400000140, 0.285643339157, + 0.005400000140, 0.302139997482, 0.005400000140, 0.318636655807, + 0.005400000140, 0.335133343935, 0.005400000140, 0.351630002260, + 0.005400000140, 0.368126660585, 0.005400000140, 0.384623318911, + 0.005400000140, 0.401120007038, 0.005400000140, 0.417616665363, + 0.005400000140, 0.434113323689, 0.005400000140, 0.450610011816, + 0.005400000140, 0.467106670141, 0.005400000140, 0.483603328466, + 0.005400000140, 0.500100016594, 0.005400000140, 0.516596674919, + 0.005400000140, 0.533093333244, 0.005400000140, 0.549589991570, + 0.005400000140, 0.566086649895, 0.005400000140, 0.582583308220, + 0.005400000140, 0.599080026150, 0.005400000140, 0.615576684475, + 0.005400000140, 0.632073342800, 0.005400000140, 0.648570001125, + 0.005400000140, 0.665066659451, 0.005400000140, 0.681563317776, + 0.005400000140, 0.698059976101, 0.005400000140, 0.714556694031, + 0.005400000140, 0.731053352356, 0.005400000140, 0.747550010681, + 0.005400000140, 0.764046669006, 0.005400000140, 0.780543327332, + 0.005400000140, 0.797039985657, 0.005400000140, 0.813536643982, + 0.005400000140, 0.830033361912, 0.005400000140, 0.846530020237, + 0.005400000140, 0.863026678562, 0.005400000140, 0.879523336887, + 0.005400000140, 0.896019995213, 0.005400000140, 0.912516653538, + 0.005400000140, 0.929013311863, 0.005400000140, 0.945510029793, + 0.005400000140, 0.962006688118, 0.005400000140, 0.978503346443, + 0.005400000140, 0.995000004768, 0.021896665916, 0.005200000014, + 0.021896665916, 0.021696666256, 0.021896665916, 0.038193333894, + 0.021896665916, 0.054689999670, 0.021896665916, 0.071186669171, + 0.021896665916, 0.087683334947, 0.021896665916, 0.104180000722, + 0.021896665916, 0.120676666498, 0.021896665916, 0.137173339725, + 0.021896665916, 0.153669998050, 0.021896665916, 0.170166671276, + 0.021896665916, 0.186663329601, 0.021896665916, 0.203160002828, + 0.021896665916, 0.219656661153, 0.021896665916, 0.236153334379, + 0.021896665916, 0.252649992704, 0.021896665916, 0.269146680832, + 0.021896665916, 0.285643339157, 0.021896665916, 0.302139997482, + 0.021896665916, 0.318636655807, 0.021896665916, 0.335133343935, + 0.021896665916, 0.351630002260, 0.021896665916, 0.368126660585, + 0.021896665916, 0.384623318911, 0.021896665916, 0.401120007038, + 0.021896665916, 0.417616665363, 0.021896665916, 0.434113323689, + 0.021896665916, 0.450610011816, 0.021896665916, 0.467106670141, + 0.021896665916, 0.483603328466, 0.021896665916, 0.500100016594, + 0.021896665916, 0.516596674919, 0.021896665916, 0.533093333244, + 0.021896665916, 0.549589991570, 0.021896665916, 0.566086649895, + 0.021896665916, 0.582583308220, 0.021896665916, 0.599080026150, + 0.021896665916, 0.615576684475, 0.021896665916, 0.632073342800, + 0.021896665916, 0.648570001125, 0.021896665916, 0.665066659451, + 0.021896665916, 0.681563317776, 0.021896665916, 0.698059976101, + 0.021896665916, 0.714556694031, 0.021896665916, 0.731053352356, + 0.021896665916, 0.747550010681, 0.021896665916, 0.764046669006, + 0.021896665916, 0.780543327332, 0.021896665916, 0.797039985657, + 0.021896665916, 0.813536643982, 0.021896665916, 0.830033361912, + 0.021896665916, 0.846530020237, 0.021896665916, 0.863026678562, + 0.021896665916, 0.879523336887, 0.021896665916, 0.896019995213, + 0.021896665916, 0.912516653538, 0.021896665916, 0.929013311863, + 0.021896665916, 0.945510029793, 0.021896665916, 0.962006688118, + 0.021896665916, 0.978503346443, 0.021896665916, 0.995000004768, + 0.038393333554, 0.005200000014, 0.038393333554, 0.021696666256, + 0.038393333554, 0.038193333894, 0.038393333554, 0.054689999670, + 0.038393333554, 0.071186669171, 0.038393333554, 0.087683334947, + 0.038393333554, 0.104180000722, 0.038393333554, 0.120676666498, + 0.038393333554, 0.137173339725, 0.038393333554, 0.153669998050, + 0.038393333554, 0.170166671276, 0.038393333554, 0.186663329601, + 0.038393333554, 0.203160002828, 0.038393333554, 0.219656661153, + 0.038393333554, 0.236153334379, 0.038393333554, 0.252649992704, + 0.038393333554, 0.269146680832, 0.038393333554, 0.285643339157, + 0.038393333554, 0.302139997482, 0.038393333554, 0.318636655807, + 0.038393333554, 0.335133343935, 0.038393333554, 0.351630002260, + 0.038393333554, 0.368126660585, 0.038393333554, 0.384623318911, + 0.038393333554, 0.401120007038, 0.038393333554, 0.417616665363, + 0.038393333554, 0.434113323689, 0.038393333554, 0.450610011816, + 0.038393333554, 0.467106670141, 0.038393333554, 0.483603328466, + 0.038393333554, 0.500100016594, 0.038393333554, 0.516596674919, + 0.038393333554, 0.533093333244, 0.038393333554, 0.549589991570, + 0.038393333554, 0.566086649895, 0.038393333554, 0.582583308220, + 0.038393333554, 0.599080026150, 0.038393333554, 0.615576684475, + 0.038393333554, 0.632073342800, 0.038393333554, 0.648570001125, + 0.038393333554, 0.665066659451, 0.038393333554, 0.681563317776, + 0.038393333554, 0.698059976101, 0.038393333554, 0.714556694031, + 0.038393333554, 0.731053352356, 0.038393333554, 0.747550010681, + 0.038393333554, 0.764046669006, 0.038393333554, 0.780543327332, + 0.038393333554, 0.797039985657, 0.038393333554, 0.813536643982, + 0.038393333554, 0.830033361912, 0.038393333554, 0.846530020237, + 0.038393333554, 0.863026678562, 0.038393333554, 0.879523336887, + 0.038393333554, 0.896019995213, 0.038393333554, 0.912516653538, + 0.038393333554, 0.929013311863, 0.038393333554, 0.945510029793, + 0.038393333554, 0.962006688118, 0.038393333554, 0.978503346443, + 0.038393333554, 0.995000004768, 0.054889999330, 0.005200000014, + 0.054889999330, 0.021696666256, 0.054889999330, 0.038193333894, + 0.054889999330, 0.054689999670, 0.054889999330, 0.071186669171, + 0.054889999330, 0.087683334947, 0.054889999330, 0.104180000722, + 0.054889999330, 0.120676666498, 0.054889999330, 0.137173339725, + 0.054889999330, 0.153669998050, 0.054889999330, 0.170166671276, + 0.054889999330, 0.186663329601, 0.054889999330, 0.203160002828, + 0.054889999330, 0.219656661153, 0.054889999330, 0.236153334379, + 0.054889999330, 0.252649992704, 0.054889999330, 0.269146680832, + 0.054889999330, 0.285643339157, 0.054889999330, 0.302139997482, + 0.054889999330, 0.318636655807, 0.054889999330, 0.335133343935, + 0.054889999330, 0.351630002260, 0.054889999330, 0.368126660585, + 0.054889999330, 0.384623318911, 0.054889999330, 0.401120007038, + 0.054889999330, 0.417616665363, 0.054889999330, 0.434113323689, + 0.054889999330, 0.450610011816, 0.054889999330, 0.467106670141, + 0.054889999330, 0.483603328466, 0.054889999330, 0.500100016594, + 0.054889999330, 0.516596674919, 0.054889999330, 0.533093333244, + 0.054889999330, 0.549589991570, 0.054889999330, 0.566086649895, + 0.054889999330, 0.582583308220, 0.054889999330, 0.599080026150, + 0.054889999330, 0.615576684475, 0.054889999330, 0.632073342800, + 0.054889999330, 0.648570001125, 0.054889999330, 0.665066659451, + 0.054889999330, 0.681563317776, 0.054889999330, 0.698059976101, + 0.054889999330, 0.714556694031, 0.054889999330, 0.731053352356, + 0.054889999330, 0.747550010681, 0.054889999330, 0.764046669006, + 0.054889999330, 0.780543327332, 0.054889999330, 0.797039985657, + 0.054889999330, 0.813536643982, 0.054889999330, 0.830033361912, + 0.054889999330, 0.846530020237, 0.054889999330, 0.863026678562, + 0.054889999330, 0.879523336887, 0.054889999330, 0.896019995213, + 0.054889999330, 0.912516653538, 0.054889999330, 0.929013311863, + 0.054889999330, 0.945510029793, 0.054889999330, 0.962006688118, + 0.054889999330, 0.978503346443, 0.054889999330, 0.995000004768, + 0.071386665106, 0.005200000014, 0.071386665106, 0.021696666256, + 0.071386665106, 0.038193333894, 0.071386665106, 0.054689999670, + 0.071386665106, 0.071186669171, 0.071386665106, 0.087683334947, + 0.071386665106, 0.104180000722, 0.071386665106, 0.120676666498, + 0.071386665106, 0.137173339725, 0.071386665106, 0.153669998050, + 0.071386665106, 0.170166671276, 0.071386665106, 0.186663329601, + 0.071386665106, 0.203160002828, 0.071386665106, 0.219656661153, + 0.071386665106, 0.236153334379, 0.071386665106, 0.252649992704, + 0.071386665106, 0.269146680832, 0.071386665106, 0.285643339157, + 0.071386665106, 0.302139997482, 0.071386665106, 0.318636655807, + 0.071386665106, 0.335133343935, 0.071386665106, 0.351630002260, + 0.071386665106, 0.368126660585, 0.071386665106, 0.384623318911, + 0.071386665106, 0.401120007038, 0.071386665106, 0.417616665363, + 0.071386665106, 0.434113323689, 0.071386665106, 0.450610011816, + 0.071386665106, 0.467106670141, 0.071386665106, 0.483603328466, + 0.071386665106, 0.500100016594, 0.071386665106, 0.516596674919, + 0.071386665106, 0.533093333244, 0.071386665106, 0.549589991570, + 0.071386665106, 0.566086649895, 0.071386665106, 0.582583308220, + 0.071386665106, 0.599080026150, 0.071386665106, 0.615576684475, + 0.071386665106, 0.632073342800, 0.071386665106, 0.648570001125, + 0.071386665106, 0.665066659451, 0.071386665106, 0.681563317776, + 0.071386665106, 0.698059976101, 0.071386665106, 0.714556694031, + 0.071386665106, 0.731053352356, 0.071386665106, 0.747550010681, + 0.071386665106, 0.764046669006, 0.071386665106, 0.780543327332, + 0.071386665106, 0.797039985657, 0.071386665106, 0.813536643982, + 0.071386665106, 0.830033361912, 0.071386665106, 0.846530020237, + 0.071386665106, 0.863026678562, 0.071386665106, 0.879523336887, + 0.071386665106, 0.896019995213, 0.071386665106, 0.912516653538, + 0.071386665106, 0.929013311863, 0.071386665106, 0.945510029793, + 0.071386665106, 0.962006688118, 0.071386665106, 0.978503346443, + 0.071386665106, 0.995000004768, 0.087883330882, 0.005200000014, + 0.087883330882, 0.021696666256, 0.087883330882, 0.038193333894, + 0.087883330882, 0.054689999670, 0.087883330882, 0.071186669171, + 0.087883330882, 0.087683334947, 0.087883330882, 0.104180000722, + 0.087883330882, 0.120676666498, 0.087883330882, 0.137173339725, + 0.087883330882, 0.153669998050, 0.087883330882, 0.170166671276, + 0.087883330882, 0.186663329601, 0.087883330882, 0.203160002828, + 0.087883330882, 0.219656661153, 0.087883330882, 0.236153334379, + 0.087883330882, 0.252649992704, 0.087883330882, 0.269146680832, + 0.087883330882, 0.285643339157, 0.087883330882, 0.302139997482, + 0.087883330882, 0.318636655807, 0.087883330882, 0.335133343935, + 0.087883330882, 0.351630002260, 0.087883330882, 0.368126660585, + 0.087883330882, 0.384623318911, 0.087883330882, 0.401120007038, + 0.087883330882, 0.417616665363, 0.087883330882, 0.434113323689, + 0.087883330882, 0.450610011816, 0.087883330882, 0.467106670141, + 0.087883330882, 0.483603328466, 0.087883330882, 0.500100016594, + 0.087883330882, 0.516596674919, 0.087883330882, 0.533093333244, + 0.087883330882, 0.549589991570, 0.087883330882, 0.566086649895, + 0.087883330882, 0.582583308220, 0.087883330882, 0.599080026150, + 0.087883330882, 0.615576684475, 0.087883330882, 0.632073342800, + 0.087883330882, 0.648570001125, 0.087883330882, 0.665066659451, + 0.087883330882, 0.681563317776, 0.087883330882, 0.698059976101, + 0.087883330882, 0.714556694031, 0.087883330882, 0.731053352356, + 0.087883330882, 0.747550010681, 0.087883330882, 0.764046669006, + 0.087883330882, 0.780543327332, 0.087883330882, 0.797039985657, + 0.087883330882, 0.813536643982, 0.087883330882, 0.830033361912, + 0.087883330882, 0.846530020237, 0.087883330882, 0.863026678562, + 0.087883330882, 0.879523336887, 0.087883330882, 0.896019995213, + 0.087883330882, 0.912516653538, 0.087883330882, 0.929013311863, + 0.087883330882, 0.945510029793, 0.087883330882, 0.962006688118, + 0.087883330882, 0.978503346443, 0.087883330882, 0.995000004768, + 0.104379996657, 0.005200000014, 0.104379996657, 0.021696666256, + 0.104379996657, 0.038193333894, 0.104379996657, 0.054689999670, + 0.104379996657, 0.071186669171, 0.104379996657, 0.087683334947, + 0.104379996657, 0.104180000722, 0.104379996657, 0.120676666498, + 0.104379996657, 0.137173339725, 0.104379996657, 0.153669998050, + 0.104379996657, 0.170166671276, 0.104379996657, 0.186663329601, + 0.104379996657, 0.203160002828, 0.104379996657, 0.219656661153, + 0.104379996657, 0.236153334379, 0.104379996657, 0.252649992704, + 0.104379996657, 0.269146680832, 0.104379996657, 0.285643339157, + 0.104379996657, 0.302139997482, 0.104379996657, 0.318636655807, + 0.104379996657, 0.335133343935, 0.104379996657, 0.351630002260, + 0.104379996657, 0.368126660585, 0.104379996657, 0.384623318911, + 0.104379996657, 0.401120007038, 0.104379996657, 0.417616665363, + 0.104379996657, 0.434113323689, 0.104379996657, 0.450610011816, + 0.104379996657, 0.467106670141, 0.104379996657, 0.483603328466, + 0.104379996657, 0.500100016594, 0.104379996657, 0.516596674919, + 0.104379996657, 0.533093333244, 0.104379996657, 0.549589991570, + 0.104379996657, 0.566086649895, 0.104379996657, 0.582583308220, + 0.104379996657, 0.599080026150, 0.104379996657, 0.615576684475, + 0.104379996657, 0.632073342800, 0.104379996657, 0.648570001125, + 0.104379996657, 0.665066659451, 0.104379996657, 0.681563317776, + 0.104379996657, 0.698059976101, 0.104379996657, 0.714556694031, + 0.104379996657, 0.731053352356, 0.104379996657, 0.747550010681, + 0.104379996657, 0.764046669006, 0.104379996657, 0.780543327332, + 0.104379996657, 0.797039985657, 0.104379996657, 0.813536643982, + 0.104379996657, 0.830033361912, 0.104379996657, 0.846530020237, + 0.104379996657, 0.863026678562, 0.104379996657, 0.879523336887, + 0.104379996657, 0.896019995213, 0.104379996657, 0.912516653538, + 0.104379996657, 0.929013311863, 0.104379996657, 0.945510029793, + 0.104379996657, 0.962006688118, 0.104379996657, 0.978503346443, + 0.104379996657, 0.995000004768, 0.120876669884, 0.005200000014, + 0.120876669884, 0.021696666256, 0.120876669884, 0.038193333894, + 0.120876669884, 0.054689999670, 0.120876669884, 0.071186669171, + 0.120876669884, 0.087683334947, 0.120876669884, 0.104180000722, + 0.120876669884, 0.120676666498, 0.120876669884, 0.137173339725, + 0.120876669884, 0.153669998050, 0.120876669884, 0.170166671276, + 0.120876669884, 0.186663329601, 0.120876669884, 0.203160002828, + 0.120876669884, 0.219656661153, 0.120876669884, 0.236153334379, + 0.120876669884, 0.252649992704, 0.120876669884, 0.269146680832, + 0.120876669884, 0.285643339157, 0.120876669884, 0.302139997482, + 0.120876669884, 0.318636655807, 0.120876669884, 0.335133343935, + 0.120876669884, 0.351630002260, 0.120876669884, 0.368126660585, + 0.120876669884, 0.384623318911, 0.120876669884, 0.401120007038, + 0.120876669884, 0.417616665363, 0.120876669884, 0.434113323689, + 0.120876669884, 0.450610011816, 0.120876669884, 0.467106670141, + 0.120876669884, 0.483603328466, 0.120876669884, 0.500100016594, + 0.120876669884, 0.516596674919, 0.120876669884, 0.533093333244, + 0.120876669884, 0.549589991570, 0.120876669884, 0.566086649895, + 0.120876669884, 0.582583308220, 0.120876669884, 0.599080026150, + 0.120876669884, 0.615576684475, 0.120876669884, 0.632073342800, + 0.120876669884, 0.648570001125, 0.120876669884, 0.665066659451, + 0.120876669884, 0.681563317776, 0.120876669884, 0.698059976101, + 0.120876669884, 0.714556694031, 0.120876669884, 0.731053352356, + 0.120876669884, 0.747550010681, 0.120876669884, 0.764046669006, + 0.120876669884, 0.780543327332, 0.120876669884, 0.797039985657, + 0.120876669884, 0.813536643982, 0.120876669884, 0.830033361912, + 0.120876669884, 0.846530020237, 0.120876669884, 0.863026678562, + 0.120876669884, 0.879523336887, 0.120876669884, 0.896019995213, + 0.120876669884, 0.912516653538, 0.120876669884, 0.929013311863, + 0.120876669884, 0.945510029793, 0.120876669884, 0.962006688118, + 0.120876669884, 0.978503346443, 0.120876669884, 0.995000004768, + 0.137373328209, 0.005200000014, 0.137373328209, 0.021696666256, + 0.137373328209, 0.038193333894, 0.137373328209, 0.054689999670, + 0.137373328209, 0.071186669171, 0.137373328209, 0.087683334947, + 0.137373328209, 0.104180000722, 0.137373328209, 0.120676666498, + 0.137373328209, 0.137173339725, 0.137373328209, 0.153669998050, + 0.137373328209, 0.170166671276, 0.137373328209, 0.186663329601, + 0.137373328209, 0.203160002828, 0.137373328209, 0.219656661153, + 0.137373328209, 0.236153334379, 0.137373328209, 0.252649992704, + 0.137373328209, 0.269146680832, 0.137373328209, 0.285643339157, + 0.137373328209, 0.302139997482, 0.137373328209, 0.318636655807, + 0.137373328209, 0.335133343935, 0.137373328209, 0.351630002260, + 0.137373328209, 0.368126660585, 0.137373328209, 0.384623318911, + 0.137373328209, 0.401120007038, 0.137373328209, 0.417616665363, + 0.137373328209, 0.434113323689, 0.137373328209, 0.450610011816, + 0.137373328209, 0.467106670141, 0.137373328209, 0.483603328466, + 0.137373328209, 0.500100016594, 0.137373328209, 0.516596674919, + 0.137373328209, 0.533093333244, 0.137373328209, 0.549589991570, + 0.137373328209, 0.566086649895, 0.137373328209, 0.582583308220, + 0.137373328209, 0.599080026150, 0.137373328209, 0.615576684475, + 0.137373328209, 0.632073342800, 0.137373328209, 0.648570001125, + 0.137373328209, 0.665066659451, 0.137373328209, 0.681563317776, + 0.137373328209, 0.698059976101, 0.137373328209, 0.714556694031, + 0.137373328209, 0.731053352356, 0.137373328209, 0.747550010681, + 0.137373328209, 0.764046669006, 0.137373328209, 0.780543327332, + 0.137373328209, 0.797039985657, 0.137373328209, 0.813536643982, + 0.137373328209, 0.830033361912, 0.137373328209, 0.846530020237, + 0.137373328209, 0.863026678562, 0.137373328209, 0.879523336887, + 0.137373328209, 0.896019995213, 0.137373328209, 0.912516653538, + 0.137373328209, 0.929013311863, 0.137373328209, 0.945510029793, + 0.137373328209, 0.962006688118, 0.137373328209, 0.978503346443, + 0.137373328209, 0.995000004768, 0.153870001435, 0.005200000014, + 0.153870001435, 0.021696666256, 0.153870001435, 0.038193333894, + 0.153870001435, 0.054689999670, 0.153870001435, 0.071186669171, + 0.153870001435, 0.087683334947, 0.153870001435, 0.104180000722, + 0.153870001435, 0.120676666498, 0.153870001435, 0.137173339725, + 0.153870001435, 0.153669998050, 0.153870001435, 0.170166671276, + 0.153870001435, 0.186663329601, 0.153870001435, 0.203160002828, + 0.153870001435, 0.219656661153, 0.153870001435, 0.236153334379, + 0.153870001435, 0.252649992704, 0.153870001435, 0.269146680832, + 0.153870001435, 0.285643339157, 0.153870001435, 0.302139997482, + 0.153870001435, 0.318636655807, 0.153870001435, 0.335133343935, + 0.153870001435, 0.351630002260, 0.153870001435, 0.368126660585, + 0.153870001435, 0.384623318911, 0.153870001435, 0.401120007038, + 0.153870001435, 0.417616665363, 0.153870001435, 0.434113323689, + 0.153870001435, 0.450610011816, 0.153870001435, 0.467106670141, + 0.153759777546, 0.483598083258, 0.153758004308, 0.500100016594, + 0.153759777546, 0.516601920128, 0.153870001435, 0.533093333244, + 0.153870001435, 0.549589991570, 0.153870001435, 0.566086649895, + 0.153870001435, 0.582583308220, 0.153870001435, 0.599080026150, + 0.153870001435, 0.615576684475, 0.153870001435, 0.632073342800, + 0.153870001435, 0.648570001125, 0.153870001435, 0.665066659451, + 0.153870001435, 0.681563317776, 0.153870001435, 0.698059976101, + 0.153870001435, 0.714556694031, 0.153870001435, 0.731053352356, + 0.153870001435, 0.747550010681, 0.153870001435, 0.764046669006, + 0.153870001435, 0.780543327332, 0.153870001435, 0.797039985657, + 0.153870001435, 0.813536643982, 0.153870001435, 0.830033361912, + 0.153870001435, 0.846530020237, 0.153870001435, 0.863026678562, + 0.153870001435, 0.879523336887, 0.153870001435, 0.896019995213, + 0.153870001435, 0.912516653538, 0.153870001435, 0.929013311863, + 0.153870001435, 0.945510029793, 0.153870001435, 0.962006688118, + 0.153870001435, 0.978503346443, 0.153870001435, 0.995000004768, + 0.170366659760, 0.005200000014, 0.170366659760, 0.021696666256, + 0.170366659760, 0.038193333894, 0.170366659760, 0.054689999670, + 0.170366659760, 0.071186669171, 0.170366659760, 0.087683334947, + 0.170366659760, 0.104180000722, 0.170366659760, 0.120676666498, + 0.170366659760, 0.137173339725, 0.170366659760, 0.153669998050, + 0.170366659760, 0.170166671276, 0.170366659760, 0.186663329601, + 0.170366659760, 0.203160002828, 0.170366659760, 0.219656661153, + 0.170366659760, 0.236153334379, 0.170366659760, 0.252649992704, + 0.170366659760, 0.269146680832, 0.170366659760, 0.285643339157, + 0.170366659760, 0.302139997482, 0.170366659760, 0.318636655807, + 0.170366659760, 0.335133343935, 0.170366659760, 0.351630002260, + 0.170366659760, 0.368126660585, 0.170366659760, 0.384623318911, + 0.170251503587, 0.401085466146, 0.170232653618, 0.417583167553, + 0.170217052102, 0.434083402157, 0.170204803348, 0.450585722923, + 0.170195981860, 0.467089593410, 0.170190662146, 0.483594536781, + 0.170188888907, 0.500100016594, 0.170190662146, 0.516605496407, + 0.170195981860, 0.533110380173, 0.170204803348, 0.549614250660, + 0.170217052102, 0.566116571426, 0.170232653618, 0.582616806030, + 0.170251503587, 0.599114537239, 0.170366659760, 0.615576684475, + 0.170366659760, 0.632073342800, 0.170366659760, 0.648570001125, + 0.170366659760, 0.665066659451, 0.170366659760, 0.681563317776, + 0.170366659760, 0.698059976101, 0.170366659760, 0.714556694031, + 0.170366659760, 0.731053352356, 0.170366659760, 0.747550010681, + 0.170366659760, 0.764046669006, 0.170366659760, 0.780543327332, + 0.170366659760, 0.797039985657, 0.170366659760, 0.813536643982, + 0.170366659760, 0.830033361912, 0.170366659760, 0.846530020237, + 0.170366659760, 0.863026678562, 0.170366659760, 0.879523336887, + 0.170366659760, 0.896019995213, 0.170366659760, 0.912516653538, + 0.170366659760, 0.929013311863, 0.170366659760, 0.945510029793, + 0.170366659760, 0.962006688118, 0.170366659760, 0.978503346443, + 0.170366659760, 0.995000004768, 0.186863332987, 0.005200000014, + 0.186863332987, 0.021696666256, 0.186863332987, 0.038193333894, + 0.186863332987, 0.054689999670, 0.186863332987, 0.071186669171, + 0.186863332987, 0.087683334947, 0.186863332987, 0.104180000722, + 0.186863332987, 0.120676666498, 0.186863332987, 0.137173339725, + 0.186863332987, 0.153669998050, 0.186863332987, 0.170166671276, + 0.186863332987, 0.186663329601, 0.186863332987, 0.203160002828, + 0.186863332987, 0.219656661153, 0.186863332987, 0.236153334379, + 0.186863332987, 0.252649992704, 0.186863332987, 0.269146680832, + 0.186863332987, 0.285643339157, 0.186863332987, 0.302139997482, + 0.186863332987, 0.318636655807, 0.186863332987, 0.335133343935, + 0.186763614416, 0.351582765579, 0.186736032367, 0.368073076010, + 0.186711221933, 0.384567290545, 0.186689361930, 0.401065051556, + 0.186703175306, 0.417574524879, 0.186692789197, 0.434077441692, + 0.186684638262, 0.450581789017, 0.186678767204, 0.467087239027, + 0.186675220728, 0.483593434095, 0.186674043536, 0.500100016594, + 0.186675220728, 0.516606569290, 0.186678767204, 0.533112764359, + 0.186684638262, 0.549618244171, 0.186692789197, 0.566122591496, + 0.186703175306, 0.582625508308, 0.186689361930, 0.599134922028, + 0.186711221933, 0.615632712841, 0.186736032367, 0.632126927376, + 0.186763614416, 0.648617267609, 0.186863332987, 0.665066659451, + 0.186863332987, 0.681563317776, 0.186863332987, 0.698059976101, + 0.186863332987, 0.714556694031, 0.186863332987, 0.731053352356, + 0.186863332987, 0.747550010681, 0.186863332987, 0.764046669006, + 0.186863332987, 0.780543327332, 0.186863332987, 0.797039985657, + 0.186863332987, 0.813536643982, 0.186863332987, 0.830033361912, + 0.186863332987, 0.846530020237, 0.186863332987, 0.863026678562, + 0.186863332987, 0.879523336887, 0.186863332987, 0.896019995213, + 0.186863332987, 0.912516653538, 0.186863332987, 0.929013311863, + 0.186863332987, 0.945510029793, 0.186863332987, 0.962006688118, + 0.186863332987, 0.978503346443, 0.186863332987, 0.995000004768, + 0.203360006213, 0.005200000014, 0.203360006213, 0.021696666256, + 0.203360006213, 0.038193333894, 0.203360006213, 0.054689999670, + 0.203360006213, 0.071186669171, 0.203360006213, 0.087683334947, + 0.203360006213, 0.104180000722, 0.203360006213, 0.120676666498, + 0.203360006213, 0.137173339725, 0.203360006213, 0.153669998050, + 0.203360006213, 0.170166671276, 0.203360006213, 0.186663329601, + 0.203360006213, 0.203160002828, 0.203360006213, 0.219656661153, + 0.203360006213, 0.236153334379, 0.203360006213, 0.252649992704, + 0.203360006213, 0.269146680832, 0.203360006213, 0.285643339157, + 0.203360006213, 0.302139997482, 0.203360006213, 0.318636655807, + 0.203237846494, 0.335065454245, 0.203207969666, 0.351553976536, + 0.203180655837, 0.368046969175, 0.203194037080, 0.384558796883, + 0.203179538250, 0.401059836149, 0.203167080879, 0.417563080788, + 0.203156739473, 0.434068173170, 0.203056484461, 0.450559407473, + 0.203041821718, 0.467071324587, 0.203032955527, 0.483585178852, + 0.203030005097, 0.500100016594, 0.203032955527, 0.516614854336, + 0.203041821718, 0.533128678799, 0.203056484461, 0.549640595913, + 0.203156739473, 0.566131830215, 0.203167080879, 0.582636892796, + 0.203179538250, 0.599140167236, 0.203194037080, 0.615641236305, + 0.203180655837, 0.632153034210, 0.203207969666, 0.648645997047, + 0.203237846494, 0.665134549141, 0.203360006213, 0.681563317776, + 0.203360006213, 0.698059976101, 0.203360006213, 0.714556694031, + 0.203360006213, 0.731053352356, 0.203360006213, 0.747550010681, + 0.203360006213, 0.764046669006, 0.203360006213, 0.780543327332, + 0.203360006213, 0.797039985657, 0.203360006213, 0.813536643982, + 0.203360006213, 0.830033361912, 0.203360006213, 0.846530020237, + 0.203360006213, 0.863026678562, 0.203360006213, 0.879523336887, + 0.203360006213, 0.896019995213, 0.203360006213, 0.912516653538, + 0.203360006213, 0.929013311863, 0.203360006213, 0.945510029793, + 0.203360006213, 0.962006688118, 0.203360006213, 0.978503346443, + 0.203360006213, 0.995000004768, 0.219856664538, 0.005200000014, + 0.219856664538, 0.021696666256, 0.219856664538, 0.038193333894, + 0.219856664538, 0.054689999670, 0.219856664538, 0.071186669171, + 0.219856664538, 0.087683334947, 0.219856664538, 0.104180000722, + 0.219856664538, 0.120676666498, 0.219856664538, 0.137173339725, + 0.219856664538, 0.153669998050, 0.219856664538, 0.170166671276, + 0.219856664538, 0.186663329601, 0.219856664538, 0.203160002828, + 0.219856664538, 0.219656661153, 0.219856664538, 0.236153334379, + 0.219856664538, 0.252649992704, 0.219856664538, 0.269146680832, + 0.219856664538, 0.285643339157, 0.219754427671, 0.302067846060, + 0.219720572233, 0.318548619747, 0.219688817859, 0.335034608841, + 0.219696775079, 0.351545363665, 0.219678759575, 0.368042945862, + 0.219662502408, 0.384543389082, 0.219547793269, 0.401010990143, + 0.219516798854, 0.417516708374, 0.219491034746, 0.434027314186, + 0.219470724463, 0.450541883707, 0.219456076622, 0.467059522867, + 0.219447225332, 0.483579248190, 0.219444260001, 0.500100016594, + 0.219447225332, 0.516620755196, 0.219456076622, 0.533140480518, + 0.219470724463, 0.549658119678, 0.219491034746, 0.566172719002, + 0.219516798854, 0.582683265209, 0.219547793269, 0.599188983440, + 0.219662502408, 0.615656614304, 0.219678759575, 0.632157027721, + 0.219696775079, 0.648654639721, 0.219688817859, 0.665165424347, + 0.219720572233, 0.681651413441, 0.219754427671, 0.698132157326, + 0.219856664538, 0.714556694031, 0.219856664538, 0.731053352356, + 0.219856664538, 0.747550010681, 0.219856664538, 0.764046669006, + 0.219856664538, 0.780543327332, 0.219856664538, 0.797039985657, + 0.219856664538, 0.813536643982, 0.219856664538, 0.830033361912, + 0.219856664538, 0.846530020237, 0.219856664538, 0.863026678562, + 0.219856664538, 0.879523336887, 0.219856664538, 0.896019995213, + 0.219856664538, 0.912516653538, 0.219856664538, 0.929013311863, + 0.219856664538, 0.945510029793, 0.219856664538, 0.962006688118, + 0.219856664538, 0.978503346443, 0.219856664538, 0.995000004768, + 0.236353337765, 0.005200000014, 0.236353337765, 0.021696666256, + 0.236353337765, 0.038193333894, 0.236353337765, 0.054689999670, + 0.236353337765, 0.071186669171, 0.236353337765, 0.087683334947, + 0.236353337765, 0.104180000722, 0.236353337765, 0.120676666498, + 0.236353337765, 0.137173339725, 0.236353337765, 0.153669998050, + 0.236353337765, 0.170166671276, 0.236353337765, 0.186663329601, + 0.236353337765, 0.203160002828, 0.236353337765, 0.219656661153, + 0.236353337765, 0.236153334379, 0.236353337765, 0.252649992704, + 0.236353337765, 0.269146680832, 0.236246123910, 0.285556226969, + 0.236211106181, 0.302033334970, 0.236209720373, 0.318537920713, + 0.236188918352, 0.335030585527, 0.236169561744, 0.351526618004, + 0.236049428582, 0.367974728346, 0.236009195447, 0.384472787380, + 0.235973536968, 0.400977581739, 0.235827565193, 0.417452365160, + 0.235781371593, 0.433970332146, 0.235744923353, 0.450495928526, + 0.235718578100, 0.467027336359, 0.235702663660, 0.483562678099, + 0.235697329044, 0.500100016594, 0.235702663660, 0.516637325287, + 0.235718578100, 0.533172667027, 0.235744923353, 0.549704074860, + 0.235781371593, 0.566229641438, 0.235827565193, 0.582747638226, + 0.235973536968, 0.599222421646, 0.236009195447, 0.615727245808, + 0.236049428582, 0.632225275040, 0.236169561744, 0.648673355579, + 0.236188918352, 0.665169417858, 0.236209720373, 0.681662082672, + 0.236211106181, 0.698166668415, 0.236246123910, 0.714643776417, + 0.236353337765, 0.731053352356, 0.236353337765, 0.747550010681, + 0.236353337765, 0.764046669006, 0.236353337765, 0.780543327332, + 0.236353337765, 0.797039985657, 0.236353337765, 0.813536643982, + 0.236353337765, 0.830033361912, 0.236353337765, 0.846530020237, + 0.236353337765, 0.863026678562, 0.236353337765, 0.879523336887, + 0.236353337765, 0.896019995213, 0.236353337765, 0.912516653538, + 0.236353337765, 0.929013311863, 0.236353337765, 0.945510029793, + 0.236353337765, 0.962006688118, 0.236353337765, 0.978503346443, + 0.236353337765, 0.995000004768, 0.252849996090, 0.005200000014, + 0.252849996090, 0.021696666256, 0.252849996090, 0.038193333894, + 0.252849996090, 0.054689999670, 0.252849996090, 0.071186669171, + 0.252849996090, 0.087683334947, 0.252849996090, 0.104180000722, + 0.252849996090, 0.120676666498, 0.252849996090, 0.137173339725, + 0.252849996090, 0.153669998050, 0.252849996090, 0.170166671276, + 0.252849996090, 0.186663329601, 0.252849996090, 0.203160002828, + 0.252849996090, 0.219656661153, 0.252849996090, 0.236153334379, + 0.252849996090, 0.252649992704, 0.252744317055, 0.269048035145, + 0.252708643675, 0.285520821810, 0.252707988024, 0.302026391029, + 0.252686381340, 0.318516671658, 0.252577453852, 0.334951639175, + 0.252529919147, 0.351437956095, 0.252486109734, 0.367932587862, + 0.252323478460, 0.384377628565, 0.252259880304, 0.400883942842, + 0.252204835415, 0.417401611805, 0.252158880234, 0.433929026127, + 0.252403050661, 0.450520604849, 0.252405971289, 0.467047452927, + 0.252407729626, 0.483573853970, 0.252408325672, 0.500100016594, + 0.252407729626, 0.516626179218, 0.252405971289, 0.533152520657, + 0.252403050661, 0.549679398537, 0.252158880234, 0.566270947456, + 0.252204835415, 0.582798421383, 0.252259880304, 0.599316060543, + 0.252323478460, 0.615822374821, 0.252486109734, 0.632267415524, + 0.252529919147, 0.648762047291, 0.252577453852, 0.665248334408, + 0.252686381340, 0.681683301926, 0.252707988024, 0.698173582554, + 0.252708643675, 0.714679181576, 0.252744317055, 0.731151998043, + 0.252849996090, 0.747550010681, 0.252849996090, 0.764046669006, + 0.252849996090, 0.780543327332, 0.252849996090, 0.797039985657, + 0.252849996090, 0.813536643982, 0.252849996090, 0.830033361912, + 0.252849996090, 0.846530020237, 0.252849996090, 0.863026678562, + 0.252849996090, 0.879523336887, 0.252849996090, 0.896019995213, + 0.252849996090, 0.912516653538, 0.252849996090, 0.929013311863, + 0.252849996090, 0.945510029793, 0.252849996090, 0.962006688118, + 0.252849996090, 0.978503346443, 0.252849996090, 0.995000004768, + 0.269346654415, 0.005200000014, 0.269346654415, 0.021696666256, + 0.269346654415, 0.038193333894, 0.269346654415, 0.054689999670, + 0.269346654415, 0.071186669171, 0.269346654415, 0.087683334947, + 0.269346654415, 0.104180000722, 0.269346654415, 0.120676666498, + 0.269346654415, 0.137173339725, 0.269346654415, 0.153669998050, + 0.269346654415, 0.170166671276, 0.269346654415, 0.186663329601, + 0.269346654415, 0.203160002828, 0.269346654415, 0.219656661153, + 0.269346654415, 0.236153334379, 0.269248008728, 0.252544313669, + 0.269212216139, 0.269012212753, 0.269210666418, 0.285517036915, + 0.269188582897, 0.302004486322, 0.269073784351, 0.318422257900, + 0.269024014473, 0.334902882576, 0.268868744373, 0.351322770119, + 0.268791258335, 0.367809295654, 0.268720775843, 0.384310394526, + 0.268930613995, 0.400941699743, 0.268936663866, 0.417470246553, + 0.268941730261, 0.433997631073, 0.268945753574, 0.450524091721, + 0.268948674202, 0.467049807310, 0.268950432539, 0.483575046062, + 0.268951028585, 0.500100016594, 0.268950432539, 0.516624987125, + 0.268948674202, 0.533150196075, 0.268945753574, 0.549675881863, + 0.268941730261, 0.566202342510, 0.268936663866, 0.582729756832, + 0.268930613995, 0.599258303642, 0.268720775843, 0.615889608860, + 0.268791258335, 0.632390737534, 0.268868744373, 0.648877263069, + 0.269024014473, 0.665297150612, 0.269073784351, 0.681777715683, + 0.269188582897, 0.698195517063, 0.269210666418, 0.714682936668, + 0.269212216139, 0.731187760830, 0.269248008728, 0.747655689716, + 0.269346654415, 0.764046669006, 0.269346654415, 0.780543327332, + 0.269346654415, 0.797039985657, 0.269346654415, 0.813536643982, + 0.269346654415, 0.830033361912, 0.269346654415, 0.846530020237, + 0.269346654415, 0.863026678562, 0.269346654415, 0.879523336887, + 0.269346654415, 0.896019995213, 0.269346654415, 0.912516653538, + 0.269346654415, 0.929013311863, 0.269346654415, 0.945510029793, + 0.269346654415, 0.962006688118, 0.269346654415, 0.978503346443, + 0.269346654415, 0.995000004768, 0.285843342543, 0.005200000014, + 0.285843342543, 0.021696666256, 0.285843342543, 0.038193333894, + 0.285843342543, 0.054689999670, 0.285843342543, 0.071186669171, + 0.285843342543, 0.087683334947, 0.285843342543, 0.104180000722, + 0.285843342543, 0.120676666498, 0.285843342543, 0.137173339725, + 0.285843342543, 0.153669998050, 0.285843342543, 0.170166671276, + 0.285843342543, 0.186663329601, 0.285843342543, 0.203160002828, + 0.285843342543, 0.219656661153, 0.285756230354, 0.236046120524, + 0.285720825195, 0.252508640289, 0.285717040300, 0.269010663033, + 0.285694867373, 0.285494863987, 0.285581260920, 0.301898092031, + 0.285530239344, 0.318371742964, 0.285365968943, 0.334766119719, + 0.285284191370, 0.351242899895, 0.285456478596, 0.367888599634, + 0.285464167595, 0.384419173002, 0.285471051931, 0.400948196650, + 0.285477072001, 0.417475789785, 0.285482108593, 0.434002190828, + 0.285486102104, 0.450527548790, 0.285489022732, 0.467052161694, + 0.285490781069, 0.483576208353, 0.285491377115, 0.500100016594, + 0.285490781069, 0.516623795033, 0.285489022732, 0.533147871494, + 0.285486102104, 0.549672424793, 0.285482108593, 0.566197812557, + 0.285477072001, 0.582724213600, 0.285471051931, 0.599251806736, + 0.285464167595, 0.615780830383, 0.285456478596, 0.632311403751, + 0.285284191370, 0.648957133293, 0.285365968943, 0.665433883667, + 0.285530239344, 0.681828260422, 0.285581260920, 0.698301911354, + 0.285694867373, 0.714705169201, 0.285717040300, 0.731189310551, + 0.285720825195, 0.747691392899, 0.285756230354, 0.764153897762, + 0.285843342543, 0.780543327332, 0.285843342543, 0.797039985657, + 0.285843342543, 0.813536643982, 0.285843342543, 0.830033361912, + 0.285843342543, 0.846530020237, 0.285843342543, 0.863026678562, + 0.285843342543, 0.879523336887, 0.285843342543, 0.896019995213, + 0.285843342543, 0.912516653538, 0.285843342543, 0.929013311863, + 0.285843342543, 0.945510029793, 0.285843342543, 0.962006688118, + 0.285843342543, 0.978503346443, 0.285843342543, 0.995000004768, + 0.302340000868, 0.005200000014, 0.302340000868, 0.021696666256, + 0.302340000868, 0.038193333894, 0.302340000868, 0.054689999670, + 0.302340000868, 0.071186669171, 0.302340000868, 0.087683334947, + 0.302340000868, 0.104180000722, 0.302340000868, 0.120676666498, + 0.302340000868, 0.137173339725, 0.302340000868, 0.153669998050, + 0.302340000868, 0.170166671276, 0.302340000868, 0.186663329601, + 0.302340000868, 0.203160002828, 0.302267849445, 0.219554439187, + 0.302233338356, 0.236011117697, 0.302226394415, 0.252508014441, + 0.302204489708, 0.268988579512, 0.302098095417, 0.285381257534, + 0.302046805620, 0.301846802235, 0.301883697510, 0.318218380213, + 0.301799416542, 0.334682852030, 0.301986664534, 0.351365000010, + 0.301994889975, 0.367896586657, 0.302002429962, 0.384426414967, + 0.302009195089, 0.400954604149, 0.302015125751, 0.417481303215, + 0.301899492741, 0.433966487646, 0.301879584789, 0.450494885445, + 0.301865100861, 0.467027515173, 0.301856279373, 0.483563035727, + 0.301853328943, 0.500100016594, 0.301856279373, 0.516636967659, + 0.301865100861, 0.533172488213, 0.301879584789, 0.549705088139, + 0.301899492741, 0.566233515739, 0.302015125751, 0.582718729973, + 0.302009195089, 0.599245429039, 0.302002429962, 0.615773618221, + 0.301994889975, 0.632303416729, 0.301986664534, 0.648835003376, + 0.301799416542, 0.665517151356, 0.301883697510, 0.681981623173, + 0.302046805620, 0.698353230953, 0.302098095417, 0.714818716049, + 0.302204489708, 0.731211423874, 0.302226394415, 0.747691988945, + 0.302233338356, 0.764188885689, 0.302267849445, 0.780645549297, + 0.302340000868, 0.797039985657, 0.302340000868, 0.813536643982, + 0.302340000868, 0.830033361912, 0.302340000868, 0.846530020237, + 0.302340000868, 0.863026678562, 0.302340000868, 0.879523336887, + 0.302340000868, 0.896019995213, 0.302340000868, 0.912516653538, + 0.302340000868, 0.929013311863, 0.302340000868, 0.945510029793, + 0.302340000868, 0.962006688118, 0.302340000868, 0.978503346443, + 0.302340000868, 0.995000004768, 0.318836659193, 0.005200000014, + 0.318836659193, 0.021696666256, 0.318836659193, 0.038193333894, + 0.318836659193, 0.054689999670, 0.318836659193, 0.071186669171, + 0.318836659193, 0.087683334947, 0.318836659193, 0.104180000722, + 0.318836659193, 0.120676666498, 0.318836659193, 0.137173339725, + 0.318836659193, 0.153669998050, 0.318836659193, 0.170166671276, + 0.318836659193, 0.186663329601, 0.318836659193, 0.203160002828, + 0.318748593330, 0.219520568848, 0.318737924099, 0.236009716988, + 0.318716675043, 0.252486377954, 0.318622261286, 0.268873780966, + 0.318571716547, 0.285330235958, 0.318418383598, 0.301683694124, + 0.318333625793, 0.318133622408, 0.318514525890, 0.334840476513, + 0.318523049355, 0.351373404264, 0.318531006575, 0.367904365063, + 0.318538337946, 0.384433507919, 0.318425089121, 0.400895506144, + 0.318395972252, 0.417416363955, 0.318371295929, 0.433944106102, + 0.318351566792, 0.450477689505, 0.318356364965, 0.467019349337, + 0.318349331617, 0.483559042215, 0.318346977234, 0.500100016594, + 0.318349331617, 0.516640961170, 0.318356364965, 0.533180654049, + 0.318351566792, 0.549722313881, 0.318371295929, 0.566255867481, + 0.318395972252, 0.582783639431, 0.318425089121, 0.599304497242, + 0.318538337946, 0.615766525269, 0.318531006575, 0.632295608521, + 0.318523049355, 0.648826599121, 0.318514525890, 0.665359497070, + 0.318333625793, 0.682066380978, 0.318418383598, 0.698516309261, + 0.318571716547, 0.714869797230, 0.318622261286, 0.731326222420, + 0.318716675043, 0.747713625431, 0.318737924099, 0.764190256596, + 0.318748593330, 0.780679404736, 0.318836659193, 0.797039985657, + 0.318836659193, 0.813536643982, 0.318836659193, 0.830033361912, + 0.318836659193, 0.846530020237, 0.318836659193, 0.863026678562, + 0.318836659193, 0.879523336887, 0.318836659193, 0.896019995213, + 0.318836659193, 0.912516653538, 0.318836659193, 0.929013311863, + 0.318836659193, 0.945510029793, 0.318836659193, 0.962006688118, + 0.318836659193, 0.978503346443, 0.318836659193, 0.995000004768, + 0.335333347321, 0.005200000014, 0.335333347321, 0.021696666256, + 0.335333347321, 0.038193333894, 0.335333347321, 0.054689999670, + 0.335333347321, 0.071186669171, 0.335333347321, 0.087683334947, + 0.335333347321, 0.104180000722, 0.335333347321, 0.120676666498, + 0.335333347321, 0.137173339725, 0.335333347321, 0.153669998050, + 0.335333347321, 0.170166671276, 0.335333347321, 0.186663329601, + 0.335265457630, 0.203037843108, 0.335234612226, 0.219488814473, + 0.335230559111, 0.235988914967, 0.335151642561, 0.252377480268, + 0.335102856159, 0.268824011087, 0.334966123104, 0.285165965557, + 0.334882855415, 0.301599413157, 0.335040479898, 0.318314522505, + 0.335049062967, 0.334849059582, 0.335057228804, 0.351381480694, + 0.335064888000, 0.367911905050, 0.334940016270, 0.384348005056, + 0.334907740355, 0.400864630938, 0.334896683693, 0.417398363352, + 0.334877252579, 0.433930903673, 0.334861606359, 0.450468480587, + 0.334850132465, 0.467010021210, 0.334843099117, 0.483554303646, + 0.334840744734, 0.500100016594, 0.334843099117, 0.516645669937, + 0.334850132465, 0.533189952374, 0.334861606359, 0.549731492996, + 0.334877252579, 0.566269099712, 0.334896683693, 0.582801640034, + 0.334907740355, 0.599335372448, 0.334940016270, 0.615851998329, + 0.335064888000, 0.632288098335, 0.335057228804, 0.648818492889, + 0.335049062967, 0.665350973606, 0.335040479898, 0.681885480881, + 0.334882855415, 0.698600590229, 0.334966123104, 0.715034008026, + 0.335102856159, 0.731375992298, 0.335151642561, 0.747822523117, + 0.335230559111, 0.764211058617, 0.335234612226, 0.780711174011, + 0.335265457630, 0.797162175179, 0.335333347321, 0.813536643982, + 0.335333347321, 0.830033361912, 0.335333347321, 0.846530020237, + 0.335333347321, 0.863026678562, 0.335333347321, 0.879523336887, + 0.335333347321, 0.896019995213, 0.335333347321, 0.912516653538, + 0.335333347321, 0.929013311863, 0.335333347321, 0.945510029793, + 0.335333347321, 0.962006688118, 0.335333347321, 0.978503346443, + 0.335333347321, 0.995000004768, 0.351830005646, 0.005200000014, + 0.351830005646, 0.021696666256, 0.351830005646, 0.038193333894, + 0.351830005646, 0.054689999670, 0.351830005646, 0.071186669171, + 0.351830005646, 0.087683334947, 0.351830005646, 0.104180000722, + 0.351830005646, 0.120676666498, 0.351830005646, 0.137173339725, + 0.351830005646, 0.153669998050, 0.351830005646, 0.170166671276, + 0.351782768965, 0.186563611031, 0.351753979921, 0.203007981181, + 0.351745367050, 0.219496786594, 0.351726621389, 0.235969558358, + 0.351637959480, 0.252329915762, 0.351522743702, 0.268668740988, + 0.351442903280, 0.285084187984, 0.351565003395, 0.301786661148, + 0.351573407650, 0.318323045969, 0.351581484079, 0.334857225418, + 0.351589232683, 0.351389229298, 0.351467221975, 0.367804199457, + 0.351433098316, 0.384314626455, 0.351421505213, 0.400847673416, + 0.351399272680, 0.417377382517, 0.351380228996, 0.433913439512, + 0.351515233517, 0.450505077839, 0.351526618004, 0.467039257288, + 0.351533651352, 0.483570396900, 0.351536005735, 0.500100016594, + 0.351533651352, 0.516629576683, 0.351526618004, 0.533160746098, + 0.351515233517, 0.549694895744, 0.351380228996, 0.566286563873, + 0.351399272680, 0.582822620869, 0.351421505213, 0.599352300167, + 0.351433098316, 0.615885376930, 0.351467221975, 0.632395803928, + 0.351589232683, 0.648810744286, 0.351581484079, 0.665342807770, + 0.351573407650, 0.681876957417, 0.351565003395, 0.698413312435, + 0.351442903280, 0.715115845203, 0.351522743702, 0.731531262398, + 0.351637959480, 0.747870087624, 0.351726621389, 0.764230430126, + 0.351745367050, 0.780703246593, 0.351753979921, 0.797192037106, + 0.351782768965, 0.813636422157, 0.351830005646, 0.830033361912, + 0.351830005646, 0.846530020237, 0.351830005646, 0.863026678562, + 0.351830005646, 0.879523336887, 0.351830005646, 0.896019995213, + 0.351830005646, 0.912516653538, 0.351830005646, 0.929013311863, + 0.351830005646, 0.945510029793, 0.351830005646, 0.962006688118, + 0.351830005646, 0.978503346443, 0.351830005646, 0.995000004768, + 0.368326663971, 0.005200000014, 0.368326663971, 0.021696666256, + 0.368326663971, 0.038193333894, 0.368326663971, 0.054689999670, + 0.368326663971, 0.071186669171, 0.368326663971, 0.087683334947, + 0.368326663971, 0.104180000722, 0.368326663971, 0.120676666498, + 0.368326663971, 0.137173339725, 0.368326663971, 0.153669998050, + 0.368326663971, 0.170166671276, 0.368273049593, 0.186536028981, + 0.368246942759, 0.202980652452, 0.368242949247, 0.219478771091, + 0.368174701929, 0.235849425197, 0.368132591248, 0.252286106348, + 0.368009299040, 0.268591254950, 0.368088603020, 0.285256475210, + 0.368096590042, 0.301794886589, 0.368104368448, 0.318331003189, + 0.368111908436, 0.334864884615, 0.368004202843, 0.351267218590, + 0.367969691753, 0.367769688368, 0.367956489325, 0.384299427271, + 0.367932587862, 0.400824457407, 0.368048876524, 0.417443037033, + 0.368067443371, 0.433983713388, 0.368082612753, 0.450518488884, + 0.367806136608, 0.466976523399, 0.367779970169, 0.483534991741, + 0.367771118879, 0.500100016594, 0.367779970169, 0.516664981842, + 0.367806136608, 0.533223450184, 0.368082612753, 0.549681544304, + 0.368067443371, 0.566216289997, 0.368048876524, 0.582756936550, + 0.367932587862, 0.599375545979, 0.367956489325, 0.615900576115, + 0.367969691753, 0.632430315018, 0.368004202843, 0.648932754993, + 0.368111908436, 0.665335118771, 0.368104368448, 0.681868970394, + 0.368096590042, 0.698405086994, 0.368088603020, 0.714943528175, + 0.368009299040, 0.731608748436, 0.368132591248, 0.747913897038, + 0.368174701929, 0.764350593090, 0.368242949247, 0.780721247196, + 0.368246942759, 0.797219336033, 0.368273049593, 0.813663959503, + 0.368326663971, 0.830033361912, 0.368326663971, 0.846530020237, + 0.368326663971, 0.863026678562, 0.368326663971, 0.879523336887, + 0.368326663971, 0.896019995213, 0.368326663971, 0.912516653538, + 0.368326663971, 0.929013311863, 0.368326663971, 0.945510029793, + 0.368326663971, 0.962006688118, 0.368326663971, 0.978503346443, + 0.368326663971, 0.995000004768, 0.384823322296, 0.005200000014, + 0.384823322296, 0.021696666256, 0.384823322296, 0.038193333894, + 0.384823322296, 0.054689999670, 0.384823322296, 0.071186669171, + 0.384823322296, 0.087683334947, 0.384823322296, 0.104180000722, + 0.384823322296, 0.120676666498, 0.384823322296, 0.137173339725, + 0.384823322296, 0.153669998050, 0.384823322296, 0.170166671276, + 0.384767293930, 0.186511233449, 0.384758800268, 0.202994033694, + 0.384743392467, 0.219462499022, 0.384672790766, 0.235809206963, + 0.384577631950, 0.252123475075, 0.384510397911, 0.268520772457, + 0.384619176388, 0.285264164209, 0.384626418352, 0.301802426577, + 0.384633481503, 0.318338364363, 0.384548008442, 0.334740012884, + 0.384514629841, 0.351233094931, 0.384499430656, 0.367756485939, + 0.384475171566, 0.384275197983, 0.384587377310, 0.400917768478, + 0.384607851505, 0.417462766171, 0.384344965219, 0.433839976788, + 0.384289413691, 0.450381159782, 0.384247630835, 0.466942191124, + 0.384221613407, 0.483517378569, 0.384224712849, 0.500100016594, + 0.384221613407, 0.516682624817, 0.384247630835, 0.533257842064, + 0.384289413691, 0.549818813801, 0.384344965219, 0.566359996796, + 0.384607851505, 0.582737267017, 0.384587377310, 0.599282264709, + 0.384475171566, 0.615924835205, 0.384499430656, 0.632443487644, + 0.384514629841, 0.648966908455, 0.384548008442, 0.665459990501, + 0.384633481503, 0.681861639023, 0.384626418352, 0.698397576809, + 0.384619176388, 0.714935839176, 0.384510397911, 0.731679201126, + 0.384577631950, 0.748076558113, 0.384672790766, 0.764390826225, + 0.384743392467, 0.780737519264, 0.384758800268, 0.797205984592, + 0.384767293930, 0.813688755035, 0.384823322296, 0.830033361912, + 0.384823322296, 0.846530020237, 0.384823322296, 0.863026678562, + 0.384823322296, 0.879523336887, 0.384823322296, 0.896019995213, + 0.384823322296, 0.912516653538, 0.384823322296, 0.929013311863, + 0.384823322296, 0.945510029793, 0.384823322296, 0.962006688118, + 0.384823322296, 0.978503346443, 0.384823322296, 0.995000004768, + 0.401320010424, 0.005200000014, 0.401320010424, 0.021696666256, + 0.401320010424, 0.038193333894, 0.401320010424, 0.054689999670, + 0.401320010424, 0.071186669171, 0.401320010424, 0.087683334947, + 0.401320010424, 0.104180000722, 0.401320010424, 0.120676666498, + 0.401320010424, 0.137173339725, 0.401320010424, 0.153669998050, + 0.401285439730, 0.170051515102, 0.401265054941, 0.186489373446, + 0.401259839535, 0.202979549766, 0.401210993528, 0.219347789884, + 0.401177585125, 0.235773533583, 0.401083946228, 0.252059876919, + 0.401141673326, 0.268730610609, 0.401148170233, 0.285271078348, + 0.401154607534, 0.301809191704, 0.401095509529, 0.318225115538, + 0.401064634323, 0.334707736969, 0.401047676802, 0.351221501827, + 0.401024430990, 0.367732584476, 0.401117742062, 0.384387373924, + 0.401138633490, 0.400938630104, 0.400883078575, 0.417252570391, + 0.400819182396, 0.433779448271, 0.400777846575, 0.450338929892, + 0.400739639997, 0.466913223267, 0.400715559721, 0.483502596617, + 0.400707334280, 0.500100016594, 0.400715559721, 0.516697406769, + 0.400739639997, 0.533286809921, 0.400777846575, 0.549861073494, + 0.400819182396, 0.566420555115, 0.400883078575, 0.582947432995, + 0.401138633490, 0.599261343479, 0.401117742062, 0.615812599659, + 0.401024430990, 0.632467389107, 0.401047676802, 0.648978471756, + 0.401064634323, 0.665492236614, 0.401095509529, 0.681974887848, + 0.401154607534, 0.698390781879, 0.401148170233, 0.714928925037, + 0.401141673326, 0.731469392776, 0.401083946228, 0.748140096664, + 0.401177585125, 0.764426469803, 0.401210993528, 0.780852198601, + 0.401259839535, 0.797220468521, 0.401265054941, 0.813710629940, + 0.401285439730, 0.830148518085, 0.401320010424, 0.846530020237, + 0.401320010424, 0.863026678562, 0.401320010424, 0.879523336887, + 0.401320010424, 0.896019995213, 0.401320010424, 0.912516653538, + 0.401320010424, 0.929013311863, 0.401320010424, 0.945510029793, + 0.401320010424, 0.962006688118, 0.401320010424, 0.978503346443, + 0.401320010424, 0.995000004768, 0.417816668749, 0.005200000014, + 0.417816668749, 0.021696666256, 0.417816668749, 0.038193333894, + 0.417816668749, 0.054689999670, 0.417816668749, 0.071186669171, + 0.417816668749, 0.087683334947, 0.417816668749, 0.104180000722, + 0.417816668749, 0.120676666498, 0.417816668749, 0.137173339725, + 0.417816668749, 0.153669998050, 0.417783170938, 0.170032665133, + 0.417774528265, 0.186503171921, 0.417763084173, 0.202967077494, + 0.417716711760, 0.219316795468, 0.417652368546, 0.235627561808, + 0.417601615191, 0.252004832029, 0.417670249939, 0.268736660480, + 0.417675793171, 0.285277068615, 0.417681306601, 0.301815122366, + 0.417616337538, 0.318195968866, 0.417598336935, 0.334696710110, + 0.417577385902, 0.351199269295, 0.417643040419, 0.367848873138, + 0.417662769556, 0.384407877922, 0.417452573776, 0.400683104992, + 0.417386859655, 0.417186886072, 0.417339563370, 0.433731645346, + 0.417292088270, 0.450295239687, 0.417440444231, 0.466956168413, + 0.417448908091, 0.483529776335, 0.417451858521, 0.500100016594, + 0.417448908091, 0.516670227051, 0.417440444231, 0.533243834972, + 0.417292088270, 0.549904763699, 0.417339563370, 0.566468358040, + 0.417386859655, 0.583013117313, 0.417452573776, 0.599516928196, + 0.417662769556, 0.615792155266, 0.417643040419, 0.632351100445, + 0.417577385902, 0.649000704288, 0.417598336935, 0.665503323078, + 0.417616337538, 0.682004034519, 0.417681306601, 0.698384881020, + 0.417675793171, 0.714922904968, 0.417670249939, 0.731463313103, + 0.417601615191, 0.748195171356, 0.417652368546, 0.764572441578, + 0.417716711760, 0.780883193016, 0.417763084173, 0.797232925892, + 0.417774528265, 0.813696801662, 0.417783170938, 0.830167353153, + 0.417816668749, 0.846530020237, 0.417816668749, 0.863026678562, + 0.417816668749, 0.879523336887, 0.417816668749, 0.896019995213, + 0.417816668749, 0.912516653538, 0.417816668749, 0.929013311863, + 0.417816668749, 0.945510029793, 0.417816668749, 0.962006688118, + 0.417816668749, 0.978503346443, 0.417816668749, 0.995000004768, + 0.434313327074, 0.005200000014, 0.434313327074, 0.021696666256, + 0.434313327074, 0.038193333894, 0.434313327074, 0.054689999670, + 0.434313327074, 0.071186669171, 0.434313327074, 0.087683334947, + 0.434313327074, 0.104180000722, 0.434313327074, 0.120676666498, + 0.434313327074, 0.137173339725, 0.434313327074, 0.153669998050, + 0.434283405542, 0.170017048717, 0.434277445078, 0.186492800713, + 0.434268176556, 0.202956736088, 0.434227287769, 0.219291031361, + 0.434170335531, 0.235581368208, 0.434129029512, 0.251958876848, + 0.434197634459, 0.268741726875, 0.434202194214, 0.285282105207, + 0.434166491032, 0.301699489355, 0.434144109488, 0.318171292543, + 0.434130907059, 0.334677249193, 0.434113442898, 0.351180225611, + 0.434183716774, 0.367867439985, 0.434039980173, 0.384144961834, + 0.433979451656, 0.400619179010, 0.433931648731, 0.417139559984, + 0.433882117271, 0.433682113886, 0.434021472931, 0.450391113758, + 0.434033989906, 0.466966986656, 0.433963954449, 0.483515977859, + 0.433960437775, 0.500100016594, 0.433963954449, 0.516683995724, + 0.434033989906, 0.533232986927, 0.434021472931, 0.549808859825, + 0.433882117271, 0.566517889500, 0.433931648731, 0.583060443401, + 0.433979451656, 0.599580824375, 0.434039980173, 0.616055011749, + 0.434183716774, 0.632332563400, 0.434113442898, 0.649019777775, + 0.434130907059, 0.665522754192, 0.434144109488, 0.682028710842, + 0.434166491032, 0.698500514030, 0.434202194214, 0.714917898178, + 0.434197634459, 0.731458246708, 0.434129029512, 0.748241126537, + 0.434170335531, 0.764618635178, 0.434227287769, 0.780908942223, + 0.434268176556, 0.797243237495, 0.434277445078, 0.813707232475, + 0.434283405542, 0.830182969570, 0.434313327074, 0.846530020237, + 0.434313327074, 0.863026678562, 0.434313327074, 0.879523336887, + 0.434313327074, 0.896019995213, 0.434313327074, 0.912516653538, + 0.434313327074, 0.929013311863, 0.434313327074, 0.945510029793, + 0.434313327074, 0.962006688118, 0.434313327074, 0.978503346443, + 0.434313327074, 0.995000004768, 0.450809985399, 0.005200000014, + 0.450809985399, 0.021696666256, 0.450809985399, 0.038193333894, + 0.450809985399, 0.054689999670, 0.450809985399, 0.071186669171, + 0.450809985399, 0.087683334947, 0.450809985399, 0.104180000722, + 0.450809985399, 0.120676666498, 0.450809985399, 0.137173339725, + 0.450809985399, 0.153669998050, 0.450785726309, 0.170004799962, + 0.450781792402, 0.186484634876, 0.450759410858, 0.202856481075, + 0.450741887093, 0.219270721078, 0.450695931911, 0.235544919968, + 0.450720608234, 0.252203047276, 0.450724095106, 0.268745750189, + 0.450727552176, 0.285286098719, 0.450694888830, 0.301679581404, + 0.450677692890, 0.318151563406, 0.450668483973, 0.334661602974, + 0.450705081224, 0.351315230131, 0.450718492270, 0.367882639170, + 0.450581163168, 0.384089410305, 0.450538933277, 0.400577843189, + 0.450495243073, 0.417092084885, 0.450591117144, 0.433821469545, + 0.450604587793, 0.450404584408, 0.450536906719, 0.466924607754, + 0.450527459383, 0.483509153128, 0.450524002314, 0.500100016594, + 0.450527459383, 0.516690850258, 0.450536906719, 0.533275365829, + 0.450604587793, 0.549795448780, 0.450591117144, 0.566378533840, + 0.450495243073, 0.583107888699, 0.450538933277, 0.599622189999, + 0.450581163168, 0.616110563278, 0.450718492270, 0.632317364216, + 0.450705081224, 0.648884773254, 0.450668483973, 0.665538370609, + 0.450677692890, 0.682048439980, 0.450694888830, 0.698520421982, + 0.450727552176, 0.714913904667, 0.450724095106, 0.731454253197, + 0.450720608234, 0.747996926308, 0.450695931911, 0.764655053616, + 0.450741887093, 0.780929267406, 0.450759410858, 0.797343492508, + 0.450781792402, 0.813715338707, 0.450785726309, 0.830195188522, + 0.450809985399, 0.846530020237, 0.450809985399, 0.863026678562, + 0.450809985399, 0.879523336887, 0.450809985399, 0.896019995213, + 0.450809985399, 0.912516653538, 0.450809985399, 0.929013311863, + 0.450809985399, 0.945510029793, 0.450809985399, 0.962006688118, + 0.450809985399, 0.978503346443, 0.450809985399, 0.995000004768, + 0.467306673527, 0.005200000014, 0.467306673527, 0.021696666256, + 0.467306673527, 0.038193333894, 0.467306673527, 0.054689999670, + 0.467306673527, 0.071186669171, 0.467306673527, 0.087683334947, + 0.467306673527, 0.104180000722, 0.467306673527, 0.120676666498, + 0.467306673527, 0.137173339725, 0.467306673527, 0.153669998050, + 0.467289596796, 0.169995978475, 0.467287242413, 0.186478763819, + 0.467271298170, 0.202841818333, 0.467259526253, 0.219256073236, + 0.467227309942, 0.235518589616, 0.467247456312, 0.252205967903, + 0.467249810696, 0.268748670816, 0.467252165079, 0.285289019346, + 0.467227518559, 0.301665097475, 0.467219352722, 0.318156361580, + 0.467210024595, 0.334650129080, 0.467239260674, 0.351326644421, + 0.467176526785, 0.367606133223, 0.467142194510, 0.384047627449, + 0.467113226652, 0.400539636612, 0.467156171799, 0.417240440845, + 0.467166990042, 0.433833986521, 0.467124611139, 0.450336933136, + 0.467113554478, 0.466913551092, 0.466898351908, 0.483399182558, + 0.466870367527, 0.500100016594, 0.466898351908, 0.516800820827, + 0.467113554478, 0.533286452293, 0.467124611139, 0.549863100052, + 0.467166990042, 0.566366016865, 0.467156171799, 0.582959532738, + 0.467113226652, 0.599660336971, 0.467142194510, 0.616152346134, + 0.467176526785, 0.632593870163, 0.467239260674, 0.648873388767, + 0.467210024595, 0.665549874306, 0.467219352722, 0.682043612003, + 0.467227518559, 0.698534905910, 0.467252165079, 0.714910984039, + 0.467249810696, 0.731451332569, 0.467247456312, 0.747994005680, + 0.467227309942, 0.764681398869, 0.467259526253, 0.780943930149, + 0.467271298170, 0.797358155251, 0.467287242413, 0.813721239567, + 0.467289596796, 0.830204010010, 0.467306673527, 0.846530020237, + 0.467306673527, 0.863026678562, 0.467306673527, 0.879523336887, + 0.467306673527, 0.896019995213, 0.467306673527, 0.912516653538, + 0.467306673527, 0.929013311863, 0.467306673527, 0.945510029793, + 0.467306673527, 0.962006688118, 0.467306673527, 0.978503346443, + 0.467306673527, 0.995000004768, 0.483803331852, 0.005200000014, + 0.483803331852, 0.021696666256, 0.483803331852, 0.038193333894, + 0.483803331852, 0.054689999670, 0.483803331852, 0.071186669171, + 0.483803331852, 0.087683334947, 0.483803331852, 0.104180000722, + 0.483803331852, 0.120676666498, 0.483803331852, 0.137173339725, + 0.483798086643, 0.153559774160, 0.483794540167, 0.169990658760, + 0.483793437481, 0.186475217342, 0.483785152435, 0.202832967043, + 0.483779251575, 0.219247221947, 0.483762651682, 0.235502660275, + 0.483773857355, 0.252207756042, 0.483775019646, 0.268750458956, + 0.483776211739, 0.285290777683, 0.483763039112, 0.301656305790, + 0.483759015799, 0.318149328232, 0.483754307032, 0.334643095732, + 0.483770400286, 0.351333647966, 0.483734995127, 0.367579966784, + 0.483717381954, 0.384021610022, 0.483702600002, 0.400515586138, + 0.483729779720, 0.417248904705, 0.483715981245, 0.433763951063, + 0.483709156513, 0.450327455997, 0.483599185944, 0.466698348522, + 0.483550459146, 0.483350485563, 0.483633339405, 0.500100016594, + 0.483550459146, 0.516849517822, 0.483599185944, 0.533501625061, + 0.483709156513, 0.549872517586, 0.483715981245, 0.566436052322, + 0.483729779720, 0.582951068878, 0.483702600002, 0.599684417248, + 0.483717381954, 0.616178393364, 0.483734995127, 0.632620036602, + 0.483770400286, 0.648866355419, 0.483754307032, 0.665556907654, + 0.483759015799, 0.682050645351, 0.483763039112, 0.698543727398, + 0.483776211739, 0.714909195900, 0.483775019646, 0.731449544430, + 0.483773857355, 0.747992277145, 0.483762651682, 0.764697313309, + 0.483779251575, 0.780952751637, 0.483785152435, 0.797367036343, + 0.483793437481, 0.813724756241, 0.483794540167, 0.830209314823, + 0.483798086643, 0.846640229225, 0.483803331852, 0.863026678562, + 0.483803331852, 0.879523336887, 0.483803331852, 0.896019995213, + 0.483803331852, 0.912516653538, 0.483803331852, 0.929013311863, + 0.483803331852, 0.945510029793, 0.483803331852, 0.962006688118, + 0.483803331852, 0.978503346443, 0.483803331852, 0.995000004768, + 0.500299990177, 0.005200000014, 0.500299990177, 0.021696666256, + 0.500299990177, 0.038193333894, 0.500299990177, 0.054689999670, + 0.500299990177, 0.071186669171, 0.500299990177, 0.087683334947, + 0.500299990177, 0.104180000722, 0.500299990177, 0.120676666498, + 0.500299990177, 0.137173339725, 0.500299990177, 0.153558000922, + 0.500299990177, 0.169988885522, 0.500299990177, 0.186474040151, + 0.500299990177, 0.202830001712, 0.500299990177, 0.219244256616, + 0.500299990177, 0.235497340560, 0.500299990177, 0.252208322287, + 0.500299990177, 0.268751025200, 0.500299990177, 0.285291373730, + 0.500299990177, 0.301653325558, 0.500299990177, 0.318146973848, + 0.500299990177, 0.334640741348, 0.500299990177, 0.351336002350, + 0.500299990177, 0.367571115494, 0.500299990177, 0.384024709463, + 0.500299990177, 0.400507330894, 0.500299990177, 0.417251855135, + 0.500299990177, 0.433760434389, 0.500299990177, 0.450323998928, + 0.500299990177, 0.466670364141, 0.500299990177, 0.483433336020, + 0.500299990177, 0.500100016594, 0.500299990177, 0.516766667366, + 0.500299990177, 0.533529639244, 0.500299990177, 0.549875974655, + 0.500299990177, 0.566439568996, 0.500299990177, 0.582948148251, + 0.500299990177, 0.599692642689, 0.500299990177, 0.616175293922, + 0.500299990177, 0.632628917694, 0.500299990177, 0.648863971233, + 0.500299990177, 0.665559232235, 0.500299990177, 0.682053029537, + 0.500299990177, 0.698546648026, 0.500299990177, 0.714908599854, + 0.500299990177, 0.731448948383, 0.500299990177, 0.747991681099, + 0.500299990177, 0.764702677727, 0.500299990177, 0.780955731869, + 0.500299990177, 0.797370016575, 0.500299990177, 0.813725948334, + 0.500299990177, 0.830211102962, 0.500299990177, 0.846642017365, + 0.500299990177, 0.863026678562, 0.500299990177, 0.879523336887, + 0.500299990177, 0.896019995213, 0.500299990177, 0.912516653538, + 0.500299990177, 0.929013311863, 0.500299990177, 0.945510029793, + 0.500299990177, 0.962006688118, 0.500299990177, 0.978503346443, + 0.500299990177, 0.995000004768, 0.516796648502, 0.005200000014, + 0.516796648502, 0.021696666256, 0.516796648502, 0.038193333894, + 0.516796648502, 0.054689999670, 0.516796648502, 0.071186669171, + 0.516796648502, 0.087683334947, 0.516796648502, 0.104180000722, + 0.516796648502, 0.120676666498, 0.516796648502, 0.137173339725, + 0.516801893711, 0.153559774160, 0.516805469990, 0.169990658760, + 0.516806542873, 0.186475217342, 0.516814827919, 0.202832967043, + 0.516820728779, 0.219247221947, 0.516837358475, 0.235502660275, + 0.516826152802, 0.252207756042, 0.516824960709, 0.268750458956, + 0.516823768616, 0.285290777683, 0.516837000847, 0.301656305790, + 0.516840994358, 0.318149328232, 0.516845703125, 0.334643095732, + 0.516829609871, 0.351333647966, 0.516865015030, 0.367579966784, + 0.516882598400, 0.384021610022, 0.516897380352, 0.400515586138, + 0.516870200634, 0.417248904705, 0.516884028912, 0.433763951063, + 0.516890823841, 0.450327455997, 0.517000854015, 0.466698348522, + 0.517049551010, 0.483350485563, 0.516966640949, 0.500100016594, + 0.517049551010, 0.516849517822, 0.517000854015, 0.533501625061, + 0.516890823841, 0.549872517586, 0.516884028912, 0.566436052322, + 0.516870200634, 0.582951068878, 0.516897380352, 0.599684417248, + 0.516882598400, 0.616178393364, 0.516865015030, 0.632620036602, + 0.516829609871, 0.648866355419, 0.516845703125, 0.665556907654, + 0.516840994358, 0.682050645351, 0.516837000847, 0.698543727398, + 0.516823768616, 0.714909195900, 0.516824960709, 0.731449544430, + 0.516826152802, 0.747992277145, 0.516837358475, 0.764697313309, + 0.516820728779, 0.780952751637, 0.516814827919, 0.797367036343, + 0.516806542873, 0.813724756241, 0.516805469990, 0.830209314823, + 0.516801893711, 0.846640229225, 0.516796648502, 0.863026678562, + 0.516796648502, 0.879523336887, 0.516796648502, 0.896019995213, + 0.516796648502, 0.912516653538, 0.516796648502, 0.929013311863, + 0.516796648502, 0.945510029793, 0.516796648502, 0.962006688118, + 0.516796648502, 0.978503346443, 0.516796648502, 0.995000004768, + 0.533293306828, 0.005200000014, 0.533293306828, 0.021696666256, + 0.533293306828, 0.038193333894, 0.533293306828, 0.054689999670, + 0.533293306828, 0.071186669171, 0.533293306828, 0.087683334947, + 0.533293306828, 0.104180000722, 0.533293306828, 0.120676666498, + 0.533293306828, 0.137173339725, 0.533293306828, 0.153669998050, + 0.533310413361, 0.169995978475, 0.533312737942, 0.186478763819, + 0.533328711987, 0.202841818333, 0.533340454102, 0.219256073236, + 0.533372700214, 0.235518589616, 0.533352553844, 0.252205967903, + 0.533350169659, 0.268748670816, 0.533347845078, 0.285289019346, + 0.533372461796, 0.301665097475, 0.533380687237, 0.318156361580, + 0.533389985561, 0.334650129080, 0.533360719681, 0.351326644421, + 0.533423483372, 0.367606133223, 0.533457815647, 0.384047627449, + 0.533486783504, 0.400539636612, 0.533443808556, 0.417240440845, + 0.533433020115, 0.433833986521, 0.533475399017, 0.450336933136, + 0.533486425877, 0.466913551092, 0.533701658249, 0.483399182558, + 0.533729612827, 0.500100016594, 0.533701658249, 0.516800820827, + 0.533486425877, 0.533286452293, 0.533475399017, 0.549863100052, + 0.533433020115, 0.566366016865, 0.533443808556, 0.582959532738, + 0.533486783504, 0.599660336971, 0.533457815647, 0.616152346134, + 0.533423483372, 0.632593870163, 0.533360719681, 0.648873388767, + 0.533389985561, 0.665549874306, 0.533380687237, 0.682043612003, + 0.533372461796, 0.698534905910, 0.533347845078, 0.714910984039, + 0.533350169659, 0.731451332569, 0.533352553844, 0.747994005680, + 0.533372700214, 0.764681398869, 0.533340454102, 0.780943930149, + 0.533328711987, 0.797358155251, 0.533312737942, 0.813721239567, + 0.533310413361, 0.830204010010, 0.533293306828, 0.846530020237, + 0.533293306828, 0.863026678562, 0.533293306828, 0.879523336887, + 0.533293306828, 0.896019995213, 0.533293306828, 0.912516653538, + 0.533293306828, 0.929013311863, 0.533293306828, 0.945510029793, + 0.533293306828, 0.962006688118, 0.533293306828, 0.978503346443, + 0.533293306828, 0.995000004768, 0.549790024757, 0.005200000014, + 0.549790024757, 0.021696666256, 0.549790024757, 0.038193333894, + 0.549790024757, 0.054689999670, 0.549790024757, 0.071186669171, + 0.549790024757, 0.087683334947, 0.549790024757, 0.104180000722, + 0.549790024757, 0.120676666498, 0.549790024757, 0.137173339725, + 0.549790024757, 0.153669998050, 0.549814283848, 0.170004799962, + 0.549818217754, 0.186484634876, 0.549840569496, 0.202856481075, + 0.549858093262, 0.219270721078, 0.549904048443, 0.235544919968, + 0.549879372120, 0.252203047276, 0.549875915051, 0.268745750189, + 0.549872457981, 0.285286098719, 0.549905121326, 0.301679581404, + 0.549922287464, 0.318151563406, 0.549931526184, 0.334661602974, + 0.549894928932, 0.351315230131, 0.549881517887, 0.367882639170, + 0.550018846989, 0.384089410305, 0.550061106682, 0.400577843189, + 0.550104737282, 0.417092084885, 0.550008893013, 0.433821469545, + 0.549995422363, 0.450404584408, 0.550063073635, 0.466924607754, + 0.550072550774, 0.483509153128, 0.550076007843, 0.500100016594, + 0.550072550774, 0.516690850258, 0.550063073635, 0.533275365829, + 0.549995422363, 0.549795448780, 0.550008893013, 0.566378533840, + 0.550104737282, 0.583107888699, 0.550061106682, 0.599622189999, + 0.550018846989, 0.616110563278, 0.549881517887, 0.632317364216, + 0.549894928932, 0.648884773254, 0.549931526184, 0.665538370609, + 0.549922287464, 0.682048439980, 0.549905121326, 0.698520421982, + 0.549872457981, 0.714913904667, 0.549875915051, 0.731454253197, + 0.549879372120, 0.747996926308, 0.549904048443, 0.764655053616, + 0.549858093262, 0.780929267406, 0.549840569496, 0.797343492508, + 0.549818217754, 0.813715338707, 0.549814283848, 0.830195188522, + 0.549790024757, 0.846530020237, 0.549790024757, 0.863026678562, + 0.549790024757, 0.879523336887, 0.549790024757, 0.896019995213, + 0.549790024757, 0.912516653538, 0.549790024757, 0.929013311863, + 0.549790024757, 0.945510029793, 0.549790024757, 0.962006688118, + 0.549790024757, 0.978503346443, 0.549790024757, 0.995000004768, + 0.566286683083, 0.005200000014, 0.566286683083, 0.021696666256, + 0.566286683083, 0.038193333894, 0.566286683083, 0.054689999670, + 0.566286683083, 0.071186669171, 0.566286683083, 0.087683334947, + 0.566286683083, 0.104180000722, 0.566286683083, 0.120676666498, + 0.566286683083, 0.137173339725, 0.566286683083, 0.153669998050, + 0.566316604614, 0.170017048717, 0.566322565079, 0.186492800713, + 0.566331863403, 0.202956736088, 0.566372692585, 0.219291031361, + 0.566429674625, 0.235581368208, 0.566470980644, 0.251958876848, + 0.566402375698, 0.268741726875, 0.566397786140, 0.285282105207, + 0.566433489323, 0.301699489355, 0.566455900669, 0.318171292543, + 0.566469073296, 0.334677249193, 0.566486597061, 0.351180225611, + 0.566416263580, 0.367867439985, 0.566560029984, 0.384144961834, + 0.566620528698, 0.400619179010, 0.566668331623, 0.417139559984, + 0.566717863083, 0.433682113886, 0.566578507423, 0.450391113758, + 0.566565990448, 0.466966986656, 0.566636025906, 0.483515977859, + 0.566639542580, 0.500100016594, 0.566636025906, 0.516683995724, + 0.566565990448, 0.533232986927, 0.566578507423, 0.549808859825, + 0.566717863083, 0.566517889500, 0.566668331623, 0.583060443401, + 0.566620528698, 0.599580824375, 0.566560029984, 0.616055011749, + 0.566416263580, 0.632332563400, 0.566486597061, 0.649019777775, + 0.566469073296, 0.665522754192, 0.566455900669, 0.682028710842, + 0.566433489323, 0.698500514030, 0.566397786140, 0.714917898178, + 0.566402375698, 0.731458246708, 0.566470980644, 0.748241126537, + 0.566429674625, 0.764618635178, 0.566372692585, 0.780908942223, + 0.566331863403, 0.797243237495, 0.566322565079, 0.813707232475, + 0.566316604614, 0.830182969570, 0.566286683083, 0.846530020237, + 0.566286683083, 0.863026678562, 0.566286683083, 0.879523336887, + 0.566286683083, 0.896019995213, 0.566286683083, 0.912516653538, + 0.566286683083, 0.929013311863, 0.566286683083, 0.945510029793, + 0.566286683083, 0.962006688118, 0.566286683083, 0.978503346443, + 0.566286683083, 0.995000004768, 0.582783341408, 0.005200000014, + 0.582783341408, 0.021696666256, 0.582783341408, 0.038193333894, + 0.582783341408, 0.054689999670, 0.582783341408, 0.071186669171, + 0.582783341408, 0.087683334947, 0.582783341408, 0.104180000722, + 0.582783341408, 0.120676666498, 0.582783341408, 0.137173339725, + 0.582783341408, 0.153669998050, 0.582816839218, 0.170032665133, + 0.582825481892, 0.186503171921, 0.582836925983, 0.202967077494, + 0.582883298397, 0.219316795468, 0.582947611809, 0.235627561808, + 0.582998394966, 0.252004832029, 0.582929790020, 0.268736660480, + 0.582924187183, 0.285277068615, 0.582918703556, 0.301815122366, + 0.582983672619, 0.318195968866, 0.583001673222, 0.334696710110, + 0.583022594452, 0.351199269295, 0.582956969738, 0.367848873138, + 0.582937240601, 0.384407877922, 0.583147406578, 0.400683104992, + 0.583213150501, 0.417186886072, 0.583260416985, 0.433731645346, + 0.583307921886, 0.450295239687, 0.583159565926, 0.466956168413, + 0.583151102066, 0.483529776335, 0.583148121834, 0.500100016594, + 0.583151102066, 0.516670227051, 0.583159565926, 0.533243834972, + 0.583307921886, 0.549904763699, 0.583260416985, 0.566468358040, + 0.583213150501, 0.583013117313, 0.583147406578, 0.599516928196, + 0.582937240601, 0.615792155266, 0.582956969738, 0.632351100445, + 0.583022594452, 0.649000704288, 0.583001673222, 0.665503323078, + 0.582983672619, 0.682004034519, 0.582918703556, 0.698384881020, + 0.582924187183, 0.714922904968, 0.582929790020, 0.731463313103, + 0.582998394966, 0.748195171356, 0.582947611809, 0.764572441578, + 0.582883298397, 0.780883193016, 0.582836925983, 0.797232925892, + 0.582825481892, 0.813696801662, 0.582816839218, 0.830167353153, + 0.582783341408, 0.846530020237, 0.582783341408, 0.863026678562, + 0.582783341408, 0.879523336887, 0.582783341408, 0.896019995213, + 0.582783341408, 0.912516653538, 0.582783341408, 0.929013311863, + 0.582783341408, 0.945510029793, 0.582783341408, 0.962006688118, + 0.582783341408, 0.978503346443, 0.582783341408, 0.995000004768, + 0.599279999733, 0.005200000014, 0.599279999733, 0.021696666256, + 0.599279999733, 0.038193333894, 0.599279999733, 0.054689999670, + 0.599279999733, 0.071186669171, 0.599279999733, 0.087683334947, + 0.599279999733, 0.104180000722, 0.599279999733, 0.120676666498, + 0.599279999733, 0.137173339725, 0.599279999733, 0.153669998050, + 0.599314570427, 0.170051515102, 0.599334955215, 0.186489373446, + 0.599340140820, 0.202979549766, 0.599389016628, 0.219347789884, + 0.599422454834, 0.235773533583, 0.599516034126, 0.252059876919, + 0.599458336830, 0.268730610609, 0.599451839924, 0.285271078348, + 0.599445402622, 0.301809191704, 0.599504470825, 0.318225115538, + 0.599535346031, 0.334707736969, 0.599552333355, 0.351221501827, + 0.599575579166, 0.367732584476, 0.599482238293, 0.384387373924, + 0.599461376667, 0.400938630104, 0.599716901779, 0.417252570391, + 0.599780797958, 0.433779448271, 0.599822163582, 0.450338929892, + 0.599860370159, 0.466913223267, 0.599884450436, 0.483502596617, + 0.599892675877, 0.500100016594, 0.599884450436, 0.516697406769, + 0.599860370159, 0.533286809921, 0.599822163582, 0.549861073494, + 0.599780797958, 0.566420555115, 0.599716901779, 0.582947432995, + 0.599461376667, 0.599261343479, 0.599482238293, 0.615812599659, + 0.599575579166, 0.632467389107, 0.599552333355, 0.648978471756, + 0.599535346031, 0.665492236614, 0.599504470825, 0.681974887848, + 0.599445402622, 0.698390781879, 0.599451839924, 0.714928925037, + 0.599458336830, 0.731469392776, 0.599516034126, 0.748140096664, + 0.599422454834, 0.764426469803, 0.599389016628, 0.780852198601, + 0.599340140820, 0.797220468521, 0.599334955215, 0.813710629940, + 0.599314570427, 0.830148518085, 0.599279999733, 0.846530020237, + 0.599279999733, 0.863026678562, 0.599279999733, 0.879523336887, + 0.599279999733, 0.896019995213, 0.599279999733, 0.912516653538, + 0.599279999733, 0.929013311863, 0.599279999733, 0.945510029793, + 0.599279999733, 0.962006688118, 0.599279999733, 0.978503346443, + 0.599279999733, 0.995000004768, 0.615776658058, 0.005200000014, + 0.615776658058, 0.021696666256, 0.615776658058, 0.038193333894, + 0.615776658058, 0.054689999670, 0.615776658058, 0.071186669171, + 0.615776658058, 0.087683334947, 0.615776658058, 0.104180000722, + 0.615776658058, 0.120676666498, 0.615776658058, 0.137173339725, + 0.615776658058, 0.153669998050, 0.615776658058, 0.170166671276, + 0.615832686424, 0.186511233449, 0.615841209888, 0.202994033694, + 0.615856587887, 0.219462499022, 0.615927219391, 0.235809206963, + 0.616022408009, 0.252123475075, 0.616089642048, 0.268520772457, + 0.615980803967, 0.285264164209, 0.615973591805, 0.301802426577, + 0.615966498852, 0.318338364363, 0.616051971912, 0.334740012884, + 0.616085350513, 0.351233094931, 0.616100549698, 0.367756485939, + 0.616124808788, 0.384275197983, 0.616012632847, 0.400917768478, + 0.615992128849, 0.417462766171, 0.616255044937, 0.433839976788, + 0.616310596466, 0.450381159782, 0.616352379322, 0.466942191124, + 0.616378366947, 0.483517378569, 0.616375267506, 0.500100016594, + 0.616378366947, 0.516682624817, 0.616352379322, 0.533257842064, + 0.616310596466, 0.549818813801, 0.616255044937, 0.566359996796, + 0.615992128849, 0.582737267017, 0.616012632847, 0.599282264709, + 0.616124808788, 0.615924835205, 0.616100549698, 0.632443487644, + 0.616085350513, 0.648966908455, 0.616051971912, 0.665459990501, + 0.615966498852, 0.681861639023, 0.615973591805, 0.698397576809, + 0.615980803967, 0.714935839176, 0.616089642048, 0.731679201126, + 0.616022408009, 0.748076558113, 0.615927219391, 0.764390826225, + 0.615856587887, 0.780737519264, 0.615841209888, 0.797205984592, + 0.615832686424, 0.813688755035, 0.615776658058, 0.830033361912, + 0.615776658058, 0.846530020237, 0.615776658058, 0.863026678562, + 0.615776658058, 0.879523336887, 0.615776658058, 0.896019995213, + 0.615776658058, 0.912516653538, 0.615776658058, 0.929013311863, + 0.615776658058, 0.945510029793, 0.615776658058, 0.962006688118, + 0.615776658058, 0.978503346443, 0.615776658058, 0.995000004768, + 0.632273316383, 0.005200000014, 0.632273316383, 0.021696666256, + 0.632273316383, 0.038193333894, 0.632273316383, 0.054689999670, + 0.632273316383, 0.071186669171, 0.632273316383, 0.087683334947, + 0.632273316383, 0.104180000722, 0.632273316383, 0.120676666498, + 0.632273316383, 0.137173339725, 0.632273316383, 0.153669998050, + 0.632273316383, 0.170166671276, 0.632326960564, 0.186536028981, + 0.632353067398, 0.202980652452, 0.632357060909, 0.219478771091, + 0.632425308228, 0.235849425197, 0.632467389107, 0.252286106348, + 0.632590711117, 0.268591254950, 0.632511377335, 0.285256475210, + 0.632503390312, 0.301794886589, 0.632495641708, 0.318331003189, + 0.632488071918, 0.334864884615, 0.632595777512, 0.351267218590, + 0.632630288601, 0.367769688368, 0.632643520832, 0.384299427271, + 0.632667422295, 0.400824457407, 0.632551133633, 0.417443037033, + 0.632532536983, 0.433983713388, 0.632517397404, 0.450518488884, + 0.632793843746, 0.466976523399, 0.632820010185, 0.483534991741, + 0.632828891277, 0.500100016594, 0.632820010185, 0.516664981842, + 0.632793843746, 0.533223450184, 0.632517397404, 0.549681544304, + 0.632532536983, 0.566216289997, 0.632551133633, 0.582756936550, + 0.632667422295, 0.599375545979, 0.632643520832, 0.615900576115, + 0.632630288601, 0.632430315018, 0.632595777512, 0.648932754993, + 0.632488071918, 0.665335118771, 0.632495641708, 0.681868970394, + 0.632503390312, 0.698405086994, 0.632511377335, 0.714943528175, + 0.632590711117, 0.731608748436, 0.632467389107, 0.747913897038, + 0.632425308228, 0.764350593090, 0.632357060909, 0.780721247196, + 0.632353067398, 0.797219336033, 0.632326960564, 0.813663959503, + 0.632273316383, 0.830033361912, 0.632273316383, 0.846530020237, + 0.632273316383, 0.863026678562, 0.632273316383, 0.879523336887, + 0.632273316383, 0.896019995213, 0.632273316383, 0.912516653538, + 0.632273316383, 0.929013311863, 0.632273316383, 0.945510029793, + 0.632273316383, 0.962006688118, 0.632273316383, 0.978503346443, + 0.632273316383, 0.995000004768, 0.648769974709, 0.005200000014, + 0.648769974709, 0.021696666256, 0.648769974709, 0.038193333894, + 0.648769974709, 0.054689999670, 0.648769974709, 0.071186669171, + 0.648769974709, 0.087683334947, 0.648769974709, 0.104180000722, + 0.648769974709, 0.120676666498, 0.648769974709, 0.137173339725, + 0.648769974709, 0.153669998050, 0.648769974709, 0.170166671276, + 0.648817241192, 0.186563611031, 0.648846030235, 0.203007981181, + 0.648854672909, 0.219496786594, 0.648873388767, 0.235969558358, + 0.648962020874, 0.252329915762, 0.649077236652, 0.268668740988, + 0.649157106876, 0.285084187984, 0.649034976959, 0.301786661148, + 0.649026572704, 0.318323045969, 0.649018526077, 0.334857225418, + 0.649010777473, 0.351389229298, 0.649132788181, 0.367804199457, + 0.649166882038, 0.384314626455, 0.649178504944, 0.400847673416, + 0.649200737476, 0.417377382517, 0.649219810963, 0.433913439512, + 0.649084746838, 0.450505077839, 0.649073362350, 0.467039257288, + 0.649066388607, 0.483570396900, 0.649064004421, 0.500100016594, + 0.649066388607, 0.516629576683, 0.649073362350, 0.533160746098, + 0.649084746838, 0.549694895744, 0.649219810963, 0.566286563873, + 0.649200737476, 0.582822620869, 0.649178504944, 0.599352300167, + 0.649166882038, 0.615885376930, 0.649132788181, 0.632395803928, + 0.649010777473, 0.648810744286, 0.649018526077, 0.665342807770, + 0.649026572704, 0.681876957417, 0.649034976959, 0.698413312435, + 0.649157106876, 0.715115845203, 0.649077236652, 0.731531262398, + 0.648962020874, 0.747870087624, 0.648873388767, 0.764230430126, + 0.648854672909, 0.780703246593, 0.648846030235, 0.797192037106, + 0.648817241192, 0.813636422157, 0.648769974709, 0.830033361912, + 0.648769974709, 0.846530020237, 0.648769974709, 0.863026678562, + 0.648769974709, 0.879523336887, 0.648769974709, 0.896019995213, + 0.648769974709, 0.912516653538, 0.648769974709, 0.929013311863, + 0.648769974709, 0.945510029793, 0.648769974709, 0.962006688118, + 0.648769974709, 0.978503346443, 0.648769974709, 0.995000004768, + 0.665266692638, 0.005200000014, 0.665266692638, 0.021696666256, + 0.665266692638, 0.038193333894, 0.665266692638, 0.054689999670, + 0.665266692638, 0.071186669171, 0.665266692638, 0.087683334947, + 0.665266692638, 0.104180000722, 0.665266692638, 0.120676666498, + 0.665266692638, 0.137173339725, 0.665266692638, 0.153669998050, + 0.665266692638, 0.170166671276, 0.665266692638, 0.186663329601, + 0.665334522724, 0.203037843108, 0.665365397930, 0.219488814473, + 0.665369451046, 0.235988914967, 0.665448367596, 0.252377480268, + 0.665497124195, 0.268824011087, 0.665633857250, 0.285165965557, + 0.665717124939, 0.301599413157, 0.665559530258, 0.318314522505, + 0.665550947189, 0.334849059582, 0.665542781353, 0.351381480694, + 0.665535092354, 0.367911905050, 0.665659964085, 0.384348005056, + 0.665692269802, 0.400864630938, 0.665703296661, 0.417398363352, + 0.665722727776, 0.433930903673, 0.665738403797, 0.450468480587, + 0.665749847889, 0.467010021210, 0.665756881237, 0.483554303646, + 0.665759265423, 0.500100016594, 0.665756881237, 0.516645669937, + 0.665749847889, 0.533189952374, 0.665738403797, 0.549731492996, + 0.665722727776, 0.566269099712, 0.665703296661, 0.582801640034, + 0.665692269802, 0.599335372448, 0.665659964085, 0.615851998329, + 0.665535092354, 0.632288098335, 0.665542781353, 0.648818492889, + 0.665550947189, 0.665350973606, 0.665559530258, 0.681885480881, + 0.665717124939, 0.698600590229, 0.665633857250, 0.715034008026, + 0.665497124195, 0.731375992298, 0.665448367596, 0.747822523117, + 0.665369451046, 0.764211058617, 0.665365397930, 0.780711174011, + 0.665334522724, 0.797162175179, 0.665266692638, 0.813536643982, + 0.665266692638, 0.830033361912, 0.665266692638, 0.846530020237, + 0.665266692638, 0.863026678562, 0.665266692638, 0.879523336887, + 0.665266692638, 0.896019995213, 0.665266692638, 0.912516653538, + 0.665266692638, 0.929013311863, 0.665266692638, 0.945510029793, + 0.665266692638, 0.962006688118, 0.665266692638, 0.978503346443, + 0.665266692638, 0.995000004768, 0.681763350964, 0.005200000014, + 0.681763350964, 0.021696666256, 0.681763350964, 0.038193333894, + 0.681763350964, 0.054689999670, 0.681763350964, 0.071186669171, + 0.681763350964, 0.087683334947, 0.681763350964, 0.104180000722, + 0.681763350964, 0.120676666498, 0.681763350964, 0.137173339725, + 0.681763350964, 0.153669998050, 0.681763350964, 0.170166671276, + 0.681763350964, 0.186663329601, 0.681763350964, 0.203160002828, + 0.681851387024, 0.219520568848, 0.681862056255, 0.236009716988, + 0.681883335114, 0.252486377954, 0.681977748871, 0.268873780966, + 0.682028293610, 0.285330235958, 0.682181596756, 0.301683694124, + 0.682266414165, 0.318133622408, 0.682085454464, 0.334840476513, + 0.682076931000, 0.351373404264, 0.682069003582, 0.367904365063, + 0.682061672211, 0.384433507919, 0.682174921036, 0.400895506144, + 0.682204008102, 0.417416363955, 0.682228684425, 0.433944106102, + 0.682248413563, 0.450477689505, 0.682243645191, 0.467019349337, + 0.682250678539, 0.483559042215, 0.682253062725, 0.500100016594, + 0.682250678539, 0.516640961170, 0.682243645191, 0.533180654049, + 0.682248413563, 0.549722313881, 0.682228684425, 0.566255867481, + 0.682204008102, 0.582783639431, 0.682174921036, 0.599304497242, + 0.682061672211, 0.615766525269, 0.682069003582, 0.632295608521, + 0.682076931000, 0.648826599121, 0.682085454464, 0.665359497070, + 0.682266414165, 0.682066380978, 0.682181596756, 0.698516309261, + 0.682028293610, 0.714869797230, 0.681977748871, 0.731326222420, + 0.681883335114, 0.747713625431, 0.681862056255, 0.764190256596, + 0.681851387024, 0.780679404736, 0.681763350964, 0.797039985657, + 0.681763350964, 0.813536643982, 0.681763350964, 0.830033361912, + 0.681763350964, 0.846530020237, 0.681763350964, 0.863026678562, + 0.681763350964, 0.879523336887, 0.681763350964, 0.896019995213, + 0.681763350964, 0.912516653538, 0.681763350964, 0.929013311863, + 0.681763350964, 0.945510029793, 0.681763350964, 0.962006688118, + 0.681763350964, 0.978503346443, 0.681763350964, 0.995000004768, + 0.698260009289, 0.005200000014, 0.698260009289, 0.021696666256, + 0.698260009289, 0.038193333894, 0.698260009289, 0.054689999670, + 0.698260009289, 0.071186669171, 0.698260009289, 0.087683334947, + 0.698260009289, 0.104180000722, 0.698260009289, 0.120676666498, + 0.698260009289, 0.137173339725, 0.698260009289, 0.153669998050, + 0.698260009289, 0.170166671276, 0.698260009289, 0.186663329601, + 0.698260009289, 0.203160002828, 0.698332190514, 0.219554439187, + 0.698366641998, 0.236011117697, 0.698373615742, 0.252508014441, + 0.698395490646, 0.268988579512, 0.698501944542, 0.285381257534, + 0.698553204536, 0.301846802235, 0.698716282845, 0.318218380213, + 0.698800563812, 0.334682852030, 0.698613345623, 0.351365000010, + 0.698605120182, 0.367896586657, 0.698597609997, 0.384426414967, + 0.698590815067, 0.400954604149, 0.698584914207, 0.417481303215, + 0.698700487614, 0.433966487646, 0.698720395565, 0.450494885445, + 0.698734879494, 0.467027515173, 0.698743700981, 0.483563035727, + 0.698746681213, 0.500100016594, 0.698743700981, 0.516636967659, + 0.698734879494, 0.533172488213, 0.698720395565, 0.549705088139, + 0.698700487614, 0.566233515739, 0.698584914207, 0.582718729973, + 0.698590815067, 0.599245429039, 0.698597609997, 0.615773618221, + 0.698605120182, 0.632303416729, 0.698613345623, 0.648835003376, + 0.698800563812, 0.665517151356, 0.698716282845, 0.681981623173, + 0.698553204536, 0.698353230953, 0.698501944542, 0.714818716049, + 0.698395490646, 0.731211423874, 0.698373615742, 0.747691988945, + 0.698366641998, 0.764188885689, 0.698332190514, 0.780645549297, + 0.698260009289, 0.797039985657, 0.698260009289, 0.813536643982, + 0.698260009289, 0.830033361912, 0.698260009289, 0.846530020237, + 0.698260009289, 0.863026678562, 0.698260009289, 0.879523336887, + 0.698260009289, 0.896019995213, 0.698260009289, 0.912516653538, + 0.698260009289, 0.929013311863, 0.698260009289, 0.945510029793, + 0.698260009289, 0.962006688118, 0.698260009289, 0.978503346443, + 0.698260009289, 0.995000004768, 0.714756667614, 0.005200000014, + 0.714756667614, 0.021696666256, 0.714756667614, 0.038193333894, + 0.714756667614, 0.054689999670, 0.714756667614, 0.071186669171, + 0.714756667614, 0.087683334947, 0.714756667614, 0.104180000722, + 0.714756667614, 0.120676666498, 0.714756667614, 0.137173339725, + 0.714756667614, 0.153669998050, 0.714756667614, 0.170166671276, + 0.714756667614, 0.186663329601, 0.714756667614, 0.203160002828, + 0.714756667614, 0.219656661153, 0.714843750000, 0.236046120524, + 0.714879155159, 0.252508640289, 0.714882969856, 0.269010663033, + 0.714905142784, 0.285494863987, 0.715018749237, 0.301898092031, + 0.715069770813, 0.318371742964, 0.715234041214, 0.334766119719, + 0.715315818787, 0.351242899895, 0.715143501759, 0.367888599634, + 0.715135812759, 0.384419173002, 0.715128958225, 0.400948196650, + 0.715122938156, 0.417475789785, 0.715117871761, 0.434002190828, + 0.715113878250, 0.450527548790, 0.715110957623, 0.467052161694, + 0.715109229088, 0.483576208353, 0.715108633041, 0.500100016594, + 0.715109229088, 0.516623795033, 0.715110957623, 0.533147871494, + 0.715113878250, 0.549672424793, 0.715117871761, 0.566197812557, + 0.715122938156, 0.582724213600, 0.715128958225, 0.599251806736, + 0.715135812759, 0.615780830383, 0.715143501759, 0.632311403751, + 0.715315818787, 0.648957133293, 0.715234041214, 0.665433883667, + 0.715069770813, 0.681828260422, 0.715018749237, 0.698301911354, + 0.714905142784, 0.714705169201, 0.714882969856, 0.731189310551, + 0.714879155159, 0.747691392899, 0.714843750000, 0.764153897762, + 0.714756667614, 0.780543327332, 0.714756667614, 0.797039985657, + 0.714756667614, 0.813536643982, 0.714756667614, 0.830033361912, + 0.714756667614, 0.846530020237, 0.714756667614, 0.863026678562, + 0.714756667614, 0.879523336887, 0.714756667614, 0.896019995213, + 0.714756667614, 0.912516653538, 0.714756667614, 0.929013311863, + 0.714756667614, 0.945510029793, 0.714756667614, 0.962006688118, + 0.714756667614, 0.978503346443, 0.714756667614, 0.995000004768, + 0.731253325939, 0.005200000014, 0.731253325939, 0.021696666256, + 0.731253325939, 0.038193333894, 0.731253325939, 0.054689999670, + 0.731253325939, 0.071186669171, 0.731253325939, 0.087683334947, + 0.731253325939, 0.104180000722, 0.731253325939, 0.120676666498, + 0.731253325939, 0.137173339725, 0.731253325939, 0.153669998050, + 0.731253325939, 0.170166671276, 0.731253325939, 0.186663329601, + 0.731253325939, 0.203160002828, 0.731253325939, 0.219656661153, + 0.731253325939, 0.236153334379, 0.731351971626, 0.252544313669, + 0.731387794018, 0.269012212753, 0.731389343739, 0.285517036915, + 0.731411457062, 0.302004486322, 0.731526196003, 0.318422257900, + 0.731575965881, 0.334902882576, 0.731731235981, 0.351322770119, + 0.731808722019, 0.367809295654, 0.731879234314, 0.384310394526, + 0.731669425964, 0.400941699743, 0.731663346291, 0.417470246553, + 0.731658279896, 0.433997631073, 0.731654226780, 0.450524091721, + 0.731651306152, 0.467049807310, 0.731649577618, 0.483575046062, + 0.731648981571, 0.500100016594, 0.731649577618, 0.516624987125, + 0.731651306152, 0.533150196075, 0.731654226780, 0.549675881863, + 0.731658279896, 0.566202342510, 0.731663346291, 0.582729756832, + 0.731669425964, 0.599258303642, 0.731879234314, 0.615889608860, + 0.731808722019, 0.632390737534, 0.731731235981, 0.648877263069, + 0.731575965881, 0.665297150612, 0.731526196003, 0.681777715683, + 0.731411457062, 0.698195517063, 0.731389343739, 0.714682936668, + 0.731387794018, 0.731187760830, 0.731351971626, 0.747655689716, + 0.731253325939, 0.764046669006, 0.731253325939, 0.780543327332, + 0.731253325939, 0.797039985657, 0.731253325939, 0.813536643982, + 0.731253325939, 0.830033361912, 0.731253325939, 0.846530020237, + 0.731253325939, 0.863026678562, 0.731253325939, 0.879523336887, + 0.731253325939, 0.896019995213, 0.731253325939, 0.912516653538, + 0.731253325939, 0.929013311863, 0.731253325939, 0.945510029793, + 0.731253325939, 0.962006688118, 0.731253325939, 0.978503346443, + 0.731253325939, 0.995000004768, 0.747749984264, 0.005200000014, + 0.747749984264, 0.021696666256, 0.747749984264, 0.038193333894, + 0.747749984264, 0.054689999670, 0.747749984264, 0.071186669171, + 0.747749984264, 0.087683334947, 0.747749984264, 0.104180000722, + 0.747749984264, 0.120676666498, 0.747749984264, 0.137173339725, + 0.747749984264, 0.153669998050, 0.747749984264, 0.170166671276, + 0.747749984264, 0.186663329601, 0.747749984264, 0.203160002828, + 0.747749984264, 0.219656661153, 0.747749984264, 0.236153334379, + 0.747749984264, 0.252649992704, 0.747855663300, 0.269048035145, + 0.747891366482, 0.285520821810, 0.747892022133, 0.302026391029, + 0.747913599014, 0.318516671658, 0.748022556305, 0.334951639175, + 0.748070061207, 0.351437956095, 0.748113870621, 0.367932587862, + 0.748276531696, 0.384377628565, 0.748340129852, 0.400883942842, + 0.748395204544, 0.417401611805, 0.748441159725, 0.433929026127, + 0.748196959496, 0.450520604849, 0.748194038868, 0.467047452927, + 0.748192250729, 0.483573853970, 0.748191654682, 0.500100016594, + 0.748192250729, 0.516626179218, 0.748194038868, 0.533152520657, + 0.748196959496, 0.549679398537, 0.748441159725, 0.566270947456, + 0.748395204544, 0.582798421383, 0.748340129852, 0.599316060543, + 0.748276531696, 0.615822374821, 0.748113870621, 0.632267415524, + 0.748070061207, 0.648762047291, 0.748022556305, 0.665248334408, + 0.747913599014, 0.681683301926, 0.747892022133, 0.698173582554, + 0.747891366482, 0.714679181576, 0.747855663300, 0.731151998043, + 0.747749984264, 0.747550010681, 0.747749984264, 0.764046669006, + 0.747749984264, 0.780543327332, 0.747749984264, 0.797039985657, + 0.747749984264, 0.813536643982, 0.747749984264, 0.830033361912, + 0.747749984264, 0.846530020237, 0.747749984264, 0.863026678562, + 0.747749984264, 0.879523336887, 0.747749984264, 0.896019995213, + 0.747749984264, 0.912516653538, 0.747749984264, 0.929013311863, + 0.747749984264, 0.945510029793, 0.747749984264, 0.962006688118, + 0.747749984264, 0.978503346443, 0.747749984264, 0.995000004768, + 0.764246642590, 0.005200000014, 0.764246642590, 0.021696666256, + 0.764246642590, 0.038193333894, 0.764246642590, 0.054689999670, + 0.764246642590, 0.071186669171, 0.764246642590, 0.087683334947, + 0.764246642590, 0.104180000722, 0.764246642590, 0.120676666498, + 0.764246642590, 0.137173339725, 0.764246642590, 0.153669998050, + 0.764246642590, 0.170166671276, 0.764246642590, 0.186663329601, + 0.764246642590, 0.203160002828, 0.764246642590, 0.219656661153, + 0.764246642590, 0.236153334379, 0.764246642590, 0.252649992704, + 0.764246642590, 0.269146680832, 0.764353871346, 0.285556226969, + 0.764388859272, 0.302033334970, 0.764390289783, 0.318537920713, + 0.764411091805, 0.335030585527, 0.764430463314, 0.351526618004, + 0.764550566673, 0.367974728346, 0.764590799809, 0.384472787380, + 0.764626443386, 0.400977581739, 0.764772415161, 0.417452365160, + 0.764818608761, 0.433970332146, 0.764855086803, 0.450495928526, + 0.764881432056, 0.467027336359, 0.764897346497, 0.483562678099, + 0.764902651310, 0.500100016594, 0.764897346497, 0.516637325287, + 0.764881432056, 0.533172667027, 0.764855086803, 0.549704074860, + 0.764818608761, 0.566229641438, 0.764772415161, 0.582747638226, + 0.764626443386, 0.599222421646, 0.764590799809, 0.615727245808, + 0.764550566673, 0.632225275040, 0.764430463314, 0.648673355579, + 0.764411091805, 0.665169417858, 0.764390289783, 0.681662082672, + 0.764388859272, 0.698166668415, 0.764353871346, 0.714643776417, + 0.764246642590, 0.731053352356, 0.764246642590, 0.747550010681, + 0.764246642590, 0.764046669006, 0.764246642590, 0.780543327332, + 0.764246642590, 0.797039985657, 0.764246642590, 0.813536643982, + 0.764246642590, 0.830033361912, 0.764246642590, 0.846530020237, + 0.764246642590, 0.863026678562, 0.764246642590, 0.879523336887, + 0.764246642590, 0.896019995213, 0.764246642590, 0.912516653538, + 0.764246642590, 0.929013311863, 0.764246642590, 0.945510029793, + 0.764246642590, 0.962006688118, 0.764246642590, 0.978503346443, + 0.764246642590, 0.995000004768, 0.780743360519, 0.005200000014, + 0.780743360519, 0.021696666256, 0.780743360519, 0.038193333894, + 0.780743360519, 0.054689999670, 0.780743360519, 0.071186669171, + 0.780743360519, 0.087683334947, 0.780743360519, 0.104180000722, + 0.780743360519, 0.120676666498, 0.780743360519, 0.137173339725, + 0.780743360519, 0.153669998050, 0.780743360519, 0.170166671276, + 0.780743360519, 0.186663329601, 0.780743360519, 0.203160002828, + 0.780743360519, 0.219656661153, 0.780743360519, 0.236153334379, + 0.780743360519, 0.252649992704, 0.780743360519, 0.269146680832, + 0.780743360519, 0.285643339157, 0.780845582485, 0.302067846060, + 0.780879437923, 0.318548619747, 0.780911207199, 0.335034608841, + 0.780903220177, 0.351545363665, 0.780921220779, 0.368042945862, + 0.780937492847, 0.384543389082, 0.781052231789, 0.401010990143, + 0.781083226204, 0.417516708374, 0.781108975410, 0.434027314186, + 0.781129300594, 0.450541883707, 0.781143903732, 0.467059522867, + 0.781152784824, 0.483579248190, 0.781155765057, 0.500100016594, + 0.781152784824, 0.516620755196, 0.781143903732, 0.533140480518, + 0.781129300594, 0.549658119678, 0.781108975410, 0.566172719002, + 0.781083226204, 0.582683265209, 0.781052231789, 0.599188983440, + 0.780937492847, 0.615656614304, 0.780921220779, 0.632157027721, + 0.780903220177, 0.648654639721, 0.780911207199, 0.665165424347, + 0.780879437923, 0.681651413441, 0.780845582485, 0.698132157326, + 0.780743360519, 0.714556694031, 0.780743360519, 0.731053352356, + 0.780743360519, 0.747550010681, 0.780743360519, 0.764046669006, + 0.780743360519, 0.780543327332, 0.780743360519, 0.797039985657, + 0.780743360519, 0.813536643982, 0.780743360519, 0.830033361912, + 0.780743360519, 0.846530020237, 0.780743360519, 0.863026678562, + 0.780743360519, 0.879523336887, 0.780743360519, 0.896019995213, + 0.780743360519, 0.912516653538, 0.780743360519, 0.929013311863, + 0.780743360519, 0.945510029793, 0.780743360519, 0.962006688118, + 0.780743360519, 0.978503346443, 0.780743360519, 0.995000004768, + 0.797240018845, 0.005200000014, 0.797240018845, 0.021696666256, + 0.797240018845, 0.038193333894, 0.797240018845, 0.054689999670, + 0.797240018845, 0.071186669171, 0.797240018845, 0.087683334947, + 0.797240018845, 0.104180000722, 0.797240018845, 0.120676666498, + 0.797240018845, 0.137173339725, 0.797240018845, 0.153669998050, + 0.797240018845, 0.170166671276, 0.797240018845, 0.186663329601, + 0.797240018845, 0.203160002828, 0.797240018845, 0.219656661153, + 0.797240018845, 0.236153334379, 0.797240018845, 0.252649992704, + 0.797240018845, 0.269146680832, 0.797240018845, 0.285643339157, + 0.797240018845, 0.302139997482, 0.797240018845, 0.318636655807, + 0.797362148762, 0.335065454245, 0.797392010689, 0.351553976536, + 0.797419369221, 0.368046969175, 0.797405958176, 0.384558796883, + 0.797420442104, 0.401059836149, 0.797432899475, 0.417563080788, + 0.797443270683, 0.434068173170, 0.797543525696, 0.450559407473, + 0.797558188438, 0.467071324587, 0.797567009926, 0.483585178852, + 0.797569990158, 0.500100016594, 0.797567009926, 0.516614854336, + 0.797558188438, 0.533128678799, 0.797543525696, 0.549640595913, + 0.797443270683, 0.566131830215, 0.797432899475, 0.582636892796, + 0.797420442104, 0.599140167236, 0.797405958176, 0.615641236305, + 0.797419369221, 0.632153034210, 0.797392010689, 0.648645997047, + 0.797362148762, 0.665134549141, 0.797240018845, 0.681563317776, + 0.797240018845, 0.698059976101, 0.797240018845, 0.714556694031, + 0.797240018845, 0.731053352356, 0.797240018845, 0.747550010681, + 0.797240018845, 0.764046669006, 0.797240018845, 0.780543327332, + 0.797240018845, 0.797039985657, 0.797240018845, 0.813536643982, + 0.797240018845, 0.830033361912, 0.797240018845, 0.846530020237, + 0.797240018845, 0.863026678562, 0.797240018845, 0.879523336887, + 0.797240018845, 0.896019995213, 0.797240018845, 0.912516653538, + 0.797240018845, 0.929013311863, 0.797240018845, 0.945510029793, + 0.797240018845, 0.962006688118, 0.797240018845, 0.978503346443, + 0.797240018845, 0.995000004768, 0.813736677170, 0.005200000014, + 0.813736677170, 0.021696666256, 0.813736677170, 0.038193333894, + 0.813736677170, 0.054689999670, 0.813736677170, 0.071186669171, + 0.813736677170, 0.087683334947, 0.813736677170, 0.104180000722, + 0.813736677170, 0.120676666498, 0.813736677170, 0.137173339725, + 0.813736677170, 0.153669998050, 0.813736677170, 0.170166671276, + 0.813736677170, 0.186663329601, 0.813736677170, 0.203160002828, + 0.813736677170, 0.219656661153, 0.813736677170, 0.236153334379, + 0.813736677170, 0.252649992704, 0.813736677170, 0.269146680832, + 0.813736677170, 0.285643339157, 0.813736677170, 0.302139997482, + 0.813736677170, 0.318636655807, 0.813736677170, 0.335133343935, + 0.813836395741, 0.351582765579, 0.813863992691, 0.368073076010, + 0.813888788223, 0.384567290545, 0.813910603523, 0.401065051556, + 0.813896834850, 0.417574524879, 0.813907206059, 0.434077441692, + 0.813915371895, 0.450581789017, 0.813921213150, 0.467087239027, + 0.813924789429, 0.483593434095, 0.813925981522, 0.500100016594, + 0.813924789429, 0.516606569290, 0.813921213150, 0.533112764359, + 0.813915371895, 0.549618244171, 0.813907206059, 0.566122591496, + 0.813896834850, 0.582625508308, 0.813910603523, 0.599134922028, + 0.813888788223, 0.615632712841, 0.813863992691, 0.632126927376, + 0.813836395741, 0.648617267609, 0.813736677170, 0.665066659451, + 0.813736677170, 0.681563317776, 0.813736677170, 0.698059976101, + 0.813736677170, 0.714556694031, 0.813736677170, 0.731053352356, + 0.813736677170, 0.747550010681, 0.813736677170, 0.764046669006, + 0.813736677170, 0.780543327332, 0.813736677170, 0.797039985657, + 0.813736677170, 0.813536643982, 0.813736677170, 0.830033361912, + 0.813736677170, 0.846530020237, 0.813736677170, 0.863026678562, + 0.813736677170, 0.879523336887, 0.813736677170, 0.896019995213, + 0.813736677170, 0.912516653538, 0.813736677170, 0.929013311863, + 0.813736677170, 0.945510029793, 0.813736677170, 0.962006688118, + 0.813736677170, 0.978503346443, 0.813736677170, 0.995000004768, + 0.830233335495, 0.005200000014, 0.830233335495, 0.021696666256, + 0.830233335495, 0.038193333894, 0.830233335495, 0.054689999670, + 0.830233335495, 0.071186669171, 0.830233335495, 0.087683334947, + 0.830233335495, 0.104180000722, 0.830233335495, 0.120676666498, + 0.830233335495, 0.137173339725, 0.830233335495, 0.153669998050, + 0.830233335495, 0.170166671276, 0.830233335495, 0.186663329601, + 0.830233335495, 0.203160002828, 0.830233335495, 0.219656661153, + 0.830233335495, 0.236153334379, 0.830233335495, 0.252649992704, + 0.830233335495, 0.269146680832, 0.830233335495, 0.285643339157, + 0.830233335495, 0.302139997482, 0.830233335495, 0.318636655807, + 0.830233335495, 0.335133343935, 0.830233335495, 0.351630002260, + 0.830233335495, 0.368126660585, 0.830233335495, 0.384623318911, + 0.830348491669, 0.401085466146, 0.830367326736, 0.417583167553, + 0.830382943153, 0.434083402157, 0.830395221710, 0.450585722923, + 0.830404043198, 0.467089593410, 0.830409348011, 0.483594536781, + 0.830411136150, 0.500100016594, 0.830409348011, 0.516605496407, + 0.830404043198, 0.533110380173, 0.830395221710, 0.549614250660, + 0.830382943153, 0.566116571426, 0.830367326736, 0.582616806030, + 0.830348491669, 0.599114537239, 0.830233335495, 0.615576684475, + 0.830233335495, 0.632073342800, 0.830233335495, 0.648570001125, + 0.830233335495, 0.665066659451, 0.830233335495, 0.681563317776, + 0.830233335495, 0.698059976101, 0.830233335495, 0.714556694031, + 0.830233335495, 0.731053352356, 0.830233335495, 0.747550010681, + 0.830233335495, 0.764046669006, 0.830233335495, 0.780543327332, + 0.830233335495, 0.797039985657, 0.830233335495, 0.813536643982, + 0.830233335495, 0.830033361912, 0.830233335495, 0.846530020237, + 0.830233335495, 0.863026678562, 0.830233335495, 0.879523336887, + 0.830233335495, 0.896019995213, 0.830233335495, 0.912516653538, + 0.830233335495, 0.929013311863, 0.830233335495, 0.945510029793, + 0.830233335495, 0.962006688118, 0.830233335495, 0.978503346443, + 0.830233335495, 0.995000004768, 0.846729993820, 0.005200000014, + 0.846729993820, 0.021696666256, 0.846729993820, 0.038193333894, + 0.846729993820, 0.054689999670, 0.846729993820, 0.071186669171, + 0.846729993820, 0.087683334947, 0.846729993820, 0.104180000722, + 0.846729993820, 0.120676666498, 0.846729993820, 0.137173339725, + 0.846729993820, 0.153669998050, 0.846729993820, 0.170166671276, + 0.846729993820, 0.186663329601, 0.846729993820, 0.203160002828, + 0.846729993820, 0.219656661153, 0.846729993820, 0.236153334379, + 0.846729993820, 0.252649992704, 0.846729993820, 0.269146680832, + 0.846729993820, 0.285643339157, 0.846729993820, 0.302139997482, + 0.846729993820, 0.318636655807, 0.846729993820, 0.335133343935, + 0.846729993820, 0.351630002260, 0.846729993820, 0.368126660585, + 0.846729993820, 0.384623318911, 0.846729993820, 0.401120007038, + 0.846729993820, 0.417616665363, 0.846729993820, 0.434113323689, + 0.846729993820, 0.450610011816, 0.846729993820, 0.467106670141, + 0.846840202808, 0.483598083258, 0.846841990948, 0.500100016594, + 0.846840202808, 0.516601920128, 0.846729993820, 0.533093333244, + 0.846729993820, 0.549589991570, 0.846729993820, 0.566086649895, + 0.846729993820, 0.582583308220, 0.846729993820, 0.599080026150, + 0.846729993820, 0.615576684475, 0.846729993820, 0.632073342800, + 0.846729993820, 0.648570001125, 0.846729993820, 0.665066659451, + 0.846729993820, 0.681563317776, 0.846729993820, 0.698059976101, + 0.846729993820, 0.714556694031, 0.846729993820, 0.731053352356, + 0.846729993820, 0.747550010681, 0.846729993820, 0.764046669006, + 0.846729993820, 0.780543327332, 0.846729993820, 0.797039985657, + 0.846729993820, 0.813536643982, 0.846729993820, 0.830033361912, + 0.846729993820, 0.846530020237, 0.846729993820, 0.863026678562, + 0.846729993820, 0.879523336887, 0.846729993820, 0.896019995213, + 0.846729993820, 0.912516653538, 0.846729993820, 0.929013311863, + 0.846729993820, 0.945510029793, 0.846729993820, 0.962006688118, + 0.846729993820, 0.978503346443, 0.846729993820, 0.995000004768, + 0.863226652145, 0.005200000014, 0.863226652145, 0.021696666256, + 0.863226652145, 0.038193333894, 0.863226652145, 0.054689999670, + 0.863226652145, 0.071186669171, 0.863226652145, 0.087683334947, + 0.863226652145, 0.104180000722, 0.863226652145, 0.120676666498, + 0.863226652145, 0.137173339725, 0.863226652145, 0.153669998050, + 0.863226652145, 0.170166671276, 0.863226652145, 0.186663329601, + 0.863226652145, 0.203160002828, 0.863226652145, 0.219656661153, + 0.863226652145, 0.236153334379, 0.863226652145, 0.252649992704, + 0.863226652145, 0.269146680832, 0.863226652145, 0.285643339157, + 0.863226652145, 0.302139997482, 0.863226652145, 0.318636655807, + 0.863226652145, 0.335133343935, 0.863226652145, 0.351630002260, + 0.863226652145, 0.368126660585, 0.863226652145, 0.384623318911, + 0.863226652145, 0.401120007038, 0.863226652145, 0.417616665363, + 0.863226652145, 0.434113323689, 0.863226652145, 0.450610011816, + 0.863226652145, 0.467106670141, 0.863226652145, 0.483603328466, + 0.863226652145, 0.500100016594, 0.863226652145, 0.516596674919, + 0.863226652145, 0.533093333244, 0.863226652145, 0.549589991570, + 0.863226652145, 0.566086649895, 0.863226652145, 0.582583308220, + 0.863226652145, 0.599080026150, 0.863226652145, 0.615576684475, + 0.863226652145, 0.632073342800, 0.863226652145, 0.648570001125, + 0.863226652145, 0.665066659451, 0.863226652145, 0.681563317776, + 0.863226652145, 0.698059976101, 0.863226652145, 0.714556694031, + 0.863226652145, 0.731053352356, 0.863226652145, 0.747550010681, + 0.863226652145, 0.764046669006, 0.863226652145, 0.780543327332, + 0.863226652145, 0.797039985657, 0.863226652145, 0.813536643982, + 0.863226652145, 0.830033361912, 0.863226652145, 0.846530020237, + 0.863226652145, 0.863026678562, 0.863226652145, 0.879523336887, + 0.863226652145, 0.896019995213, 0.863226652145, 0.912516653538, + 0.863226652145, 0.929013311863, 0.863226652145, 0.945510029793, + 0.863226652145, 0.962006688118, 0.863226652145, 0.978503346443, + 0.863226652145, 0.995000004768, 0.879723310471, 0.005200000014, + 0.879723310471, 0.021696666256, 0.879723310471, 0.038193333894, + 0.879723310471, 0.054689999670, 0.879723310471, 0.071186669171, + 0.879723310471, 0.087683334947, 0.879723310471, 0.104180000722, + 0.879723310471, 0.120676666498, 0.879723310471, 0.137173339725, + 0.879723310471, 0.153669998050, 0.879723310471, 0.170166671276, + 0.879723310471, 0.186663329601, 0.879723310471, 0.203160002828, + 0.879723310471, 0.219656661153, 0.879723310471, 0.236153334379, + 0.879723310471, 0.252649992704, 0.879723310471, 0.269146680832, + 0.879723310471, 0.285643339157, 0.879723310471, 0.302139997482, + 0.879723310471, 0.318636655807, 0.879723310471, 0.335133343935, + 0.879723310471, 0.351630002260, 0.879723310471, 0.368126660585, + 0.879723310471, 0.384623318911, 0.879723310471, 0.401120007038, + 0.879723310471, 0.417616665363, 0.879723310471, 0.434113323689, + 0.879723310471, 0.450610011816, 0.879723310471, 0.467106670141, + 0.879723310471, 0.483603328466, 0.879723310471, 0.500100016594, + 0.879723310471, 0.516596674919, 0.879723310471, 0.533093333244, + 0.879723310471, 0.549589991570, 0.879723310471, 0.566086649895, + 0.879723310471, 0.582583308220, 0.879723310471, 0.599080026150, + 0.879723310471, 0.615576684475, 0.879723310471, 0.632073342800, + 0.879723310471, 0.648570001125, 0.879723310471, 0.665066659451, + 0.879723310471, 0.681563317776, 0.879723310471, 0.698059976101, + 0.879723310471, 0.714556694031, 0.879723310471, 0.731053352356, + 0.879723310471, 0.747550010681, 0.879723310471, 0.764046669006, + 0.879723310471, 0.780543327332, 0.879723310471, 0.797039985657, + 0.879723310471, 0.813536643982, 0.879723310471, 0.830033361912, + 0.879723310471, 0.846530020237, 0.879723310471, 0.863026678562, + 0.879723310471, 0.879523336887, 0.879723310471, 0.896019995213, + 0.879723310471, 0.912516653538, 0.879723310471, 0.929013311863, + 0.879723310471, 0.945510029793, 0.879723310471, 0.962006688118, + 0.879723310471, 0.978503346443, 0.879723310471, 0.995000004768, + 0.896220028400, 0.005200000014, 0.896220028400, 0.021696666256, + 0.896220028400, 0.038193333894, 0.896220028400, 0.054689999670, + 0.896220028400, 0.071186669171, 0.896220028400, 0.087683334947, + 0.896220028400, 0.104180000722, 0.896220028400, 0.120676666498, + 0.896220028400, 0.137173339725, 0.896220028400, 0.153669998050, + 0.896220028400, 0.170166671276, 0.896220028400, 0.186663329601, + 0.896220028400, 0.203160002828, 0.896220028400, 0.219656661153, + 0.896220028400, 0.236153334379, 0.896220028400, 0.252649992704, + 0.896220028400, 0.269146680832, 0.896220028400, 0.285643339157, + 0.896220028400, 0.302139997482, 0.896220028400, 0.318636655807, + 0.896220028400, 0.335133343935, 0.896220028400, 0.351630002260, + 0.896220028400, 0.368126660585, 0.896220028400, 0.384623318911, + 0.896220028400, 0.401120007038, 0.896220028400, 0.417616665363, + 0.896220028400, 0.434113323689, 0.896220028400, 0.450610011816, + 0.896220028400, 0.467106670141, 0.896220028400, 0.483603328466, + 0.896220028400, 0.500100016594, 0.896220028400, 0.516596674919, + 0.896220028400, 0.533093333244, 0.896220028400, 0.549589991570, + 0.896220028400, 0.566086649895, 0.896220028400, 0.582583308220, + 0.896220028400, 0.599080026150, 0.896220028400, 0.615576684475, + 0.896220028400, 0.632073342800, 0.896220028400, 0.648570001125, + 0.896220028400, 0.665066659451, 0.896220028400, 0.681563317776, + 0.896220028400, 0.698059976101, 0.896220028400, 0.714556694031, + 0.896220028400, 0.731053352356, 0.896220028400, 0.747550010681, + 0.896220028400, 0.764046669006, 0.896220028400, 0.780543327332, + 0.896220028400, 0.797039985657, 0.896220028400, 0.813536643982, + 0.896220028400, 0.830033361912, 0.896220028400, 0.846530020237, + 0.896220028400, 0.863026678562, 0.896220028400, 0.879523336887, + 0.896220028400, 0.896019995213, 0.896220028400, 0.912516653538, + 0.896220028400, 0.929013311863, 0.896220028400, 0.945510029793, + 0.896220028400, 0.962006688118, 0.896220028400, 0.978503346443, + 0.896220028400, 0.995000004768, 0.912716686726, 0.005200000014, + 0.912716686726, 0.021696666256, 0.912716686726, 0.038193333894, + 0.912716686726, 0.054689999670, 0.912716686726, 0.071186669171, + 0.912716686726, 0.087683334947, 0.912716686726, 0.104180000722, + 0.912716686726, 0.120676666498, 0.912716686726, 0.137173339725, + 0.912716686726, 0.153669998050, 0.912716686726, 0.170166671276, + 0.912716686726, 0.186663329601, 0.912716686726, 0.203160002828, + 0.912716686726, 0.219656661153, 0.912716686726, 0.236153334379, + 0.912716686726, 0.252649992704, 0.912716686726, 0.269146680832, + 0.912716686726, 0.285643339157, 0.912716686726, 0.302139997482, + 0.912716686726, 0.318636655807, 0.912716686726, 0.335133343935, + 0.912716686726, 0.351630002260, 0.912716686726, 0.368126660585, + 0.912716686726, 0.384623318911, 0.912716686726, 0.401120007038, + 0.912716686726, 0.417616665363, 0.912716686726, 0.434113323689, + 0.912716686726, 0.450610011816, 0.912716686726, 0.467106670141, + 0.912716686726, 0.483603328466, 0.912716686726, 0.500100016594, + 0.912716686726, 0.516596674919, 0.912716686726, 0.533093333244, + 0.912716686726, 0.549589991570, 0.912716686726, 0.566086649895, + 0.912716686726, 0.582583308220, 0.912716686726, 0.599080026150, + 0.912716686726, 0.615576684475, 0.912716686726, 0.632073342800, + 0.912716686726, 0.648570001125, 0.912716686726, 0.665066659451, + 0.912716686726, 0.681563317776, 0.912716686726, 0.698059976101, + 0.912716686726, 0.714556694031, 0.912716686726, 0.731053352356, + 0.912716686726, 0.747550010681, 0.912716686726, 0.764046669006, + 0.912716686726, 0.780543327332, 0.912716686726, 0.797039985657, + 0.912716686726, 0.813536643982, 0.912716686726, 0.830033361912, + 0.912716686726, 0.846530020237, 0.912716686726, 0.863026678562, + 0.912716686726, 0.879523336887, 0.912716686726, 0.896019995213, + 0.912716686726, 0.912516653538, 0.912716686726, 0.929013311863, + 0.912716686726, 0.945510029793, 0.912716686726, 0.962006688118, + 0.912716686726, 0.978503346443, 0.912716686726, 0.995000004768, + 0.929213345051, 0.005200000014, 0.929213345051, 0.021696666256, + 0.929213345051, 0.038193333894, 0.929213345051, 0.054689999670, + 0.929213345051, 0.071186669171, 0.929213345051, 0.087683334947, + 0.929213345051, 0.104180000722, 0.929213345051, 0.120676666498, + 0.929213345051, 0.137173339725, 0.929213345051, 0.153669998050, + 0.929213345051, 0.170166671276, 0.929213345051, 0.186663329601, + 0.929213345051, 0.203160002828, 0.929213345051, 0.219656661153, + 0.929213345051, 0.236153334379, 0.929213345051, 0.252649992704, + 0.929213345051, 0.269146680832, 0.929213345051, 0.285643339157, + 0.929213345051, 0.302139997482, 0.929213345051, 0.318636655807, + 0.929213345051, 0.335133343935, 0.929213345051, 0.351630002260, + 0.929213345051, 0.368126660585, 0.929213345051, 0.384623318911, + 0.929213345051, 0.401120007038, 0.929213345051, 0.417616665363, + 0.929213345051, 0.434113323689, 0.929213345051, 0.450610011816, + 0.929213345051, 0.467106670141, 0.929213345051, 0.483603328466, + 0.929213345051, 0.500100016594, 0.929213345051, 0.516596674919, + 0.929213345051, 0.533093333244, 0.929213345051, 0.549589991570, + 0.929213345051, 0.566086649895, 0.929213345051, 0.582583308220, + 0.929213345051, 0.599080026150, 0.929213345051, 0.615576684475, + 0.929213345051, 0.632073342800, 0.929213345051, 0.648570001125, + 0.929213345051, 0.665066659451, 0.929213345051, 0.681563317776, + 0.929213345051, 0.698059976101, 0.929213345051, 0.714556694031, + 0.929213345051, 0.731053352356, 0.929213345051, 0.747550010681, + 0.929213345051, 0.764046669006, 0.929213345051, 0.780543327332, + 0.929213345051, 0.797039985657, 0.929213345051, 0.813536643982, + 0.929213345051, 0.830033361912, 0.929213345051, 0.846530020237, + 0.929213345051, 0.863026678562, 0.929213345051, 0.879523336887, + 0.929213345051, 0.896019995213, 0.929213345051, 0.912516653538, + 0.929213345051, 0.929013311863, 0.929213345051, 0.945510029793, + 0.929213345051, 0.962006688118, 0.929213345051, 0.978503346443, + 0.929213345051, 0.995000004768, 0.945710003376, 0.005200000014, + 0.945710003376, 0.021696666256, 0.945710003376, 0.038193333894, + 0.945710003376, 0.054689999670, 0.945710003376, 0.071186669171, + 0.945710003376, 0.087683334947, 0.945710003376, 0.104180000722, + 0.945710003376, 0.120676666498, 0.945710003376, 0.137173339725, + 0.945710003376, 0.153669998050, 0.945710003376, 0.170166671276, + 0.945710003376, 0.186663329601, 0.945710003376, 0.203160002828, + 0.945710003376, 0.219656661153, 0.945710003376, 0.236153334379, + 0.945710003376, 0.252649992704, 0.945710003376, 0.269146680832, + 0.945710003376, 0.285643339157, 0.945710003376, 0.302139997482, + 0.945710003376, 0.318636655807, 0.945710003376, 0.335133343935, + 0.945710003376, 0.351630002260, 0.945710003376, 0.368126660585, + 0.945710003376, 0.384623318911, 0.945710003376, 0.401120007038, + 0.945710003376, 0.417616665363, 0.945710003376, 0.434113323689, + 0.945710003376, 0.450610011816, 0.945710003376, 0.467106670141, + 0.945710003376, 0.483603328466, 0.945710003376, 0.500100016594, + 0.945710003376, 0.516596674919, 0.945710003376, 0.533093333244, + 0.945710003376, 0.549589991570, 0.945710003376, 0.566086649895, + 0.945710003376, 0.582583308220, 0.945710003376, 0.599080026150, + 0.945710003376, 0.615576684475, 0.945710003376, 0.632073342800, + 0.945710003376, 0.648570001125, 0.945710003376, 0.665066659451, + 0.945710003376, 0.681563317776, 0.945710003376, 0.698059976101, + 0.945710003376, 0.714556694031, 0.945710003376, 0.731053352356, + 0.945710003376, 0.747550010681, 0.945710003376, 0.764046669006, + 0.945710003376, 0.780543327332, 0.945710003376, 0.797039985657, + 0.945710003376, 0.813536643982, 0.945710003376, 0.830033361912, + 0.945710003376, 0.846530020237, 0.945710003376, 0.863026678562, + 0.945710003376, 0.879523336887, 0.945710003376, 0.896019995213, + 0.945710003376, 0.912516653538, 0.945710003376, 0.929013311863, + 0.945710003376, 0.945510029793, 0.945710003376, 0.962006688118, + 0.945710003376, 0.978503346443, 0.945710003376, 0.995000004768, + 0.962206661701, 0.005200000014, 0.962206661701, 0.021696666256, + 0.962206661701, 0.038193333894, 0.962206661701, 0.054689999670, + 0.962206661701, 0.071186669171, 0.962206661701, 0.087683334947, + 0.962206661701, 0.104180000722, 0.962206661701, 0.120676666498, + 0.962206661701, 0.137173339725, 0.962206661701, 0.153669998050, + 0.962206661701, 0.170166671276, 0.962206661701, 0.186663329601, + 0.962206661701, 0.203160002828, 0.962206661701, 0.219656661153, + 0.962206661701, 0.236153334379, 0.962206661701, 0.252649992704, + 0.962206661701, 0.269146680832, 0.962206661701, 0.285643339157, + 0.962206661701, 0.302139997482, 0.962206661701, 0.318636655807, + 0.962206661701, 0.335133343935, 0.962206661701, 0.351630002260, + 0.962206661701, 0.368126660585, 0.962206661701, 0.384623318911, + 0.962206661701, 0.401120007038, 0.962206661701, 0.417616665363, + 0.962206661701, 0.434113323689, 0.962206661701, 0.450610011816, + 0.962206661701, 0.467106670141, 0.962206661701, 0.483603328466, + 0.962206661701, 0.500100016594, 0.962206661701, 0.516596674919, + 0.962206661701, 0.533093333244, 0.962206661701, 0.549589991570, + 0.962206661701, 0.566086649895, 0.962206661701, 0.582583308220, + 0.962206661701, 0.599080026150, 0.962206661701, 0.615576684475, + 0.962206661701, 0.632073342800, 0.962206661701, 0.648570001125, + 0.962206661701, 0.665066659451, 0.962206661701, 0.681563317776, + 0.962206661701, 0.698059976101, 0.962206661701, 0.714556694031, + 0.962206661701, 0.731053352356, 0.962206661701, 0.747550010681, + 0.962206661701, 0.764046669006, 0.962206661701, 0.780543327332, + 0.962206661701, 0.797039985657, 0.962206661701, 0.813536643982, + 0.962206661701, 0.830033361912, 0.962206661701, 0.846530020237, + 0.962206661701, 0.863026678562, 0.962206661701, 0.879523336887, + 0.962206661701, 0.896019995213, 0.962206661701, 0.912516653538, + 0.962206661701, 0.929013311863, 0.962206661701, 0.945510029793, + 0.962206661701, 0.962006688118, 0.962206661701, 0.978503346443, + 0.962206661701, 0.995000004768, 0.978703320026, 0.005200000014, + 0.978703320026, 0.021696666256, 0.978703320026, 0.038193333894, + 0.978703320026, 0.054689999670, 0.978703320026, 0.071186669171, + 0.978703320026, 0.087683334947, 0.978703320026, 0.104180000722, + 0.978703320026, 0.120676666498, 0.978703320026, 0.137173339725, + 0.978703320026, 0.153669998050, 0.978703320026, 0.170166671276, + 0.978703320026, 0.186663329601, 0.978703320026, 0.203160002828, + 0.978703320026, 0.219656661153, 0.978703320026, 0.236153334379, + 0.978703320026, 0.252649992704, 0.978703320026, 0.269146680832, + 0.978703320026, 0.285643339157, 0.978703320026, 0.302139997482, + 0.978703320026, 0.318636655807, 0.978703320026, 0.335133343935, + 0.978703320026, 0.351630002260, 0.978703320026, 0.368126660585, + 0.978703320026, 0.384623318911, 0.978703320026, 0.401120007038, + 0.978703320026, 0.417616665363, 0.978703320026, 0.434113323689, + 0.978703320026, 0.450610011816, 0.978703320026, 0.467106670141, + 0.978703320026, 0.483603328466, 0.978703320026, 0.500100016594, + 0.978703320026, 0.516596674919, 0.978703320026, 0.533093333244, + 0.978703320026, 0.549589991570, 0.978703320026, 0.566086649895, + 0.978703320026, 0.582583308220, 0.978703320026, 0.599080026150, + 0.978703320026, 0.615576684475, 0.978703320026, 0.632073342800, + 0.978703320026, 0.648570001125, 0.978703320026, 0.665066659451, + 0.978703320026, 0.681563317776, 0.978703320026, 0.698059976101, + 0.978703320026, 0.714556694031, 0.978703320026, 0.731053352356, + 0.978703320026, 0.747550010681, 0.978703320026, 0.764046669006, + 0.978703320026, 0.780543327332, 0.978703320026, 0.797039985657, + 0.978703320026, 0.813536643982, 0.978703320026, 0.830033361912, + 0.978703320026, 0.846530020237, 0.978703320026, 0.863026678562, + 0.978703320026, 0.879523336887, 0.978703320026, 0.896019995213, + 0.978703320026, 0.912516653538, 0.978703320026, 0.929013311863, + 0.978703320026, 0.945510029793, 0.978703320026, 0.962006688118, + 0.978703320026, 0.978503346443, 0.978703320026, 0.995000004768, + 0.995199978352, 0.005200000014, 0.995199978352, 0.021696666256, + 0.995199978352, 0.038193333894, 0.995199978352, 0.054689999670, + 0.995199978352, 0.071186669171, 0.995199978352, 0.087683334947, + 0.995199978352, 0.104180000722, 0.995199978352, 0.120676666498, + 0.995199978352, 0.137173339725, 0.995199978352, 0.153669998050, + 0.995199978352, 0.170166671276, 0.995199978352, 0.186663329601, + 0.995199978352, 0.203160002828, 0.995199978352, 0.219656661153, + 0.995199978352, 0.236153334379, 0.995199978352, 0.252649992704, + 0.995199978352, 0.269146680832, 0.995199978352, 0.285643339157, + 0.995199978352, 0.302139997482, 0.995199978352, 0.318636655807, + 0.995199978352, 0.335133343935, 0.995199978352, 0.351630002260, + 0.995199978352, 0.368126660585, 0.995199978352, 0.384623318911, + 0.995199978352, 0.401120007038, 0.995199978352, 0.417616665363, + 0.995199978352, 0.434113323689, 0.995199978352, 0.450610011816, + 0.995199978352, 0.467106670141, 0.995199978352, 0.483603328466, + 0.995199978352, 0.500100016594, 0.995199978352, 0.516596674919, + 0.995199978352, 0.533093333244, 0.995199978352, 0.549589991570, + 0.995199978352, 0.566086649895, 0.995199978352, 0.582583308220, + 0.995199978352, 0.599080026150, 0.995199978352, 0.615576684475, + 0.995199978352, 0.632073342800, 0.995199978352, 0.648570001125, + 0.995199978352, 0.665066659451, 0.995199978352, 0.681563317776, + 0.995199978352, 0.698059976101, 0.995199978352, 0.714556694031, + 0.995199978352, 0.731053352356, 0.995199978352, 0.747550010681, + 0.995199978352, 0.764046669006, 0.995199978352, 0.780543327332, + 0.995199978352, 0.797039985657, 0.995199978352, 0.813536643982, + 0.995199978352, 0.830033361912, 0.995199978352, 0.846530020237, + 0.995199978352, 0.863026678562, 0.995199978352, 0.879523336887, + 0.995199978352, 0.896019995213, 0.995199978352, 0.912516653538, + 0.995199978352, 0.929013311863, 0.995199978352, 0.945510029793, + 0.995199978352, 0.962006688118, 0.995199978352, 0.978503346443, + 0.995199978352, 0.995000004768, +}; + +} /* namespace Pdraw */ + +#endif /* USE_GLES2 */ diff --git a/libpdraw/src/pdraw_gles2_video.cpp b/libpdraw/src/pdraw_gles2_video.cpp index 615d9bf..7b71204 100644 --- a/libpdraw/src/pdraw_gles2_video.cpp +++ b/libpdraw/src/pdraw_gles2_video.cpp @@ -1,3010 +1,3010 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * OpenGL ES 2.0 video rendering - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_gles2vid -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_gles2_video.hpp" - -#ifdef USE_GLES2 - -# include "pdraw_session.hpp" - -# include -# ifdef BCM_VIDEOCORE -# include -# include -# include -# endif /* BCM_VIDEOCORE */ - -# include - -namespace Pdraw { - - -/* Blurred padding dark coef */ -# define PDRAW_BLURRED_PADDING_DARK_COEF (0.75f) - -/* The temporal frequency of zebra pattern */ -# define PDRAW_ZEBRA_FREQUENCY_HZ (1.f) -/* The angle of zebra pattern relative to y axis */ -# define PDRAW_ZEBRA_ANGLE (60.f * M_PI / 180.f) -/* The weight in pixels of zebra pattern, relative to 1920 width */ -# define PDRAW_ZEBRA_WEIGHT (8.f) - -# define GLES2_VIDEO_BLUR_MIN_SIGMA 0.8f -# define GLES2_VIDEO_BLUR_MAX_SIGMA 6.0f -# define GLES2_VIDEO_BLURRED_PADDING_SIGMA 3.0f - -# define GLES2_VIDEO_HISTOGRAM_COMPUTE_INTERVAL_US 100000 - -# define GLES2_VIDEO_FLASH_LIGHT_COEF 0.3f -# define GLES2_VIDEO_FLASH_GAMMA_COEF 2.0f - - -static const GLchar *videoVertexShader = - "uniform mat4 transform_matrix;\n" - "attribute vec4 position;\n" - "attribute vec2 texcoord;\n" - "varying vec2 v_texcoord;\n" - "\n" - "void main()\n" - "{\n" - " gl_Position = transform_matrix * position;\n" - " v_texcoord = texcoord;\n" - "}\n"; - -static const GLchar *zebraFragmentShader = - "uniform float zebra_phase;\n" - "uniform float zebra_sat;\n" - "uniform float zebra_weight;\n" - "uniform mat2 zebra_mat;\n" - "\n" - "vec3 apply_zebra(vec3 avg, vec3 rgb)\n" - "{\n" - " vec2 rot_pos = zebra_mat * gl_FragCoord.xy;\n" - " float z = mod(rot_pos.x + 2.0 * zebra_weight * zebra_phase,\n" - " zebra_weight * 2.0) - zebra_weight;\n" - " float zebra = smoothstep(zebra_weight / 2.0 - 0.5,\n" - " zebra_weight / 2.0 + 0.5, abs(z));\n" - " float pixel_val = min(min(avg.r, avg.g), avg.b);\n" - " float zebra_factor = step(zebra_sat, pixel_val);\n" - " rgb = mix(rgb, zebra * rgb, zebra_factor);\n" - " return rgb;\n" - "}\n"; - -static const GLchar *textureNoconvFragmentShader = -# ifdef BCM_VIDEOCORE - "uniform samplerExternalOES s_texture_0;\n" - "uniform samplerExternalOES s_texture_1;\n" - "uniform samplerExternalOES s_texture_2;\n" -# else /* BCM_VIDEOCORE */ - "uniform sampler2D s_texture_0;\n" - "uniform sampler2D s_texture_1;\n" - "uniform sampler2D s_texture_2;\n" -# endif /* BCM_VIDEOCORE */ - "uniform vec2 stride[3];\n" - "uniform vec2 max_coords[3];\n" - "uniform mat3 yuv2rgb_mat;\n" - "uniform vec3 yuv2rgb_offset;\n" - "\n" - "vec3 read_rgb(vec2 coord)\n" - "{\n" - " return texture2D(s_texture_0, min(coord, max_coords[0] - stride[0] / 2.0)).rgb;\n" - "}\n" - "\n" - "vec3 read_rgb_with_offset(vec2 coord, vec2 offset_px)\n" - "{\n" - " return texture2D(s_texture_0, min(coord + offset_px * stride[0], max_coords[0] - stride[0] / 2.0)).rgb;\n" - "}\n"; - -static const GLchar *textureI420FragmentShader = - "uniform sampler2D s_texture_0;\n" - "uniform sampler2D s_texture_1;\n" - "uniform sampler2D s_texture_2;\n" - "uniform vec2 stride[3];\n" - "uniform vec2 max_coords[3];\n" - "uniform mat3 yuv2rgb_mat;\n" - "uniform vec3 yuv2rgb_offset;\n" - "\n" - "vec3 read_rgb(vec2 coord)\n" - "{\n" - " vec3 yuv;\n" - " yuv.r = texture2D(s_texture_0, min(coord, max_coords[0] - stride[0] / 2.0)).r;\n" - " yuv.g = texture2D(s_texture_1, min(coord, max_coords[1] - stride[1] / 2.0)).r;\n" - " yuv.b = texture2D(s_texture_2, min(coord, max_coords[2] - stride[2] / 2.0)).r;\n" - " return yuv2rgb_mat * (yuv.rgb + yuv2rgb_offset);\n" - "}\n" - "\n" - "vec3 read_rgb_with_offset(vec2 coord, vec2 offset_px)\n" - "{\n" - " vec3 yuv;\n" - " yuv.r = texture2D(s_texture_0, min(coord + offset_px * stride[0], max_coords[0] - stride[0] / 2.0)).r;\n" - " yuv.g = texture2D(s_texture_1, min(coord + offset_px * stride[1], max_coords[1] - stride[1] / 2.0)).r;\n" - " yuv.b = texture2D(s_texture_2, min(coord + offset_px * stride[2], max_coords[2] - stride[2] / 2.0)).r;\n" - " return yuv2rgb_mat * (yuv.rgb + yuv2rgb_offset);\n" - "}\n"; - -/* YUV 4:2:0 planar with 16 bits data format in little endian - * and 10 bits depth, padding in higher bits */ -static const GLchar *textureI42010LELowFragmentShader = - "uniform sampler2D s_texture_0;\n" - "uniform sampler2D s_texture_1;\n" - "uniform sampler2D s_texture_2;\n" - "uniform vec2 stride[3];\n" - "uniform vec2 max_coords[3];\n" - "uniform mat3 yuv2rgb_mat;\n" - "uniform vec3 yuv2rgb_offset;\n" - "\n" - "vec3 read_rgb(vec2 coord)\n" - "{\n" - " vec3 yuv;\n" - " vec4 y = texture2D(s_texture_0, min(coord, max_coords[0] - stride[0] / 2.0));\n" - " vec4 u = texture2D(s_texture_1, min(coord, max_coords[1] - stride[1] / 2.0));\n" - " vec4 v = texture2D(s_texture_2, min(coord, max_coords[2] - stride[2] / 2.0));\n" - " yuv.r = y.a * 64. + y.r / 4.;\n" - " yuv.g = u.a * 64. + u.r / 4.;\n" - " yuv.b = v.a * 64. + v.r / 4.;\n" - " return yuv2rgb_mat * (yuv.rgb + yuv2rgb_offset);\n" - "}\n" - "\n" - "vec3 read_rgb_with_offset(vec2 coord, vec2 offset_px)\n" - "{\n" - " vec3 yuv;\n" - " vec4 y = texture2D(s_texture_0, min(coord + offset_px * stride[0], max_coords[0] - stride[0] / 2.0));\n" - " vec4 u = texture2D(s_texture_1, min(coord + offset_px * stride[1], max_coords[1] - stride[1] / 2.0));\n" - " vec4 v = texture2D(s_texture_2, min(coord + offset_px * stride[2], max_coords[2] - stride[2] / 2.0));\n" - " yuv.r = y.a * 64. + y.r / 4.;\n" - " yuv.g = u.a * 64. + u.r / 4.;\n" - " yuv.b = v.a * 64. + v.r / 4.;\n" - " return yuv2rgb_mat * (yuv.rgb + yuv2rgb_offset);\n" - "}\n"; - -static const GLchar *textureNV12FragmentShader = - "uniform sampler2D s_texture_0;\n" - "uniform sampler2D s_texture_1;\n" - "uniform sampler2D s_texture_2;\n" - "uniform vec2 stride[3];\n" - "uniform vec2 max_coords[3];\n" - "uniform mat3 yuv2rgb_mat;\n" - "uniform vec3 yuv2rgb_offset;\n" - "\n" - "vec3 read_rgb(vec2 coord)\n" - "{\n" - " vec3 yuv;\n" - " yuv.r = texture2D(s_texture_0, min(coord, max_coords[0] - stride[0] / 2.0)).r;\n" - " yuv.gb = texture2D(s_texture_1, min(coord, max_coords[1] - stride[1] / 2.0)).ra;\n" - " return yuv2rgb_mat * (yuv.rgb + yuv2rgb_offset);\n" - "}\n" - "\n" - "vec3 read_rgb_with_offset(vec2 coord, vec2 offset_px)\n" - "{\n" - " vec3 yuv;\n" - " yuv.r = texture2D(s_texture_0, min(coord + offset_px * stride[0], max_coords[0] - stride[0] / 2.0)).r;\n" - " yuv.gb = texture2D(s_texture_1, min(coord + offset_px * stride[1], max_coords[1] - stride[1] / 2.0)).ra;\n" - " return yuv2rgb_mat * (yuv.rgb + yuv2rgb_offset);\n" - "}\n"; - -/* YUV 4:2:0 semi-planar with 16 bits data format in little endian - * and 10 bits depth, padding in lower bits */ -static const GLchar *textureNV1210LEHighFragmentShader = - "uniform sampler2D s_texture_0;\n" - "uniform sampler2D s_texture_1;\n" - "uniform sampler2D s_texture_2;\n" - "uniform vec2 stride[3];\n" - "uniform vec2 max_coords[3];\n" - "uniform mat3 yuv2rgb_mat;\n" - "uniform vec3 yuv2rgb_offset;\n" - "\n" - "vec3 read_rgb(vec2 coord)\n" - "{\n" - " vec3 yuv;\n" - " vec4 y = texture2D(s_texture_0, min(coord, max_coords[0] - stride[0] / 2.0));\n" - " vec4 uv = texture2D(s_texture_1, min(coord, max_coords[1] - stride[1] / 2.0));\n" - " yuv.r = y.a + y.r / 256.;\n" - " yuv.g = uv.g + uv.b / 256.;\n" - " yuv.b = uv.a + uv.r / 256.;\n" - " return yuv2rgb_mat * (yuv.rgb + yuv2rgb_offset);\n" - "}\n" - "\n" - "vec3 read_rgb_with_offset(vec2 coord, vec2 offset_px)\n" - "{\n" - " vec3 yuv;\n" - " vec4 y = texture2D(s_texture_0, min(coord + offset_px * stride[0], max_coords[0] - stride[0] / 2.0));\n" - " vec4 uv = texture2D(s_texture_1, min(coord + offset_px * stride[1], max_coords[1] - stride[1] / 2.0));\n" - " yuv.r = y.a + y.r / 256.;\n" - " yuv.g = uv.g + uv.b / 256.;\n" - " yuv.b = uv.a + uv.r / 256.;\n" - " return yuv2rgb_mat * (yuv.rgb + yuv2rgb_offset);\n" - "}\n"; - -static const GLchar *videoFragmentShader = -# ifdef BCM_VIDEOCORE - "#extension GL_OES_EGL_image_external : require\n" -# endif /* BCM_VIDEOCORE */ -# if defined(GL_ES_VERSION_2_0) && \ - (defined(ANDROID) || defined(__APPLE__) || defined(MESON)) - "precision highp float;\n" -# endif - "varying vec2 v_texcoord;\n" - "uniform float sat_coef;\n" - "uniform float light_coef;\n" - "uniform float dark_coef;\n" - "uniform float zebra_enable;\n" - "uniform float zebra_avg_weights[9];\n" - "\n" - "vec3 apply_zebra(vec3 avg, vec3 rgb);\n" - "vec3 read_rgb(vec2 coord);\n" - "vec3 read_rgb_with_offset(vec2 coord, vec2 offset_px);\n" - "\n" - "vec3 read_rgb_avg(vec2 coord)\n" - "{\n" - " vec3 rgb = vec3(0.0);\n" - " for (int y = -1; y <= 1; y++)\n" - " {\n" - " for (int x = -1; x <= 1; x++)\n" - " {\n" - " vec2 offset = vec2(float(x), float(y));\n" - " rgb += read_rgb_with_offset(coord , offset)\n" - " * zebra_avg_weights[(y + 1) * 3 + (x + 1)];\n" - " }\n" - " }\n" - " return rgb;\n" - "}\n" - "\n" - "void main()\n" - "{\n" - " vec3 rgb = read_rgb(v_texcoord);\n" - " \n" - " if (zebra_enable > 0.5)\n" - " {\n" - " vec3 avg = read_rgb_avg(v_texcoord);\n" - " rgb = apply_zebra(avg, rgb);\n" - " }\n" - " float luma = 0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b;\n" - " rgb = mix(vec3(luma), rgb, sat_coef);\n" - " rgb = mix(vec3(1.0), rgb, light_coef);\n" - " rgb = mix(vec3(0.0), rgb, dark_coef);\n" - " gl_FragColor = vec4(rgb, 1.0);\n" - "}\n"; - -const GLchar *Gles2Video::videoFragmentShaders[PROGRAM_MAX][3] = { - { - videoFragmentShader, - textureNoconvFragmentShader, - zebraFragmentShader, - }, - { - videoFragmentShader, - textureI420FragmentShader, - zebraFragmentShader, - }, - { - videoFragmentShader, - textureI42010LELowFragmentShader, - zebraFragmentShader, - }, - { - videoFragmentShader, - textureNV12FragmentShader, - zebraFragmentShader, - }, - { - videoFragmentShader, - textureNV1210LEHighFragmentShader, - zebraFragmentShader, - }, -}; - -static const GLchar *blurHVertexShader = - "attribute vec2 position;\n" - "varying float v_texcoord_x[15];\n" - "varying float v_texcoord_y;\n" - "uniform float pixel_size;\n" - "\n" - "void main()\n" - "{\n" - " gl_Position = vec4(position, 0.0, 1.0);\n" - " vec2 center_tex_coords = position * 0.5 + 0.5;\n" - " for (int i = -7; i <= 7; i++) {\n" - " float offset = pixel_size * float(i);\n" - " v_texcoord_x[i + 7] = center_tex_coords.x + offset;\n" - " }\n" - " v_texcoord_y = center_tex_coords.y;\n" - "}\n"; - -static const GLchar *blurVVertexShader = - "attribute vec2 position;\n" - "varying float v_texcoord_x;\n" - "varying float v_texcoord_y[15];\n" - "uniform float pixel_size;\n" - "\n" - "void main()\n" - "{\n" - " gl_Position = vec4(position, 0.0, 1.0);\n" - " vec2 center_tex_coords = position * 0.5 + 0.5;\n" - " for (int i = -7; i <= 7; i++) {\n" - " float offset = pixel_size * float(i);\n" - " v_texcoord_y[i + 7] = center_tex_coords.y + offset;\n" - " }\n" - " v_texcoord_x = center_tex_coords.x;\n" - "}\n"; - -static const GLchar *blurHFragmentShader = -# if defined(GL_ES_VERSION_2_0) && \ - (defined(ANDROID) || defined(__APPLE__) || defined(MESON)) - "precision mediump float;\n" -# endif - "varying float v_texcoord_x[15];\n" - "varying float v_texcoord_y;\n" - "uniform sampler2D s_texture;\n" - "uniform float blur_weights[15];\n" - "\n" - "void main()\n" - "{\n" - " vec3 rgb = vec3(0.0);\n" - " for (int i = 0; i < 15; i++) {\n" - " vec2 coords = vec2(v_texcoord_x[i], v_texcoord_y);\n" - " rgb += texture2D(s_texture, coords).rgb * blur_weights[i];\n" - " }\n" - " gl_FragColor = vec4(rgb, 1.0);\n" - "}\n"; - -static const GLchar *blurVFragmentShader = -# if defined(GL_ES_VERSION_2_0) && \ - (defined(ANDROID) || defined(__APPLE__) || defined(MESON)) - "precision mediump float;\n" -# endif - "varying float v_texcoord_x;\n" - "varying float v_texcoord_y[15];\n" - "uniform sampler2D s_texture;\n" - "uniform float blur_weights[15];\n" - "\n" - "void main()\n" - "{\n" - " vec3 rgb = vec3(0.0);\n" - " for (int i = 0; i < 15; i++) {\n" - " vec2 coords = vec2(v_texcoord_x, v_texcoord_y[i]);\n" - " rgb += texture2D(s_texture, coords).rgb * blur_weights[i];\n" - " }\n" - " gl_FragColor = vec4(rgb, 1.0);\n" - "}\n"; - -static const GLchar *histogramVertexShader = - "attribute vec4 position;\n" - "attribute vec2 texcoord;\n" - "varying vec2 v_texcoord;\n" - "\n" - "void main()\n" - "{\n" - " gl_Position = position;\n" - " v_texcoord = texcoord;\n" - "}\n"; - -static const GLchar *histogramFragmentShader = -# ifdef BCM_VIDEOCORE - "#extension GL_OES_EGL_image_external : require\n" -# endif /* BCM_VIDEOCORE */ -# if defined(GL_ES_VERSION_2_0) && \ - (defined(ANDROID) || defined(__APPLE__) || defined(MESON)) - "precision mediump float;\n" -# endif - "varying vec2 v_texcoord;\n" - "\n" - "vec3 read_rgb(vec2 coord);\n" - "\n" - "void main()\n" - "{\n" - " vec3 rgb = read_rgb(v_texcoord);\n" - " float luma = 0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b;\n" - " gl_FragColor = vec4(rgb, luma);\n" - "}\n"; - -const GLchar *Gles2Video::histogramFragmentShaders[PROGRAM_MAX][2] = { - { - histogramFragmentShader, - textureNoconvFragmentShader, - }, - { - histogramFragmentShader, - textureI420FragmentShader, - }, - { - histogramFragmentShader, - textureNV12FragmentShader, - }, - { - histogramFragmentShader, - textureI42010LELowFragmentShader, - }, - { - histogramFragmentShader, - textureNV1210LEHighFragmentShader, - }, -}; - -static const GLfloat zebraAvgWeights[9] = {0.077847f, - 0.123317f, - 0.077847f, - 0.123317f, - 0.195346f, - 0.123317f, - 0.077847f, - 0.123317f, - 0.077847f}; - - -Gles2Video::Gles2Video(Session *session, - GLuint defaultFbo, - unsigned int firstTexUnit) -{ - int ret; - GLint vertexShader = 0, fragmentShader[PROGRAM_MAX] = {}; - GLint success = 0; - unsigned int i; - - mSession = session; - mVideoWidth = 0; - mVideoHeight = 0; - mFirstTexUnit = firstTexUnit; - mDefaultFbo = defaultFbo; - mTransition = GLES2_VIDEO_TRANSITION_NONE; - mTransitionStartTime = 0; - mTransitionDuration = 0; - mTransitionHold = false; - memset(mProgram, 0, sizeof(mProgram)); - memset(mProgramTransformMatrix, 0, sizeof(mProgramTransformMatrix)); - memset(mProgramYuv2RgbMatrix, 0, sizeof(mProgramYuv2RgbMatrix)); - memset(mProgramYuv2RgbOffset, 0, sizeof(mProgramYuv2RgbOffset)); - memset(mProgramStride, 0, sizeof(mProgramStride)); - memset(mProgramMaxCoords, 0, sizeof(mProgramMaxCoords)); - memset(mProgramSatCoef, 0, sizeof(mProgramSatCoef)); - memset(mProgramLightCoef, 0, sizeof(mProgramLightCoef)); - memset(mProgramDarkCoef, 0, sizeof(mProgramDarkCoef)); - memset(mProgramZebraEnable, 0, sizeof(mProgramZebraEnable)); - memset(mProgramZebraThreshold, 0, sizeof(mProgramZebraThreshold)); - memset(mProgramZebraPhase, 0, sizeof(mProgramZebraPhase)); - memset(mProgramZebraWeight, 0, sizeof(mProgramZebraWeight)); - memset(mTextures, 0, sizeof(mTextures)); - mExtTexture = 0; - memset(mUniformSamplers, 0, sizeof(mUniformSamplers)); - memset(mPositionHandle, 0, sizeof(mPositionHandle)); - memset(mTexcoordHandle, 0, sizeof(mTexcoordHandle)); - mBlurInit = false; - mApplyBlur = false; - memset(mBlurWeights, 0, sizeof(mBlurWeights)); - mBlurFboWidth = 0; - mBlurFboHeight = 0; - memset(mBlurFbo, 0, sizeof(mBlurFbo)); - memset(mBlurFboTexture, 0, sizeof(mBlurFboTexture)); - memset(mBlurProgram, 0, sizeof(mBlurProgram)); - memset(mBlurUniformPixelSize, 0, sizeof(mBlurUniformPixelSize)); - memset(mBlurUniformWeights, 0, sizeof(mBlurUniformWeights)); - memset(mBlurUniformSampler, 0, sizeof(mBlurUniformSampler)); - memset(mBlurPositionHandle, 0, sizeof(mBlurPositionHandle)); - mPaddingPass1Width = 0; - mPaddingPass1Height = 0; - mPaddingPass2Width = 0; - mPaddingPass2Height = 0; - memset(mPaddingBlurWeights, 0, sizeof(mPaddingBlurWeights)); - memset(mPaddingFbo, 0, sizeof(mPaddingFbo)); - memset(mPaddingFboTexture, 0, sizeof(mPaddingFboTexture)); - mHistogramInit = false; - mHistogramLastComputeTime = 0; - memset(mHistogramProgram, 0, sizeof(mHistogramProgram)); - memset(mHistogramYuv2RgbMatrix, 0, sizeof(mHistogramYuv2RgbMatrix)); - memset(mHistogramYuv2RgbOffset, 0, sizeof(mHistogramYuv2RgbOffset)); - memset(mHistogramUniformSampler, 0, sizeof(mHistogramUniformSampler)); - memset(mHistogramPositionHandle, 0, sizeof(mHistogramPositionHandle)); - memset(mHistogramTexcoordHandle, 0, sizeof(mHistogramTexcoordHandle)); - mHistogramFbo = 0; - mHistogramFboTexture = 0; - mHistogramBuffer = nullptr; - for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) - mHistogramValid[i] = false; - memset(mHistogram, 0, sizeof(mHistogram)); - memset(mHistogramNorm, 0, sizeof(mHistogram)); - mSatCoef = 1.0f; - mBaseSatCoef = 1.0f; - mLightCoef = 1.0f; - mBaseLightCoef = 1.0f; - mDarkCoef = 1.0f; - mBaseDarkCoef = 1.0f; -# ifdef BCM_VIDEOCORE - mEglImage = EGL_NO_IMAGE_KHR; -# endif /* BCM_VIDEOCORE */ - - GLCHK(); - - /* Vertex shader */ - vertexShader = glCreateShader(GL_VERTEX_SHADER); - if ((vertexShader == 0) || (vertexShader == GL_INVALID_ENUM)) { - ULOGE("failed to create vertex shader"); - goto err; - } - - glShaderSource(vertexShader, 1, &videoVertexShader, nullptr); - glCompileShader(vertexShader); - glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); - if (!success) { - GLchar infoLog[512]; - glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog); - ULOGE("vertex shader compilation failed '%s'", infoLog); - goto err; - } - - for (i = 0; i < PROGRAM_MAX; i++) { - /* Fragment shader */ - fragmentShader[i] = glCreateShader(GL_FRAGMENT_SHADER); - if ((fragmentShader[i] == 0) || - (fragmentShader[i] == GL_INVALID_ENUM)) { - ULOGE("failed to create fragment shader"); - goto err; - } - - glShaderSource( - fragmentShader[i], 3, videoFragmentShaders[i], nullptr); - glCompileShader(fragmentShader[i]); - glGetShaderiv(fragmentShader[i], GL_COMPILE_STATUS, &success); - if (!success) { - GLchar infoLog[512]; - glGetShaderInfoLog( - fragmentShader[i], 512, nullptr, infoLog); - ULOGE("fragment shader compilation failed '%s'", - infoLog); - goto err; - } - - /* Link shaders */ - mProgram[i] = glCreateProgram(); - glAttachShader(mProgram[i], vertexShader); - glAttachShader(mProgram[i], fragmentShader[i]); - glLinkProgram(mProgram[i]); - glGetProgramiv(mProgram[i], GL_LINK_STATUS, &success); - if (!success) { - GLchar infoLog[512]; - glGetProgramInfoLog(mProgram[i], 512, nullptr, infoLog); - ULOGE("program link failed '%s'", infoLog); - goto err; - } - - glDeleteShader(fragmentShader[i]); - fragmentShader[i] = 0; - } - - glDeleteShader(vertexShader); - vertexShader = 0; - - GLCHK(); - - for (i = 0; i < PROGRAM_MAX; i++) { - mProgramTransformMatrix[i] = - glGetUniformLocation(mProgram[i], "transform_matrix"); - mProgramYuv2RgbMatrix[i] = - glGetUniformLocation(mProgram[i], "yuv2rgb_mat"); - mProgramYuv2RgbOffset[i] = - glGetUniformLocation(mProgram[i], "yuv2rgb_offset"); - mProgramStride[i] = glGetUniformLocation(mProgram[i], "stride"); - mProgramMaxCoords[i] = - glGetUniformLocation(mProgram[i], "max_coords"); - mProgramSatCoef[i] = - glGetUniformLocation(mProgram[i], "sat_coef"); - mProgramLightCoef[i] = - glGetUniformLocation(mProgram[i], "light_coef"); - mProgramDarkCoef[i] = - glGetUniformLocation(mProgram[i], "dark_coef"); - mProgramZebraEnable[i] = - glGetUniformLocation(mProgram[i], "zebra_enable"); - mProgramZebraThreshold[i] = - glGetUniformLocation(mProgram[i], "zebra_sat"); - mProgramZebraPhase[i] = - glGetUniformLocation(mProgram[i], "zebra_phase"); - mProgramZebraWeight[i] = - glGetUniformLocation(mProgram[i], "zebra_weight"); - mUniformSamplers[i][0] = - glGetUniformLocation(mProgram[i], "s_texture_0"); - mUniformSamplers[i][1] = - glGetUniformLocation(mProgram[i], "s_texture_1"); - mUniformSamplers[i][2] = - glGetUniformLocation(mProgram[i], "s_texture_2"); - mPositionHandle[i] = - glGetAttribLocation(mProgram[i], "position"); - mTexcoordHandle[i] = - glGetAttribLocation(mProgram[i], "texcoord"); - } - - GLCHK(); - - ret = setupBlur(); - if (ret < 0) - ULOG_ERRNO("setupBlur", -ret); - - GLCHK(); - - ret = setupHistograms(); - if (ret < 0) - ULOG_ERRNO("setupHistograms", -ret); - - GLCHK(); - - /* Setup zebra shaders */ - setupZebra(PROGRAM_NOCONV); - setupZebra(PROGRAM_YUV_TO_RGB_PLANAR); - setupZebra(PROGRAM_YUV_TO_RGB_SEMIPLANAR); - setupZebra(PROGRAM_YUV_TO_RGB_PLANAR_10_16LE); - setupZebra(PROGRAM_YUV_TO_RGB_SEMIPLANAR_10_16LE_HIGH); - - GLCHK(glGenTextures(GLES2_VIDEO_TEX_UNIT_COUNT, mTextures)); - - for (i = 0; i < GLES2_VIDEO_TEX_UNIT_COUNT; i++) { - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + i)); -# ifdef BCM_VIDEOCORE - if (i == 0) { - GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, - mTextures[i])); - } else { - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[i])); - } -# else /* BCM_VIDEOCORE */ - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[i])); -# endif /* BCM_VIDEOCORE */ - - GLCHK(glTexParameteri( - GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - GLCHK(glTexParameteri( - GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GLCHK(glTexParameterf( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GLCHK(glTexParameterf( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - } - - GLCHK(glBindTexture(GL_TEXTURE_2D, 0)); - - return; - -err: - if (mTextures[0]) - GLCHK(glDeleteTextures(GLES2_VIDEO_TEX_UNIT_COUNT, mTextures)); - if (vertexShader) - GLCHK(glDeleteShader(vertexShader)); - for (i = 0; i < PROGRAM_MAX; i++) { - if (fragmentShader[i]) - GLCHK(glDeleteShader(fragmentShader[i])); - if (mProgram[i] > 0) - GLCHK(glDeleteProgram(mProgram[i])); - } - memset(mProgram, 0, sizeof(mProgram)); - memset(mTextures, 0, sizeof(mTextures)); - cleanupBlur(); - cleanupHistograms(); -} - - -Gles2Video::~Gles2Video(void) -{ - if (mTextures[0]) - GLCHK(glDeleteTextures(GLES2_VIDEO_TEX_UNIT_COUNT, mTextures)); - for (unsigned int i = 0; i < PROGRAM_MAX; i++) { - if (mProgram[i] > 0) - GLCHK(glDeleteProgram(mProgram[i])); - } - - cleanupBlur(); - cleanupPaddingFbo(); - cleanupHistograms(); -} - - -enum Gles2Video::program -Gles2Video::getProgram(const struct vdef_raw_format *format) -{ - if (vdef_raw_format_cmp(format, &vdef_i420)) { - return PROGRAM_YUV_TO_RGB_PLANAR; - } else if (vdef_raw_format_cmp(format, &vdef_nv12)) { - return PROGRAM_YUV_TO_RGB_SEMIPLANAR; - } else if (vdef_raw_format_cmp(format, &vdef_i420_10_16le)) { - return PROGRAM_YUV_TO_RGB_PLANAR_10_16LE; - } else if (vdef_raw_format_cmp(format, &vdef_nv12_10_16le_high)) { - return PROGRAM_YUV_TO_RGB_SEMIPLANAR_10_16LE_HIGH; - } else if (vdef_raw_format_cmp(format, &vdef_rgb)) { - return PROGRAM_NOCONV; - } else if (vdef_raw_format_cmp(format, &vdef_mmal_opaque)) { - return PROGRAM_NOCONV; - } else { - ULOGE("unsupported frame format"); - return PROGRAM_NOCONV; - } -} - - -int Gles2Video::setupBlur(void) -{ - int ret = 0; - GLint vertexShaderH = 0, vertexShaderV = 0; - GLint fragmentShaderH = 0, fragmentShaderV = 0; - GLint success = 0; - - /* Free previous resources */ - cleanupBlur(); - - /* Render sizes */ - mBlurFboWidth = GLES2_VIDEO_BLUR_FBO_TARGET_SIZE; - mBlurFboHeight = GLES2_VIDEO_BLUR_FBO_TARGET_SIZE; - - /* Shaders compilation */ - vertexShaderH = glCreateShader(GL_VERTEX_SHADER); - if ((vertexShaderH == 0) || (vertexShaderH == GL_INVALID_ENUM)) { - ULOGE("failed to create vertex shader"); - ret = -ENOMEM; - goto error; - } - - glShaderSource(vertexShaderH, 1, &blurHVertexShader, nullptr); - glCompileShader(vertexShaderH); - glGetShaderiv(vertexShaderH, GL_COMPILE_STATUS, &success); - if (!success) { - GLchar infoLog[512]; - glGetShaderInfoLog(vertexShaderH, 512, nullptr, infoLog); - ULOGE("vertex shader (H) compilation failed '%s'", infoLog); - ret = -EPROTO; - goto error; - } - - vertexShaderV = glCreateShader(GL_VERTEX_SHADER); - if ((vertexShaderV == 0) || (vertexShaderV == GL_INVALID_ENUM)) { - ULOGE("failed to create vertex shader"); - ret = -ENOMEM; - goto error; - } - - glShaderSource(vertexShaderV, 1, &blurVVertexShader, nullptr); - glCompileShader(vertexShaderV); - glGetShaderiv(vertexShaderV, GL_COMPILE_STATUS, &success); - if (!success) { - GLchar infoLog[512]; - glGetShaderInfoLog(vertexShaderV, 512, nullptr, infoLog); - ULOGE("vertex shader (V) compilation failed '%s'", infoLog); - ret = -EPROTO; - goto error; - } - - fragmentShaderH = glCreateShader(GL_FRAGMENT_SHADER); - if ((fragmentShaderH == 0) || (fragmentShaderH == GL_INVALID_ENUM)) { - ULOGE("failed to create fragment shader"); - ret = -ENOMEM; - goto error; - } - - glShaderSource(fragmentShaderH, 1, &blurHFragmentShader, nullptr); - glCompileShader(fragmentShaderH); - glGetShaderiv(fragmentShaderH, GL_COMPILE_STATUS, &success); - if (!success) { - GLchar infoLog[512]; - glGetShaderInfoLog(fragmentShaderH, 512, nullptr, infoLog); - ULOGE("fragment shader compilation failed '%s'", infoLog); - ret = -EPROTO; - goto error; - } - - fragmentShaderV = glCreateShader(GL_FRAGMENT_SHADER); - if ((fragmentShaderV == 0) || (fragmentShaderV == GL_INVALID_ENUM)) { - ULOGE("failed to create fragment shader"); - ret = -ENOMEM; - goto error; - } - - glShaderSource(fragmentShaderV, 1, &blurVFragmentShader, nullptr); - glCompileShader(fragmentShaderV); - glGetShaderiv(fragmentShaderV, GL_COMPILE_STATUS, &success); - if (!success) { - GLchar infoLog[512]; - glGetShaderInfoLog(fragmentShaderV, 512, nullptr, infoLog); - ULOGE("fragment shader compilation failed '%s'", infoLog); - ret = -EPROTO; - goto error; - } - - /* Shaders link */ - mBlurProgram[0] = glCreateProgram(); - glAttachShader(mBlurProgram[0], vertexShaderH); - glAttachShader(mBlurProgram[0], fragmentShaderH); - glLinkProgram(mBlurProgram[0]); - glGetProgramiv(mBlurProgram[0], GL_LINK_STATUS, &success); - if (!success) { - GLchar infoLog[512]; - glGetProgramInfoLog(mBlurProgram[0], 512, nullptr, infoLog); - ULOGE("program link failed '%s'", infoLog); - ret = -EPROTO; - goto error; - } - - mBlurProgram[1] = glCreateProgram(); - glAttachShader(mBlurProgram[1], vertexShaderV); - glAttachShader(mBlurProgram[1], fragmentShaderV); - glLinkProgram(mBlurProgram[1]); - glGetProgramiv(mBlurProgram[1], GL_LINK_STATUS, &success); - if (!success) { - GLchar infoLog[512]; - glGetProgramInfoLog(mBlurProgram[1], 512, nullptr, infoLog); - ULOGE("program link failed '%s'", infoLog); - ret = -EPROTO; - goto error; - } - - glDeleteShader(vertexShaderH); - vertexShaderH = 0; - glDeleteShader(vertexShaderV); - vertexShaderV = 0; - glDeleteShader(fragmentShaderH); - fragmentShaderH = 0; - glDeleteShader(fragmentShaderV); - fragmentShaderV = 0; - - /* Attributes and uniforms handles */ - mBlurUniformPixelSize[0] = - glGetUniformLocation(mBlurProgram[0], "pixel_size"); - mBlurUniformWeights[0] = - glGetUniformLocation(mBlurProgram[0], "blur_weights"); - mBlurUniformSampler[0] = - glGetUniformLocation(mBlurProgram[0], "s_texture"); - mBlurPositionHandle[0] = - glGetAttribLocation(mBlurProgram[0], "position"); - mBlurUniformPixelSize[1] = - glGetUniformLocation(mBlurProgram[1], "pixel_size"); - mBlurUniformWeights[1] = - glGetUniformLocation(mBlurProgram[1], "blur_weights"); - mBlurUniformSampler[1] = - glGetUniformLocation(mBlurProgram[1], "s_texture"); - mBlurPositionHandle[1] = - glGetAttribLocation(mBlurProgram[1], "position"); - - mBlurInit = true; - return 0; - -error: - cleanupBlur(); - return ret; -} - - -void Gles2Video::cleanupBlur(void) -{ - cleanupBlurFbo(); - if (mBlurProgram[0] > 0) { - GLCHK(glDeleteProgram(mBlurProgram[0])); - mBlurProgram[0] = 0; - } - if (mBlurProgram[1] > 0) { - GLCHK(glDeleteProgram(mBlurProgram[1])); - mBlurProgram[1] = 0; - } - mBlurInit = false; -} - - -int Gles2Video::setupBlurFbo(void) -{ - int ret = 0; - unsigned int i; - GLenum gle; - - /* Free previous resources */ - cleanupBlurFbo(); - - if (!mBlurInit) - return 0; - - /* Render sizes */ - if (mVideoWidth > mVideoHeight) { - mBlurFboWidth = GLES2_VIDEO_BLUR_FBO_TARGET_SIZE; - mBlurFboHeight = (GLES2_VIDEO_BLUR_FBO_TARGET_SIZE * - mVideoHeight / mVideoWidth + - 3) & - ~3; - } else { - mBlurFboWidth = (GLES2_VIDEO_BLUR_FBO_TARGET_SIZE * - mVideoWidth / mVideoHeight + - 3) & - ~3; - mBlurFboHeight = GLES2_VIDEO_BLUR_FBO_TARGET_SIZE; - } - - /* Allocate FBOs and textures */ - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + - GLES2_VIDEO_TEX_UNIT_COUNT)); - for (i = 0; i < 2; i++) { - GLCHK(glGenFramebuffers(1, &mBlurFbo[i])); - if (mBlurFbo[i] <= 0) { - ULOGE("failed to create framebuffer"); - ret = -ENOMEM; - goto error; - } - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mBlurFbo[i])); - - GLCHK(glGenTextures(1, &mBlurFboTexture[i])); - if (mBlurFboTexture[i] <= 0) { - ULOGE("failed to create texture"); - ret = -ENOMEM; - goto error; - } - GLCHK(glBindTexture(GL_TEXTURE_2D, mBlurFboTexture[i])); - GLCHK(glTexImage2D(GL_TEXTURE_2D, - 0, - GL_RGB, - mBlurFboWidth, - mBlurFboHeight, - 0, - GL_RGB, - GL_UNSIGNED_BYTE, - nullptr)); - - GLCHK(glTexParameteri( - GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - GLCHK(glTexParameteri( - GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GLCHK(glTexParameterf( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GLCHK(glTexParameterf( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - - GLCHK(glFramebufferTexture2D(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - mBlurFboTexture[i], - 0)); - - gle = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (gle != GL_FRAMEBUFFER_COMPLETE) { - ULOGE("invalid framebuffer status"); - ret = -EPROTO; - goto error; - } - } - - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); - return 0; - -error: - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); - cleanupBlur(); - return ret; -} - - -void Gles2Video::cleanupBlurFbo(void) -{ - if (mBlurFboTexture[0] > 0) { - GLCHK(glDeleteTextures(2, mBlurFboTexture)); - memset(mBlurFboTexture, 0, sizeof(mBlurFboTexture)); - } - if (mBlurFbo[0] > 0) { - GLCHK(glDeleteFramebuffers(2, mBlurFbo)); - memset(mBlurFbo, 0, sizeof(mBlurFbo)); - } - mBlurFboWidth = 0; - mBlurFboHeight = 0; -} - - -void Gles2Video::renderBlur(size_t framePlaneStride[3], - const struct vdef_raw_format *format, - const struct vdef_frame_info *info, - const struct vdef_rect *crop, - const struct pdraw_rect *renderPos, - float videoW, - float videoH, - Eigen::Matrix4f &viewProjMat) -{ - unsigned int i; - float stride[GLES2_VIDEO_TEX_UNIT_COUNT * 2] = {0}; - float maxCoords[GLES2_VIDEO_TEX_UNIT_COUNT * 2] = {0}; - float vertices[12]; - float texCoords[8]; - bool mirrorTexture = false; - - if (!mBlurInit) - return; - - enum program prog; - prog = getProgram(format); - - /* Pass 1 downscale */ - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mBlurFbo[0])); - GLCHK(glViewport(0, 0, mBlurFboWidth, mBlurFboHeight)); - - GLCHK(glUseProgram(mProgram[prog])); - - switch (prog) { - default: - case PROGRAM_NOCONV: - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit)); -# ifdef BCM_VIDEOCORE - GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextures[0])); -# else /* BCM_VIDEOCORE */ - GLCHK(glBindTexture(GL_TEXTURE_2D, - (mExtTexture > 0) ? mExtTexture - : mTextures[0])); -# endif /* BCM_VIDEOCORE */ - GLCHK(glUniform1i(mUniformSamplers[prog][0], mFirstTexUnit)); - stride[0] = 1.f / framePlaneStride[0]; - stride[1] = 1.f / info->resolution.height; - maxCoords[0] = - (float)(crop->left + crop->width) / framePlaneStride[0]; - maxCoords[1] = (float)(crop->top + crop->height) / - info->resolution.height; - break; - case PROGRAM_YUV_TO_RGB_PLANAR: - case PROGRAM_YUV_TO_RGB_PLANAR_10_16LE: - mirrorTexture = true; - for (i = 0; i < GLES2_VIDEO_TEX_UNIT_COUNT; i++) { - int height = - info->resolution.height / ((i > 0) ? 2 : 1); - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + i)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[i])); - GLCHK(glUniform1i(mUniformSamplers[prog][i], - mFirstTexUnit + i)); - stride[2 * i] = 1.f / framePlaneStride[i]; - stride[2 * i + 1] = 1.f / height; - maxCoords[2 * i] = - (float)(crop->left + crop->width) / - (framePlaneStride[i] * ((i > 0) ? 2 : 1)); - maxCoords[2 * i + 1] = - (float)(crop->top + crop->height) / - info->resolution.height; - } - break; - case PROGRAM_YUV_TO_RGB_SEMIPLANAR: - case PROGRAM_YUV_TO_RGB_SEMIPLANAR_10_16LE_HIGH: - mirrorTexture = true; - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 0)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[0])); - GLCHK(glUniform1i(mUniformSamplers[prog][0], - mFirstTexUnit + 0)); - stride[0] = 1.f / framePlaneStride[0]; - stride[1] = 1.f / info->resolution.height; - maxCoords[0] = - (float)(crop->left + crop->width) / framePlaneStride[0]; - maxCoords[1] = (float)(crop->top + crop->height) / - info->resolution.height; - - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 1)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[1])); - GLCHK(glUniform1i(mUniformSamplers[prog][1], - mFirstTexUnit + 1)); - stride[2] = 1.f / (framePlaneStride[1] / 2); - stride[3] = 1.f / (info->resolution.height / 2); - maxCoords[2] = - (float)(crop->left + crop->width) / framePlaneStride[1]; - maxCoords[3] = (float)(crop->top + crop->height) / - info->resolution.height; - break; - } - - GLCHK(glUniform2fv( - mProgramStride[prog], GLES2_VIDEO_TEX_UNIT_COUNT, stride)); - GLCHK(glUniform2fv(mProgramMaxCoords[prog], - GLES2_VIDEO_TEX_UNIT_COUNT, - maxCoords)); - GLCHK(glUniform3f(mProgramYuv2RgbOffset[prog], - vdef_yuv_to_rgb_norm_offset[info->matrix_coefs] - [info->full_range][0], - vdef_yuv_to_rgb_norm_offset[info->matrix_coefs] - [info->full_range][1], - vdef_yuv_to_rgb_norm_offset[info->matrix_coefs] - [info->full_range][2])); - GLCHK(glUniformMatrix3fv( - mProgramYuv2RgbMatrix[prog], - 1, - GL_FALSE, - &vdef_yuv_to_rgb_norm_matrix[info->matrix_coefs] - [info->full_range][0])); - - /* Disable overexposure zebras */ - updateZebra(nullptr, prog, false, 0.f); - - vertices[0] = -1.; - vertices[1] = -1.; - vertices[2] = 1.; - vertices[3] = 1.; - vertices[4] = -1.; - vertices[5] = 1.; - vertices[6] = -1.; - vertices[7] = 1.; - vertices[8] = 1.; - vertices[9] = 1.; - vertices[10] = 1.; - vertices[11] = 1.; - - Eigen::Matrix4f id = Eigen::Matrix4f::Identity(); - GLCHK(glUniformMatrix4fv( - mProgramTransformMatrix[prog], 1, false, id.data())); - GLCHK(glUniform1f(mProgramSatCoef[prog], 1.f)); - GLCHK(glUniform1f(mProgramLightCoef[prog], 1.f)); - GLCHK(glUniform1f(mProgramDarkCoef[prog], 1.f)); - - GLCHK(glVertexAttribPointer( - mPositionHandle[prog], 3, GL_FLOAT, false, 0, vertices)); - GLCHK(glEnableVertexAttribArray(mPositionHandle[prog])); - - if (mirrorTexture) { - texCoords[0] = (float)crop->left / (float)framePlaneStride[0]; - texCoords[1] = (float)(crop->top + crop->height) / - (float)info->resolution.height; - texCoords[2] = (float)(crop->left + crop->width) / - (float)framePlaneStride[0]; - texCoords[3] = (float)(crop->top + crop->height) / - (float)info->resolution.height; - texCoords[4] = (float)crop->left / (float)framePlaneStride[0]; - texCoords[5] = - (float)crop->top / (float)info->resolution.height; - texCoords[6] = (float)(crop->left + crop->width) / - (float)framePlaneStride[0]; - texCoords[7] = - (float)crop->top / (float)info->resolution.height; - } else { - texCoords[0] = (float)crop->left / (float)framePlaneStride[0]; - texCoords[1] = - (float)crop->top / (float)info->resolution.height; - texCoords[2] = (float)(crop->left + crop->width) / - (float)framePlaneStride[0]; - texCoords[3] = - (float)crop->top / (float)info->resolution.height; - texCoords[4] = (float)crop->left / (float)framePlaneStride[0]; - texCoords[5] = (float)(crop->top + crop->height) / - (float)info->resolution.height; - texCoords[6] = (float)(crop->left + crop->width) / - (float)framePlaneStride[0]; - texCoords[7] = (float)(crop->top + crop->height) / - (float)info->resolution.height; - } - - GLCHK(glVertexAttribPointer( - mTexcoordHandle[prog], 2, GL_FLOAT, false, 0, texCoords)); - GLCHK(glEnableVertexAttribArray(mTexcoordHandle[prog])); - - GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - - GLCHK(glDisableVertexAttribArray(mPositionHandle[prog])); - GLCHK(glDisableVertexAttribArray(mTexcoordHandle[prog])); - - /* Horizontal blur pass */ - vertices[0] = -1.; - vertices[1] = -1.; - vertices[2] = 1.; - vertices[3] = -1.; - vertices[4] = -1.; - vertices[5] = 1.; - vertices[6] = 1.; - vertices[7] = 1.; - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mBlurFbo[1])); - GLCHK(glViewport(0, 0, mBlurFboWidth, mBlurFboHeight)); - GLCHK(glUseProgram(mBlurProgram[0])); - GLCHK(glUniform1fv(mBlurUniformWeights[0], - GLES2_VIDEO_BLUR_TAP_COUNT, - mBlurWeights)); - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + - GLES2_VIDEO_TEX_UNIT_COUNT)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mBlurFboTexture[0])); - GLCHK(glUniform1i(mBlurUniformSampler[0], - mFirstTexUnit + GLES2_VIDEO_TEX_UNIT_COUNT)); - GLCHK(glVertexAttribPointer( - mBlurPositionHandle[0], 2, GL_FLOAT, false, 0, vertices)); - GLCHK(glEnableVertexAttribArray(mBlurPositionHandle[0])); - GLCHK(glUniform1f(mBlurUniformPixelSize[0], 1.0 / mBlurFboWidth)); - GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - GLCHK(glDisableVertexAttribArray(mBlurPositionHandle[0])); - - /* Vertical blur pass */ - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mBlurFbo[0])); - GLCHK(glViewport(0, 0, mBlurFboWidth, mBlurFboHeight)); - GLCHK(glUseProgram(mBlurProgram[1])); - GLCHK(glUniform1fv(mBlurUniformWeights[1], - GLES2_VIDEO_BLUR_TAP_COUNT, - mBlurWeights)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mBlurFboTexture[1])); - GLCHK(glUniform1i(mBlurUniformSampler[1], - mFirstTexUnit + GLES2_VIDEO_TEX_UNIT_COUNT)); - GLCHK(glVertexAttribPointer( - mBlurPositionHandle[1], 2, GL_FLOAT, false, 0, vertices)); - GLCHK(glEnableVertexAttribArray(mBlurPositionHandle[1])); - GLCHK(glUniform1f(mBlurUniformPixelSize[1], 1.0 / mBlurFboHeight)); - GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - GLCHK(glDisableVertexAttribArray(mBlurPositionHandle[1])); - - /* Render to screen */ - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); - GLCHK(glViewport(renderPos->x, - renderPos->y, - renderPos->width, - renderPos->height)); - GLCHK(glUseProgram(mProgram[PROGRAM_NOCONV])); - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + - GLES2_VIDEO_TEX_UNIT_COUNT)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mBlurFboTexture[0])); - GLCHK(glUniform1i(mUniformSamplers[PROGRAM_NOCONV][0], - mFirstTexUnit + GLES2_VIDEO_TEX_UNIT_COUNT)); - GLCHK(glUniformMatrix4fv(mProgramTransformMatrix[PROGRAM_NOCONV], - 1, - false, - viewProjMat.data())); - GLCHK(glUniform1f(mProgramSatCoef[PROGRAM_NOCONV], mSatCoef)); - GLCHK(glUniform1f(mProgramLightCoef[PROGRAM_NOCONV], mLightCoef)); - GLCHK(glUniform1f(mProgramDarkCoef[PROGRAM_NOCONV], mDarkCoef)); - - maxCoords[0] = 1.; - maxCoords[1] = 1.; - stride[0] = 0.; - stride[1] = 0.; - GLCHK(glUniform2fv(mProgramStride[PROGRAM_NOCONV], - GLES2_VIDEO_TEX_UNIT_COUNT, - stride)); - GLCHK(glUniform2fv(mProgramMaxCoords[PROGRAM_NOCONV], - GLES2_VIDEO_TEX_UNIT_COUNT, - maxCoords)); - - vertices[0] = -videoW; - vertices[1] = -videoH; - vertices[2] = 1.; - vertices[3] = videoW; - vertices[4] = -videoH; - vertices[5] = 1.; - vertices[6] = -videoW; - vertices[7] = videoH; - vertices[8] = 1.; - vertices[9] = videoW; - vertices[10] = videoH; - vertices[11] = 1.; - - texCoords[0] = 0.; - texCoords[1] = 0.; - texCoords[2] = 1.; - texCoords[3] = 0.; - texCoords[4] = 0.; - texCoords[5] = 1.; - texCoords[6] = 1.; - texCoords[7] = 1.; - - GLCHK(glVertexAttribPointer(mPositionHandle[PROGRAM_NOCONV], - 3, - GL_FLOAT, - false, - 0, - vertices)); - GLCHK(glEnableVertexAttribArray(mPositionHandle[PROGRAM_NOCONV])); - - GLCHK(glVertexAttribPointer(mTexcoordHandle[PROGRAM_NOCONV], - 2, - GL_FLOAT, - false, - 0, - texCoords)); - GLCHK(glEnableVertexAttribArray(mTexcoordHandle[PROGRAM_NOCONV])); - - GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - - GLCHK(glDisableVertexAttribArray(mPositionHandle[PROGRAM_NOCONV])); - GLCHK(glDisableVertexAttribArray(mTexcoordHandle[PROGRAM_NOCONV])); - - GLCHK(glUseProgram(mProgram[prog])); -} - - -int Gles2Video::setupPaddingFbo(enum pdraw_video_renderer_fill_mode fillMode) -{ - GLenum gle; - unsigned int i; - - /* Free previous resources */ - cleanupPaddingFbo(); - - if (!mBlurInit) - return 0; - - if ((fillMode != PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_CROP) && - (fillMode != PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_EXTEND)) - return 0; - - /* Render sizes */ - if (mVideoWidth > mVideoHeight) { - mPaddingPass1Width = GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_1; - mPaddingPass1Height = (GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_1 * - mVideoHeight / mVideoWidth + - 3) & - ~3; - mPaddingPass2Width = GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_2; - mPaddingPass2Height = (GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_2 * - mVideoHeight / mVideoWidth + - 3) & - ~3; - } else { - mPaddingPass1Width = (GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_1 * - mVideoWidth / mVideoHeight + - 3) & - ~3; - mPaddingPass1Height = GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_1; - mPaddingPass2Width = (GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_2 * - mVideoWidth / mVideoHeight + - 3) & - ~3; - mPaddingPass2Height = GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_2; - } - - pdraw_gaussianDistribution(mPaddingBlurWeights, - GLES2_VIDEO_BLUR_TAP_COUNT, - GLES2_VIDEO_BLURRED_PADDING_SIGMA); - - /* Allocate new resources */ - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + - GLES2_VIDEO_TEX_UNIT_COUNT)); - for (i = 0; i < 4; i++) { - GLCHK(glGenFramebuffers(1, &mPaddingFbo[i])); - if (mPaddingFbo[i] <= 0) { - ULOGE("failed to create framebuffer"); - goto err; - } - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mPaddingFbo[i])); - - GLCHK(glGenTextures(1, &mPaddingFboTexture[i])); - if (mPaddingFboTexture[i] <= 0) { - ULOGE("failed to create texture"); - goto err; - } - GLCHK(glBindTexture(GL_TEXTURE_2D, mPaddingFboTexture[i])); - GLCHK(glTexImage2D( - GL_TEXTURE_2D, - 0, - GL_RGB, - (i < 2) ? mPaddingPass1Width : mPaddingPass2Width, - (i < 2) ? mPaddingPass1Height : mPaddingPass2Height, - 0, - GL_RGB, - GL_UNSIGNED_BYTE, - nullptr)); - - GLCHK(glTexParameteri( - GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - GLCHK(glTexParameteri( - GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GLCHK(glTexParameterf( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GLCHK(glTexParameterf( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - - GLCHK(glFramebufferTexture2D(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - mPaddingFboTexture[i], - 0)); - - gle = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (gle != GL_FRAMEBUFFER_COMPLETE) { - ULOGE("invalid framebuffer status"); - goto err; - } - } - - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); - return 0; - -err: - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); - cleanupPaddingFbo(); - return -EPROTO; -} - - -void Gles2Video::cleanupPaddingFbo(void) -{ - if (mPaddingFboTexture[0] > 0) { - GLCHK(glDeleteTextures(4, mPaddingFboTexture)); - memset(mPaddingFboTexture, 0, sizeof(mPaddingFboTexture)); - } - if (mPaddingFbo[0] > 0) { - GLCHK(glDeleteFramebuffers(4, mPaddingFbo)); - memset(mPaddingFbo, 0, sizeof(mPaddingFbo)); - } - mPaddingPass1Width = 0; - mPaddingPass1Height = 0; - mPaddingPass2Width = 0; - mPaddingPass2Height = 0; -} - - -void Gles2Video::renderPadding(size_t framePlaneStride[3], - const struct vdef_raw_format *format, - const struct vdef_frame_info *info, - const struct vdef_rect *crop, - const struct pdraw_rect *renderPos, - float videoW, - float videoH, - float videoW2, - float videoH2, - float videoAR, - float windowAR, - enum pdraw_video_renderer_fill_mode fillMode, - bool immersive, - Eigen::Matrix4f &viewProjMat) -{ - unsigned int i; - float stride[GLES2_VIDEO_TEX_UNIT_COUNT * 2] = {0}; - float maxCoords[GLES2_VIDEO_TEX_UNIT_COUNT * 2] = {0}; - float vertices[12]; - float texCoords[8]; - bool mirrorTexture = false; - - if (!mBlurInit) - return; - - if ((fillMode != PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_CROP) && - (fillMode != PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_EXTEND)) - return; - - enum program prog; - prog = getProgram(format); - - /* Pass 1 downscale */ - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mPaddingFbo[0])); - GLCHK(glViewport(0, 0, mPaddingPass1Width, mPaddingPass1Height)); - - GLCHK(glUseProgram(mProgram[prog])); - - switch (prog) { - default: - case PROGRAM_NOCONV: - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit)); -# ifdef BCM_VIDEOCORE - GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextures[0])); -# else /* BCM_VIDEOCORE */ - GLCHK(glBindTexture(GL_TEXTURE_2D, - (mExtTexture > 0) ? mExtTexture - : mTextures[0])); -# endif /* BCM_VIDEOCORE */ - GLCHK(glUniform1i(mUniformSamplers[prog][0], mFirstTexUnit)); - stride[0] = 1.f / framePlaneStride[0]; - stride[1] = 1.f / info->resolution.height; - maxCoords[0] = - (float)(crop->left + crop->width) / framePlaneStride[0]; - maxCoords[1] = (float)(crop->top + crop->height) / - info->resolution.height; - break; - case PROGRAM_YUV_TO_RGB_PLANAR: - case PROGRAM_YUV_TO_RGB_PLANAR_10_16LE: - mirrorTexture = true; - for (i = 0; i < GLES2_VIDEO_TEX_UNIT_COUNT; i++) { - int height = - info->resolution.height / ((i > 0) ? 2 : 1); - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + i)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[i])); - GLCHK(glUniform1i(mUniformSamplers[prog][i], - mFirstTexUnit + i)); - stride[2 * i] = 1.f / framePlaneStride[i]; - stride[2 * i + 1] = 1.f / height; - maxCoords[2 * i] = - (float)(crop->left + crop->width) / - (framePlaneStride[i] * ((i > 0) ? 2 : 1)); - maxCoords[2 * i + 1] = - (float)(crop->top + crop->height) / - info->resolution.height; - } - break; - case PROGRAM_YUV_TO_RGB_SEMIPLANAR: - case PROGRAM_YUV_TO_RGB_SEMIPLANAR_10_16LE_HIGH: - mirrorTexture = true; - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 0)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[0])); - GLCHK(glUniform1i(mUniformSamplers[prog][0], - mFirstTexUnit + 0)); - stride[0] = 1.f / framePlaneStride[0]; - stride[1] = 1.f / info->resolution.height; - maxCoords[0] = - (float)(crop->left + crop->width) / framePlaneStride[0]; - maxCoords[1] = (float)(crop->top + crop->height) / - info->resolution.height; - - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 1)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[1])); - GLCHK(glUniform1i(mUniformSamplers[prog][1], - mFirstTexUnit + 1)); - stride[2] = 1.f / (framePlaneStride[1] / 2); - stride[3] = 1.f / (info->resolution.height / 2); - maxCoords[2] = - (float)(crop->left + crop->width) / framePlaneStride[1]; - maxCoords[3] = (float)(crop->top + crop->height) / - info->resolution.height; - break; - } - - GLCHK(glUniform2fv( - mProgramStride[prog], GLES2_VIDEO_TEX_UNIT_COUNT, stride)); - GLCHK(glUniform2fv(mProgramMaxCoords[prog], - GLES2_VIDEO_TEX_UNIT_COUNT, - maxCoords)); - GLCHK(glUniform3f(mProgramYuv2RgbOffset[prog], - vdef_yuv_to_rgb_norm_offset[info->matrix_coefs] - [info->full_range][0], - vdef_yuv_to_rgb_norm_offset[info->matrix_coefs] - [info->full_range][1], - vdef_yuv_to_rgb_norm_offset[info->matrix_coefs] - [info->full_range][2])); - GLCHK(glUniformMatrix3fv( - mProgramYuv2RgbMatrix[prog], - 1, - GL_FALSE, - &vdef_yuv_to_rgb_norm_matrix[info->matrix_coefs] - [info->full_range][0])); - - /* Disable overexposure zebras */ - updateZebra(nullptr, prog, false, 0.f); - - vertices[0] = -1.; - vertices[1] = -1.; - vertices[2] = 1.; - vertices[3] = 1.; - vertices[4] = -1.; - vertices[5] = 1.; - vertices[6] = -1.; - vertices[7] = 1.; - vertices[8] = 1.; - vertices[9] = 1.; - vertices[10] = 1.; - vertices[11] = 1.; - - Eigen::Matrix4f id = Eigen::Matrix4f::Identity(); - GLCHK(glUniformMatrix4fv( - mProgramTransformMatrix[prog], 1, false, id.data())); - GLCHK(glUniform1f(mProgramSatCoef[prog], 1.f)); - GLCHK(glUniform1f(mProgramLightCoef[prog], 1.f)); - GLCHK(glUniform1f(mProgramDarkCoef[prog], 1.f)); - - GLCHK(glVertexAttribPointer( - mPositionHandle[prog], 3, GL_FLOAT, false, 0, vertices)); - GLCHK(glEnableVertexAttribArray(mPositionHandle[prog])); - - if (mirrorTexture) { - texCoords[0] = (float)crop->left / (float)framePlaneStride[0]; - texCoords[1] = (float)(crop->top + crop->height) / - (float)info->resolution.height; - texCoords[2] = (float)(crop->left + crop->width) / - (float)framePlaneStride[0]; - texCoords[3] = (float)(crop->top + crop->height) / - (float)info->resolution.height; - texCoords[4] = (float)crop->left / (float)framePlaneStride[0]; - texCoords[5] = - (float)crop->top / (float)info->resolution.height; - texCoords[6] = (float)(crop->left + crop->width) / - (float)framePlaneStride[0]; - texCoords[7] = - (float)crop->top / (float)info->resolution.height; - } else { - texCoords[0] = (float)crop->left / (float)framePlaneStride[0]; - texCoords[1] = - (float)crop->top / (float)info->resolution.height; - texCoords[2] = (float)(crop->left + crop->width) / - (float)framePlaneStride[0]; - texCoords[3] = - (float)crop->top / (float)info->resolution.height; - texCoords[4] = (float)crop->left / (float)framePlaneStride[0]; - texCoords[5] = (float)(crop->top + crop->height) / - (float)info->resolution.height; - texCoords[6] = (float)(crop->left + crop->width) / - (float)framePlaneStride[0]; - texCoords[7] = (float)(crop->top + crop->height) / - (float)info->resolution.height; - } - - GLCHK(glVertexAttribPointer( - mTexcoordHandle[prog], 2, GL_FLOAT, false, 0, texCoords)); - GLCHK(glEnableVertexAttribArray(mTexcoordHandle[prog])); - - GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - - GLCHK(glDisableVertexAttribArray(mPositionHandle[prog])); - GLCHK(glDisableVertexAttribArray(mTexcoordHandle[prog])); - - /* Pass 1 horizontal blur */ - vertices[0] = -1.; - vertices[1] = -1.; - vertices[2] = 1.; - vertices[3] = -1.; - vertices[4] = -1.; - vertices[5] = 1.; - vertices[6] = 1.; - vertices[7] = 1.; - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mPaddingFbo[1])); - GLCHK(glViewport(0, 0, mPaddingPass1Width, mPaddingPass1Height)); - GLCHK(glUseProgram(mBlurProgram[0])); - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + - GLES2_VIDEO_TEX_UNIT_COUNT)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mPaddingFboTexture[0])); - GLCHK(glUniform1i(mBlurUniformSampler[0], - mFirstTexUnit + GLES2_VIDEO_TEX_UNIT_COUNT)); - GLCHK(glVertexAttribPointer( - mBlurPositionHandle[0], 2, GL_FLOAT, false, 0, vertices)); - GLCHK(glEnableVertexAttribArray(mBlurPositionHandle[0])); - GLCHK(glUniform1f(mBlurUniformPixelSize[0], 1.0 / mPaddingPass1Width)); - GLCHK(glUniform1fv(mBlurUniformWeights[0], - GLES2_VIDEO_BLUR_TAP_COUNT, - mPaddingBlurWeights)); - GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - GLCHK(glDisableVertexAttribArray(mBlurPositionHandle[0])); - - /* Pass 1 vertical blur */ - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mPaddingFbo[2])); - GLCHK(glViewport(0, 0, mPaddingPass2Width, mPaddingPass2Height)); - GLCHK(glUseProgram(mBlurProgram[1])); - GLCHK(glBindTexture(GL_TEXTURE_2D, mPaddingFboTexture[1])); - GLCHK(glUniform1i(mBlurUniformSampler[1], - mFirstTexUnit + GLES2_VIDEO_TEX_UNIT_COUNT)); - GLCHK(glVertexAttribPointer( - mBlurPositionHandle[1], 2, GL_FLOAT, false, 0, vertices)); - GLCHK(glEnableVertexAttribArray(mBlurPositionHandle[1])); - GLCHK(glUniform1f(mBlurUniformPixelSize[1], 1.0 / mPaddingPass1Height)); - GLCHK(glUniform1fv(mBlurUniformWeights[1], - GLES2_VIDEO_BLUR_TAP_COUNT, - mPaddingBlurWeights)); - GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - GLCHK(glDisableVertexAttribArray(mBlurPositionHandle[1])); - - /* Pass 2 horizontal blur */ - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mPaddingFbo[3])); - GLCHK(glViewport(0, 0, mPaddingPass2Width, mPaddingPass2Height)); - GLCHK(glUseProgram(mBlurProgram[0])); - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + - GLES2_VIDEO_TEX_UNIT_COUNT)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mPaddingFboTexture[2])); - GLCHK(glUniform1i(mBlurUniformSampler[0], - mFirstTexUnit + GLES2_VIDEO_TEX_UNIT_COUNT)); - GLCHK(glVertexAttribPointer( - mBlurPositionHandle[0], 2, GL_FLOAT, false, 0, vertices)); - GLCHK(glEnableVertexAttribArray(mBlurPositionHandle[0])); - GLCHK(glUniform1f(mBlurUniformPixelSize[0], 1.0 / mPaddingPass2Width)); - GLCHK(glUniform1fv(mBlurUniformWeights[0], - GLES2_VIDEO_BLUR_TAP_COUNT, - mPaddingBlurWeights)); - GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - GLCHK(glDisableVertexAttribArray(mBlurPositionHandle[0])); - - /* Pass 2 vertical blur */ - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mPaddingFbo[2])); - GLCHK(glViewport(0, 0, mPaddingPass2Width, mPaddingPass2Height)); - GLCHK(glUseProgram(mBlurProgram[1])); - GLCHK(glBindTexture(GL_TEXTURE_2D, mPaddingFboTexture[3])); - GLCHK(glUniform1i(mBlurUniformSampler[1], - mFirstTexUnit + GLES2_VIDEO_TEX_UNIT_COUNT)); - GLCHK(glVertexAttribPointer( - mBlurPositionHandle[1], 2, GL_FLOAT, false, 0, vertices)); - GLCHK(glEnableVertexAttribArray(mBlurPositionHandle[1])); - GLCHK(glUniform1f(mBlurUniformPixelSize[1], 1.0 / mPaddingPass2Height)); - GLCHK(glUniform1fv(mBlurUniformWeights[1], - GLES2_VIDEO_BLUR_TAP_COUNT, - mPaddingBlurWeights)); - GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - GLCHK(glDisableVertexAttribArray(mBlurPositionHandle[1])); - - /* Render to screen */ - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); - GLCHK(glViewport(renderPos->x, - renderPos->y, - renderPos->width, - renderPos->height)); - GLCHK(glUseProgram(mProgram[PROGRAM_NOCONV])); - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + - GLES2_VIDEO_TEX_UNIT_COUNT)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mPaddingFboTexture[2])); - GLCHK(glUniform1i(mUniformSamplers[PROGRAM_NOCONV][0], - mFirstTexUnit + GLES2_VIDEO_TEX_UNIT_COUNT)); - GLCHK(glUniformMatrix4fv(mProgramTransformMatrix[PROGRAM_NOCONV], - 1, - false, - viewProjMat.data())); - GLCHK(glUniform1f(mProgramSatCoef[PROGRAM_NOCONV], mSatCoef)); - GLCHK(glUniform1f(mProgramLightCoef[PROGRAM_NOCONV], mLightCoef)); - GLCHK(glUniform1f(mProgramDarkCoef[PROGRAM_NOCONV], - (immersive) ? mDarkCoef - : PDRAW_BLURRED_PADDING_DARK_COEF * - mDarkCoef)); - - maxCoords[0] = 1.; - maxCoords[1] = 1.; - stride[0] = 0.; - stride[1] = 0.; - GLCHK(glUniform2fv(mProgramStride[PROGRAM_NOCONV], - GLES2_VIDEO_TEX_UNIT_COUNT, - stride)); - GLCHK(glUniform2fv(mProgramMaxCoords[PROGRAM_NOCONV], - GLES2_VIDEO_TEX_UNIT_COUNT, - maxCoords)); - - vertices[0] = -videoW2; - vertices[1] = -videoH2; - vertices[2] = 1.; - vertices[3] = videoW2; - vertices[4] = -videoH2; - vertices[5] = 1.; - vertices[6] = -videoW2; - vertices[7] = videoH2; - vertices[8] = 1.; - vertices[9] = videoW2; - vertices[10] = videoH2; - vertices[11] = 1.; - - if (fillMode == PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_EXTEND) { - texCoords[0] = -(videoW2 / videoW / 2.) + 0.5; - texCoords[1] = -(videoH2 / videoH / 2.) + 0.5; - texCoords[2] = videoW2 / videoW / 2. + 0.5; - texCoords[3] = -(videoH2 / videoH / 2.) + 0.5; - texCoords[4] = -(videoW2 / videoW / 2.) + 0.5; - texCoords[5] = videoH2 / videoH / 2. + 0.5; - texCoords[6] = videoW2 / videoW / 2. + 0.5; - texCoords[7] = videoH2 / videoH / 2. + 0.5; - } else { - texCoords[0] = 0.; - texCoords[1] = 0.; - texCoords[2] = 1.; - texCoords[3] = 0.; - texCoords[4] = 0.; - texCoords[5] = 1.; - texCoords[6] = 1.; - texCoords[7] = 1.; - } - - GLCHK(glVertexAttribPointer(mPositionHandle[PROGRAM_NOCONV], - 3, - GL_FLOAT, - false, - 0, - vertices)); - GLCHK(glEnableVertexAttribArray(mPositionHandle[PROGRAM_NOCONV])); - - GLCHK(glVertexAttribPointer(mTexcoordHandle[PROGRAM_NOCONV], - 2, - GL_FLOAT, - false, - 0, - texCoords)); - GLCHK(glEnableVertexAttribArray(mTexcoordHandle[PROGRAM_NOCONV])); - - GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - - GLCHK(glDisableVertexAttribArray(mPositionHandle[PROGRAM_NOCONV])); - GLCHK(glDisableVertexAttribArray(mTexcoordHandle[PROGRAM_NOCONV])); - - GLCHK(glUseProgram(mProgram[prog])); -} - - -void Gles2Video::setupZebra(enum program prog) -{ - float co = cosf(PDRAW_ZEBRA_ANGLE); - float si = sinf(PDRAW_ZEBRA_ANGLE); - const GLfloat zebra_mat[] = {co, si, -si, co}; - - GLCHK(glUseProgram(mProgram[prog])); - GLCHK(glUniformMatrix2fv( - glGetUniformLocation(mProgram[prog], "zebra_mat"), - 1, - GL_FALSE, - zebra_mat)); - GLCHK(glUniform1fv( - glGetUniformLocation(mProgram[prog], "zebra_avg_weights"), - 9, - zebraAvgWeights)); -} - - -void Gles2Video::updateZebra(struct pdraw_rect *contentPos, - enum program prog, - bool enable, - float threshold) -{ - GLCHK(glUniform1f(mProgramZebraEnable[prog], enable ? 1.f : 0.f)); - GLCHK(glUniform1f(mProgramZebraThreshold[prog], threshold)); - - if (enable && contentPos != nullptr) { - struct timespec ts; - uint64_t time_us; - if (time_get_monotonic(&ts) < 0) { - ULOGE("time_get_monotonic"); - return; - } - if (time_timespec_to_us(&ts, &time_us) < 0) { - ULOGE("time_timespec_to_us"); - return; - } - uint64_t zebra_period_us = - (uint64_t)(1000000.f / PDRAW_ZEBRA_FREQUENCY_HZ); - float zebra_phase = - (float)(time_us % zebra_period_us) / zebra_period_us; - GLCHK(glUniform1f(mProgramZebraPhase[prog], zebra_phase)); - float zebra_weight = - PDRAW_ZEBRA_WEIGHT * contentPos->width / 1920; - GLCHK(glUniform1f(mProgramZebraWeight[prog], zebra_weight)); - } -} - - -int Gles2Video::setupHistograms(void) -{ - int ret = 0; - GLenum gle; - GLint success = 0; - GLint vertexShaderHistogram = 0; - GLint fragmentShaderHistogram[PROGRAM_MAX] = {0}; - unsigned int i; - - /* Buffers allocation */ - mHistogramBuffer = - (uint8_t *)malloc(4 * GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE * - GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE); - if (mHistogramBuffer == nullptr) { - ULOG_ERRNO("malloc", ENOMEM); - ret = -ENOMEM; - goto error; - } - for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) { - mHistogram[i] = (uint32_t *)malloc(256 * sizeof(uint32_t)); - if (mHistogram[i] == nullptr) { - ULOG_ERRNO("malloc", ENOMEM); - ret = -ENOMEM; - goto error; - } - } - for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) { - mHistogramNorm[i] = (float *)calloc(256, sizeof(float)); - if (mHistogramNorm[i] == nullptr) { - ULOG_ERRNO("calloc", ENOMEM); - ret = -ENOMEM; - goto error; - } - } - - /* Shaders compilation */ - vertexShaderHistogram = glCreateShader(GL_VERTEX_SHADER); - if ((vertexShaderHistogram == 0) || - (vertexShaderHistogram == GL_INVALID_ENUM)) { - ULOGE("failed to create vertex shader"); - ret = -ENOMEM; - goto error; - } - - glShaderSource( - vertexShaderHistogram, 1, &histogramVertexShader, nullptr); - glCompileShader(vertexShaderHistogram); - glGetShaderiv(vertexShaderHistogram, GL_COMPILE_STATUS, &success); - if (!success) { - GLchar infoLog[512]; - glGetShaderInfoLog( - vertexShaderHistogram, 512, nullptr, infoLog); - ULOGE("vertex shader compilation failed '%s'", infoLog); - ret = -EPROTO; - goto error; - } - - for (i = 0; i < PROGRAM_MAX; i++) { - fragmentShaderHistogram[i] = glCreateShader(GL_FRAGMENT_SHADER); - if ((fragmentShaderHistogram[i] == 0) || - (fragmentShaderHistogram[i] == GL_INVALID_ENUM)) { - ULOGE("failed to create fragment shader"); - ret = -ENOMEM; - goto error; - } - - glShaderSource(fragmentShaderHistogram[i], - 2, - histogramFragmentShaders[i], - nullptr); - glCompileShader(fragmentShaderHistogram[i]); - glGetShaderiv(fragmentShaderHistogram[i], - GL_COMPILE_STATUS, - &success); - if (!success) { - GLchar infoLog[512]; - glGetShaderInfoLog(fragmentShaderHistogram[i], - 512, - nullptr, - infoLog); - ULOGE("fragment shader compilation failed '%s'", - infoLog); - ret = -EPROTO; - goto error; - } - } - - /* Shaders link */ - for (i = 0; i < PROGRAM_MAX; i++) { - mHistogramProgram[i] = glCreateProgram(); - glAttachShader(mHistogramProgram[i], vertexShaderHistogram); - glAttachShader(mHistogramProgram[i], - fragmentShaderHistogram[i]); - glLinkProgram(mHistogramProgram[i]); - glGetProgramiv(mHistogramProgram[i], GL_LINK_STATUS, &success); - if (!success) { - GLchar infoLog[512]; - glGetProgramInfoLog( - mHistogramProgram[i], 512, nullptr, infoLog); - ULOGE("program link failed '%s'", infoLog); - ret = -EPROTO; - goto error; - } - } - - glDeleteShader(vertexShaderHistogram); - vertexShaderHistogram = 0; - for (i = 0; i < PROGRAM_MAX; i++) { - glDeleteShader(fragmentShaderHistogram[i]); - fragmentShaderHistogram[i] = 0; - } - - /* Uniforms and attribs */ - for (i = 0; i < PROGRAM_MAX; i++) { - mHistogramYuv2RgbMatrix[i] = glGetUniformLocation( - mHistogramProgram[i], "yuv2rgb_mat"); - mHistogramYuv2RgbOffset[i] = glGetUniformLocation( - mHistogramProgram[i], "yuv2rgb_offset"); - mHistogramUniformSampler[i][0] = glGetUniformLocation( - mHistogramProgram[i], "s_texture_0"); - mHistogramUniformSampler[i][1] = glGetUniformLocation( - mHistogramProgram[i], "s_texture_1"); - mHistogramUniformSampler[i][2] = glGetUniformLocation( - mHistogramProgram[i], "s_texture_2"); - mHistogramPositionHandle[i] = - glGetAttribLocation(mHistogramProgram[i], "position"); - mHistogramTexcoordHandle[i] = - glGetAttribLocation(mHistogramProgram[i], "texcoord"); - } - - /* Create the framebuffer */ - GLCHK(glGenFramebuffers(1, &mHistogramFbo)); - if (mHistogramFbo <= 0) { - ULOGE("failed to create framebuffer"); - ret = -ENOMEM; - goto error; - } - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mHistogramFbo)); - - GLCHK(glGenTextures(1, &mHistogramFboTexture)); - if (mHistogramFboTexture <= 0) { - ULOGE("failed to create texture"); - ret = -ENOMEM; - goto error; - } - GLCHK(glBindTexture(GL_TEXTURE_2D, mHistogramFboTexture)); - GLCHK(glTexImage2D(GL_TEXTURE_2D, - 0, - GL_RGBA, - GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE, - GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE, - 0, - GL_RGBA, - GL_UNSIGNED_BYTE, - nullptr)); - - GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GLCHK(glTexParameterf( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GLCHK(glTexParameterf( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - - GLCHK(glFramebufferTexture2D(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - mHistogramFboTexture, - 0)); - - gle = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (gle != GL_FRAMEBUFFER_COMPLETE) { - ULOGE("invalid framebuffer status"); - ret = -EPROTO; - goto error; - } - - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); - mHistogramInit = true; - return 0; - -error: - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); - - if (vertexShaderHistogram) - glDeleteShader(vertexShaderHistogram); - for (i = 0; i < PROGRAM_MAX; i++) { - if (fragmentShaderHistogram[i]) - glDeleteShader(fragmentShaderHistogram[i]); - } - - cleanupHistograms(); - - return ret; -} - - -void Gles2Video::cleanupHistograms(void) -{ - unsigned int i; - - if (mHistogramFboTexture > 0) { - GLCHK(glDeleteTextures(1, &mHistogramFboTexture)); - mHistogramFboTexture = 0; - } - if (mHistogramFbo > 0) { - GLCHK(glDeleteFramebuffers(1, &mHistogramFbo)); - mHistogramFbo = 0; - } - for (i = 0; i < PROGRAM_MAX; i++) { - if (mHistogramProgram[i] > 0) { - GLCHK(glDeleteProgram(mHistogramProgram[i])); - mHistogramProgram[i] = 0; - } - } - free(mHistogramBuffer); - mHistogramBuffer = nullptr; - for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) { - free(mHistogram[i]); - mHistogram[i] = nullptr; - } - for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) { - free(mHistogramNorm[i]); - mHistogramNorm[i] = nullptr; - } - mHistogramInit = false; -} - - -void Gles2Video::computeHistograms(size_t framePlaneStride[3], - const struct vdef_raw_format *format, - const struct vdef_frame_info *info, - const struct vdef_rect *crop, - const struct pdraw_rect *renderPos, - bool enable) -{ - float vertices[12]; - float texCoords[8]; - unsigned int i, j; - uint8_t *buf; - uint32_t histoMax[PDRAW_HISTOGRAM_CHANNEL_MAX]; - struct timespec ts; - uint64_t time_us; - bool mirrorTexture = false; - - if ((!mHistogramInit) || (!enable)) { - mHistogramLastComputeTime = 0; - for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) - mHistogramValid[i] = false; - return; - } - - if (time_get_monotonic(&ts) < 0) { - ULOGE("time_get_monotonic"); - return; - } - if (time_timespec_to_us(&ts, &time_us) < 0) { - ULOGE("time_timespec_to_us"); - return; - } - if ((mHistogramLastComputeTime > 0) && - (time_us < mHistogramLastComputeTime + - GLES2_VIDEO_HISTOGRAM_COMPUTE_INTERVAL_US)) - return; - mHistogramLastComputeTime = time_us; - for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) - mHistogramValid[i] = false; - - enum program prog; - prog = getProgram(format); - - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mHistogramFbo)); - GLCHK(glDisable(GL_BLEND)); - GLCHK(glViewport(0, - 0, - GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE, - GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE)); - GLCHK(glUseProgram(mHistogramProgram[prog])); - - switch (prog) { - default: - case PROGRAM_NOCONV: - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit)); -# ifdef BCM_VIDEOCORE - GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextures[0])); -# else /* BCM_VIDEOCORE */ - GLCHK(glBindTexture(GL_TEXTURE_2D, - (mExtTexture > 0) ? mExtTexture - : mTextures[0])); -# endif /* BCM_VIDEOCORE */ - GLCHK(glUniform1i(mHistogramUniformSampler[prog][0], - mFirstTexUnit)); - break; - case PROGRAM_YUV_TO_RGB_PLANAR: - case PROGRAM_YUV_TO_RGB_PLANAR_10_16LE: - mirrorTexture = true; - for (i = 0; i < GLES2_VIDEO_TEX_UNIT_COUNT; i++) { - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + i)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[i])); - GLCHK(glUniform1i(mHistogramUniformSampler[prog][i], - mFirstTexUnit + i)); - } - break; - case PROGRAM_YUV_TO_RGB_SEMIPLANAR: - case PROGRAM_YUV_TO_RGB_SEMIPLANAR_10_16LE_HIGH: - mirrorTexture = true; - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 0)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[0])); - GLCHK(glUniform1i(mHistogramUniformSampler[prog][0], - mFirstTexUnit + 0)); - - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 1)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[1])); - GLCHK(glUniform1i(mHistogramUniformSampler[prog][1], - mFirstTexUnit + 1)); - break; - } - - GLCHK(glUniform3f(mHistogramYuv2RgbOffset[prog], - vdef_yuv_to_rgb_norm_offset[info->matrix_coefs] - [info->full_range][0], - vdef_yuv_to_rgb_norm_offset[info->matrix_coefs] - [info->full_range][1], - vdef_yuv_to_rgb_norm_offset[info->matrix_coefs] - [info->full_range][2])); - GLCHK(glUniformMatrix3fv( - mHistogramYuv2RgbMatrix[prog], - 1, - GL_FALSE, - &vdef_yuv_to_rgb_norm_matrix[info->matrix_coefs] - [info->full_range][0])); - - vertices[0] = -1.; - vertices[1] = -1.; - vertices[2] = 1.; - vertices[3] = 1.; - vertices[4] = -1.; - vertices[5] = 1.; - vertices[6] = -1.; - vertices[7] = 1.; - vertices[8] = 1.; - vertices[9] = 1.; - vertices[10] = 1.; - vertices[11] = 1.; - - GLCHK(glVertexAttribPointer(mHistogramPositionHandle[prog], - 3, - GL_FLOAT, - false, - 0, - vertices)); - GLCHK(glEnableVertexAttribArray(mHistogramPositionHandle[prog])); - - if (mirrorTexture) { - texCoords[0] = (float)crop->left / (float)framePlaneStride[0]; - texCoords[1] = (float)(crop->top + crop->height) / - (float)info->resolution.height; - texCoords[2] = (float)(crop->left + crop->width) / - (float)framePlaneStride[0]; - texCoords[3] = (float)(crop->top + crop->height) / - (float)info->resolution.height; - texCoords[4] = (float)crop->left / (float)framePlaneStride[0]; - texCoords[5] = - (float)crop->top / (float)info->resolution.height; - texCoords[6] = (float)(crop->left + crop->width) / - (float)framePlaneStride[0]; - texCoords[7] = - (float)crop->top / (float)info->resolution.height; - } else { - texCoords[0] = (float)crop->left / (float)framePlaneStride[0]; - texCoords[1] = - (float)crop->top / (float)info->resolution.height; - texCoords[2] = (float)(crop->left + crop->width) / - (float)framePlaneStride[0]; - texCoords[3] = - (float)crop->top / (float)info->resolution.height; - texCoords[4] = (float)crop->left / (float)framePlaneStride[0]; - texCoords[5] = (float)(crop->top + crop->height) / - (float)info->resolution.height; - texCoords[6] = (float)(crop->left + crop->width) / - (float)framePlaneStride[0]; - texCoords[7] = (float)(crop->top + crop->height) / - (float)info->resolution.height; - } - - GLCHK(glVertexAttribPointer(mHistogramTexcoordHandle[prog], - 2, - GL_FLOAT, - false, - 0, - texCoords)); - GLCHK(glEnableVertexAttribArray(mHistogramTexcoordHandle[prog])); - - GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - - GLCHK(glDisableVertexAttribArray(mHistogramPositionHandle[prog])); - GLCHK(glDisableVertexAttribArray(mHistogramTexcoordHandle[prog])); - - GLCHK(glFinish()); - - /* Read pixels to CPU buffer */ - GLCHK(glReadPixels(0, - 0, - GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE, - GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE, - GL_RGBA, - GL_UNSIGNED_BYTE, - mHistogramBuffer)); - - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); - GLCHK(glViewport(renderPos->x, - renderPos->y, - renderPos->width, - renderPos->height)); - - /* Reset the histograms */ - for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) - memset(mHistogram[i], 0, 256 * sizeof(uint32_t)); - - /* Count the values */ - for (j = 0, buf = mHistogramBuffer; - j < GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE * - GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE; - j++) { - for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) - mHistogram[i][*buf++]++; - } - - /* Histograms normalization */ - memset(histoMax, 0, sizeof(histoMax)); - for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) { - for (j = 0; j < 256; j++) { - if (mHistogram[i][j] > histoMax[i]) - histoMax[i] = mHistogram[i][j]; - } - } - histoMax[1] = (histoMax[0] > histoMax[1]) ? histoMax[0] : histoMax[1]; - histoMax[1] = (histoMax[1] > histoMax[2]) ? histoMax[1] : histoMax[2]; - histoMax[2] = histoMax[1]; - histoMax[0] = histoMax[1]; - for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) { - if (histoMax[i] == 0.) { - memset(mHistogramNorm[i], 0, 256 * sizeof(float)); - continue; - } - for (j = 0; j < 256; j++) { - mHistogramNorm[i][j] = - (float)mHistogram[i][j] / (float)histoMax[i]; - } - mHistogramValid[i] = true; - } -} - - -void Gles2Video::getHistograms(float *histogram[PDRAW_HISTOGRAM_CHANNEL_MAX], - size_t histogramLen[PDRAW_HISTOGRAM_CHANNEL_MAX]) -{ - unsigned int i; - - for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) { - if ((mHistogramValid[i]) && (mHistogramNorm[i] != nullptr)) { - histogram[i] = mHistogramNorm[i]; - histogramLen[i] = 256; - } - } -} - - -void Gles2Video::startTransition(enum gles2_video_transition transition, - uint64_t duration, - bool hold) -{ - if (mTransition != GLES2_VIDEO_TRANSITION_NONE) - abortTransition(); - mTransition = transition; - mTransitionDuration = duration; - mTransitionHold = hold; -} - - -void Gles2Video::abortTransition(void) -{ - mTransition = GLES2_VIDEO_TRANSITION_NONE; - mTransitionDuration = 0; - mTransitionStartTime = 0; - mTransitionHold = false; -} - - -void Gles2Video::updateTransition(void) -{ - int res; - struct timespec ts = {0, 0}; - uint64_t curTime = 0; - float progress, blurSigma; - - mApplyBlur = false; - mSatCoef = mBaseSatCoef; - mLightCoef = mBaseLightCoef; - mDarkCoef = mBaseDarkCoef; - - if (mTransition == GLES2_VIDEO_TRANSITION_NONE) - return; - - res = time_get_monotonic(&ts); - if (res < 0) - ULOG_ERRNO("time_get_monotonic", -res); - res = time_timespec_to_us(&ts, &curTime); - if (res < 0) - ULOG_ERRNO("time_timespec_to_us", -res); - if (mTransitionStartTime == 0) - mTransitionStartTime = curTime; - - progress = (float)(curTime - mTransitionStartTime) / - (float)mTransitionDuration; - if (progress > 1.0) { - progress = 1.0; - if (!mTransitionHold) { - /* Transition finished */ - abortTransition(); - return; - } - } - - switch (mTransition) { - case GLES2_VIDEO_TRANSITION_FADE_TO_BLACK: - mDarkCoef = mBaseDarkCoef * (1. - progress); - break; - case GLES2_VIDEO_TRANSITION_FADE_FROM_BLACK: - mDarkCoef = mBaseDarkCoef * progress; - break; - case GLES2_VIDEO_TRANSITION_FADE_TO_WHITE: - mLightCoef = mBaseLightCoef * (1. - progress); - break; - case GLES2_VIDEO_TRANSITION_FADE_FROM_WHITE: - mLightCoef = mBaseLightCoef * progress; - break; - case GLES2_VIDEO_TRANSITION_FADE_TO_BLACK_AND_WHITE: - mSatCoef = mBaseSatCoef * (1. - progress); - break; - case GLES2_VIDEO_TRANSITION_FADE_FROM_BLACK_AND_WHITE: - mSatCoef = mBaseSatCoef * progress; - break; - case GLES2_VIDEO_TRANSITION_FADE_TO_BLUR: - blurSigma = progress * (GLES2_VIDEO_BLUR_MAX_SIGMA - - GLES2_VIDEO_BLUR_MIN_SIGMA) + - GLES2_VIDEO_BLUR_MIN_SIGMA; - pdraw_gaussianDistribution( - mBlurWeights, GLES2_VIDEO_BLUR_TAP_COUNT, blurSigma); - mApplyBlur = mBlurInit; - break; - case GLES2_VIDEO_TRANSITION_FADE_FROM_BLUR: - blurSigma = (1. - progress) * (GLES2_VIDEO_BLUR_MAX_SIGMA - - GLES2_VIDEO_BLUR_MIN_SIGMA) + - GLES2_VIDEO_BLUR_MIN_SIGMA; - pdraw_gaussianDistribution( - mBlurWeights, GLES2_VIDEO_BLUR_TAP_COUNT, blurSigma); - mApplyBlur = mBlurInit; - break; - case GLES2_VIDEO_TRANSITION_FLASH: - mLightCoef = mBaseLightCoef * - (powf(progress, GLES2_VIDEO_FLASH_GAMMA_COEF) * - GLES2_VIDEO_FLASH_LIGHT_COEF + - 1. - GLES2_VIDEO_FLASH_LIGHT_COEF); - mSatCoef = mBaseSatCoef * - powf(progress, GLES2_VIDEO_FLASH_GAMMA_COEF); - break; - default: - ULOGE("unsupported transition type: %d", mTransition); - break; - } -} - - -int Gles2Video::loadFrame(const uint8_t *framePlanes[3], - size_t framePlaneStride[3], - const struct vdef_raw_format *format, - const struct vdef_frame_info *info, - struct egl_display *eglDisplay) -{ - unsigned int i; - - if ((info == nullptr) || (format == nullptr)) { - ULOGE("invalid frame info"); - return -EINVAL; - } - if ((info->resolution.width == 0) || (info->resolution.height == 0)) { - ULOGE("invalid dimensions"); - return -EINVAL; - } - - enum program prog; - prog = getProgram(format); - - GLCHK(glUseProgram(mProgram[prog])); - - switch (prog) { - default: - case PROGRAM_NOCONV: { -# ifdef BCM_VIDEOCORE - EGLDisplay display = (EGLDisplay)eglDisplay; - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit)); - GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextures[0])); - if (mEglImage != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(display, mEglImage); - mEglImage = EGL_NO_IMAGE_KHR; - } - mEglImage = eglCreateImageKHR(display, - EGL_NO_CONTEXT, - EGL_IMAGE_BRCM_MULTIMEDIA, - (EGLClientBuffer)framePlanes[0], - nullptr); - if (mEglImage == EGL_NO_IMAGE_KHR) { - ULOGE("failed to create EGLImage"); - return -EPROTO; - } - GLCHK(glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, - mEglImage)); - GLCHK(glUniform1i(mUniformSamplers[prog][0], mFirstTexUnit)); -# endif /* BCM_VIDEOCORE */ - break; - } - case PROGRAM_YUV_TO_RGB_PLANAR: - if ((framePlanes == nullptr) || (framePlaneStride == nullptr)) { - ULOGE("invalid planes"); - return -EINVAL; - } - for (i = 0; i < GLES2_VIDEO_TEX_UNIT_COUNT; i++) { - int height = - info->resolution.height / ((i > 0) ? 2 : 1); - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + i)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[i])); - GLCHK(glTexImage2D(GL_TEXTURE_2D, - 0, - GL_LUMINANCE, - framePlaneStride[i], - height, - 0, - GL_LUMINANCE, - GL_UNSIGNED_BYTE, - framePlanes[i])); - } - break; - case PROGRAM_YUV_TO_RGB_PLANAR_10_16LE: - if ((framePlanes == nullptr) || (framePlaneStride == nullptr)) { - ULOGE("invalid planes"); - return -EINVAL; - } - for (i = 0; i < GLES2_VIDEO_TEX_UNIT_COUNT; i++) { - int height = - info->resolution.height / ((i > 0) ? 2 : 1); - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + i)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[i])); - GLCHK(glTexImage2D(GL_TEXTURE_2D, - 0, - GL_LUMINANCE_ALPHA, - framePlaneStride[i] / 2, - height, - 0, - GL_LUMINANCE_ALPHA, - GL_UNSIGNED_BYTE, - framePlanes[i])); - } - break; - case PROGRAM_YUV_TO_RGB_SEMIPLANAR: - if ((framePlanes == nullptr) || (framePlaneStride == nullptr)) { - ULOGE("invalid planes"); - return -EINVAL; - } - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 0)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[0])); - GLCHK(glTexImage2D(GL_TEXTURE_2D, - 0, - GL_LUMINANCE, - framePlaneStride[0], - info->resolution.height, - 0, - GL_LUMINANCE, - GL_UNSIGNED_BYTE, - framePlanes[0])); - - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 1)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[1])); - GLCHK(glTexImage2D(GL_TEXTURE_2D, - 0, - GL_LUMINANCE_ALPHA, - framePlaneStride[1] / 2, - info->resolution.height / 2, - 0, - GL_LUMINANCE_ALPHA, - GL_UNSIGNED_BYTE, - framePlanes[1])); - break; - case PROGRAM_YUV_TO_RGB_SEMIPLANAR_10_16LE_HIGH: - if ((framePlanes == nullptr) || (framePlaneStride == nullptr)) { - ULOGE("invalid planes"); - return -EINVAL; - } - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 0)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[0])); - GLCHK(glTexImage2D(GL_TEXTURE_2D, - 0, - GL_LUMINANCE_ALPHA, - framePlaneStride[0] / 2, - info->resolution.height, - 0, - GL_LUMINANCE_ALPHA, - GL_UNSIGNED_BYTE, - framePlanes[0])); - - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 1)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[1])); - GLCHK(glTexImage2D(GL_TEXTURE_2D, - 0, - GL_RGBA, - framePlaneStride[1] / 4, - info->resolution.height / 2, - 0, - GL_RGBA, - GL_UNSIGNED_BYTE, - framePlanes[1])); - break; - } - - return 0; -} - - -int Gles2Video::renderFrame(const struct pdraw_rect *renderPos, - struct pdraw_rect *contentPos, - Eigen::Matrix4f &viewProjMat, - size_t framePlaneStride[3], - const struct vdef_raw_format *format, - const struct vdef_frame_info *info, - const struct vdef_rect *crop, - struct vmeta_frame *metadata, - const struct pdraw_video_renderer_params *params) -{ - int ret; - unsigned int i; - float stride[GLES2_VIDEO_TEX_UNIT_COUNT * 2] = {0}; - float maxCoords[GLES2_VIDEO_TEX_UNIT_COUNT * 2] = {0}; - float vertices[12]; - float texCoords[8]; - bool mirrorTexture = false; - float videoAR; - - if ((renderPos == nullptr) || (renderPos->width == 0) || - (renderPos->height == 0)) { - ULOGE("invalid render position"); - return -EINVAL; - } - if ((info == nullptr) || (format == nullptr)) { - ULOGE("invalid frame info"); - return -EINVAL; - } - if ((info->resolution.width == 0) || (info->resolution.height == 0) || - (info->sar.width == 0) || (info->sar.height == 0) || - (framePlaneStride[0] == 0)) { - ULOGE("invalid dimensions"); - return -EINVAL; - } - - struct vdef_frame_info _info = *info; - if (_info.matrix_coefs == VDEF_MATRIX_COEFS_UNKNOWN) { - /* Default to BT.709 */ - _info.matrix_coefs = VDEF_MATRIX_COEFS_BT709; - } - - enum program prog; - prog = getProgram(format); - - if ((mVideoWidth != _info.resolution.width) || - (mVideoHeight != _info.resolution.height)) { - mVideoWidth = _info.resolution.width; - mVideoHeight = _info.resolution.height; - ret = setupBlurFbo(); - if (ret < 0) - ULOG_ERRNO("setupBlurFbo", -ret); - ret = setupPaddingFbo(params->fill_mode); - if (ret < 0) - ULOG_ERRNO("setupPaddingFbo", -ret); - } - - if (prog == PROGRAM_YUV_TO_RGB_PLANAR_10_16LE) { - framePlaneStride[0] /= 2; - framePlaneStride[1] /= 2; - framePlaneStride[2] /= 2; - } else if (prog == PROGRAM_YUV_TO_RGB_SEMIPLANAR_10_16LE_HIGH) { - framePlaneStride[0] /= 2; - framePlaneStride[1] /= 2; - } - - updateTransition(); - - computeHistograms(framePlaneStride, - format, - &_info, - crop, - renderPos, - params->enable_histograms); - - /* Video fill mode */ - float windowAR = (float)renderPos->width / (float)renderPos->height; - float sar = (float)_info.sar.width / (float)_info.sar.height; - if (params->video_texture_dar_height != 0 && - params->video_texture_dar_width != 0) { - /* If the display aspect ratio is given, - * we apply it instead of the source width/height */ - videoAR = (float)params->video_texture_dar_width / - (float)params->video_texture_dar_height; - } else { - videoAR = (float)_info.resolution.width / - (float)_info.resolution.height * sar; - } - - float windowW = 1.; - float windowH = windowAR; - float ratioW = 1.; - float ratioH = 1.; - float ratioW2 = 1.; - float ratioH2 = 1.; - switch (params->fill_mode) { - default: - case PDRAW_VIDEO_RENDERER_FILL_MODE_FIT: - case PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_CROP: - case PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_EXTEND: - /* Maintain video aspect ratio without crop and add borders if - * window aspect ratio and video aspect ratio differ */ - if (videoAR >= windowAR) { - ratioW = 1.; - ratioH = windowAR / videoAR; - ratioW2 = videoAR / windowAR; - ratioH2 = 1.; - } else { - ratioW = videoAR / windowAR; - ratioH = 1.; - ratioW2 = 1.; - ratioH2 = windowAR / videoAR; - } - break; - case PDRAW_VIDEO_RENDERER_FILL_MODE_CROP: - /* Maintain video aspect ratio without borders and add video - * crop if window aspect ratio and video aspect ratio differ */ - if (videoAR >= windowAR) { - ratioW = videoAR / windowAR; - ratioH = 1.; - } else { - ratioW = 1.; - ratioH = windowAR / videoAR; - } - break; - } - float videoW = ratioW / windowW * params->video_scale_factor; - float videoH = ratioH / windowH * params->video_scale_factor; - float videoW2 = ratioW2 / windowW; - float videoH2 = ratioH2 / windowH; - - if (contentPos) { - int32_t dw; - int32_t dh; - contentPos->width = - ratioW * renderPos->width * params->video_scale_factor; - contentPos->height = - ratioH * renderPos->height * params->video_scale_factor; - - dw = (int32_t)renderPos->width - (int32_t)contentPos->width; - dh = (int32_t)renderPos->height - (int32_t)contentPos->height; - contentPos->x = dw / 2; - contentPos->y = dh / 2; - } - - renderPadding(framePlaneStride, - format, - &_info, - crop, - renderPos, - videoW, - videoH, - videoW2, - videoH2, - videoAR, - windowAR, - params->fill_mode, - false, - viewProjMat); - - if (mApplyBlur) { - renderBlur(framePlaneStride, - format, - &_info, - crop, - renderPos, - videoW, - videoH, - viewProjMat); - } else { - GLCHK(glUseProgram(mProgram[prog])); - - switch (prog) { - default: - case PROGRAM_NOCONV: - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit)); -# ifdef BCM_VIDEOCORE - GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, - mTextures[0])); -# else /* BCM_VIDEOCORE */ - GLCHK(glBindTexture(GL_TEXTURE_2D, - (mExtTexture > 0) ? mExtTexture - : mTextures[0])); -# endif /* BCM_VIDEOCORE */ - GLCHK(glUniform1i(mUniformSamplers[prog][0], - mFirstTexUnit)); - stride[0] = 1.f / framePlaneStride[0]; - stride[1] = 1.f / _info.resolution.height; - maxCoords[0] = (float)(crop->left + crop->width) / - framePlaneStride[0]; - maxCoords[1] = (float)(crop->top + crop->height) / - _info.resolution.height; - break; - case PROGRAM_YUV_TO_RGB_PLANAR: - case PROGRAM_YUV_TO_RGB_PLANAR_10_16LE: - mirrorTexture = true; - for (i = 0; i < GLES2_VIDEO_TEX_UNIT_COUNT; i++) { - int height = _info.resolution.height / - ((i > 0) ? 2 : 1); - GLCHK(glActiveTexture(GL_TEXTURE0 + - mFirstTexUnit + i)); - GLCHK(glBindTexture(GL_TEXTURE_2D, - mTextures[i])); - GLCHK(glUniform1i(mUniformSamplers[prog][i], - mFirstTexUnit + i)); - stride[2 * i] = 1.f / framePlaneStride[i]; - stride[2 * i + 1] = 1.f / height; - maxCoords[2 * i] = - (float)(crop->left + crop->width) / - (framePlaneStride[i] * - ((i > 0) ? 2 : 1)); - maxCoords[2 * i + 1] = - (float)(crop->top + crop->height) / - _info.resolution.height; - } - break; - case PROGRAM_YUV_TO_RGB_SEMIPLANAR: - case PROGRAM_YUV_TO_RGB_SEMIPLANAR_10_16LE_HIGH: - mirrorTexture = true; - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 0)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[0])); - GLCHK(glUniform1i(mUniformSamplers[prog][0], - mFirstTexUnit + 0)); - stride[0] = 1.f / framePlaneStride[0]; - stride[1] = 1.f / _info.resolution.height; - maxCoords[0] = (float)(crop->left + crop->width) / - framePlaneStride[0]; - maxCoords[1] = (float)(crop->top + crop->height) / - _info.resolution.height; - - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 1)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[1])); - GLCHK(glUniform1i(mUniformSamplers[prog][1], - mFirstTexUnit + 1)); - stride[2] = 1.f / (framePlaneStride[1] / 2); - stride[3] = 1.f / (_info.resolution.height / 2); - maxCoords[2] = (float)(crop->left + crop->width) / - framePlaneStride[1]; - maxCoords[3] = (float)(crop->top + crop->height) / - _info.resolution.height; - break; - } - - GLCHK(glUniform2fv(mProgramStride[prog], - GLES2_VIDEO_TEX_UNIT_COUNT, - stride)); - GLCHK(glUniform2fv(mProgramMaxCoords[prog], - GLES2_VIDEO_TEX_UNIT_COUNT, - maxCoords)); - GLCHK(glUniform3f( - mProgramYuv2RgbOffset[prog], - vdef_yuv_to_rgb_norm_offset[_info.matrix_coefs] - [_info.full_range][0], - vdef_yuv_to_rgb_norm_offset[_info.matrix_coefs] - [_info.full_range][1], - vdef_yuv_to_rgb_norm_offset[_info.matrix_coefs] - [_info.full_range][2])); - GLCHK(glUniformMatrix3fv( - mProgramYuv2RgbMatrix[prog], - 1, - GL_FALSE, - &vdef_yuv_to_rgb_norm_matrix[_info.matrix_coefs] - [_info.full_range][0])); - - /* Update overexposure zebras */ - updateZebra(contentPos, - prog, - params->enable_overexposure_zebras, - params->overexposure_zebras_threshold); - - GLCHK(glUniformMatrix4fv(mProgramTransformMatrix[prog], - 1, - false, - viewProjMat.data())); - GLCHK(glUniform1f(mProgramSatCoef[prog], mSatCoef)); - GLCHK(glUniform1f(mProgramLightCoef[prog], mLightCoef)); - GLCHK(glUniform1f(mProgramDarkCoef[prog], mDarkCoef)); - - vertices[0] = -videoW; - vertices[1] = -videoH; - vertices[2] = 1.; - vertices[3] = videoW; - vertices[4] = -videoH; - vertices[5] = 1.; - vertices[6] = -videoW; - vertices[7] = videoH; - vertices[8] = 1.; - vertices[9] = videoW; - vertices[10] = videoH; - vertices[11] = 1.; - - GLCHK(glVertexAttribPointer(mPositionHandle[prog], - 3, - GL_FLOAT, - false, - 0, - vertices)); - GLCHK(glEnableVertexAttribArray(mPositionHandle[prog])); - - if (mirrorTexture) { - texCoords[0] = - (float)crop->left / (float)framePlaneStride[0]; - texCoords[1] = (float)(crop->top + crop->height) / - (float)_info.resolution.height; - texCoords[2] = (float)(crop->left + crop->width) / - (float)framePlaneStride[0]; - texCoords[3] = (float)(crop->top + crop->height) / - (float)_info.resolution.height; - texCoords[4] = - (float)crop->left / (float)framePlaneStride[0]; - texCoords[5] = (float)crop->top / - (float)_info.resolution.height; - texCoords[6] = (float)(crop->left + crop->width) / - (float)framePlaneStride[0]; - texCoords[7] = (float)crop->top / - (float)_info.resolution.height; - } else { - texCoords[0] = - (float)crop->left / (float)framePlaneStride[0]; - texCoords[1] = (float)crop->top / - (float)_info.resolution.height; - texCoords[2] = (float)(crop->left + crop->width) / - (float)framePlaneStride[0]; - texCoords[3] = (float)crop->top / - (float)_info.resolution.height; - texCoords[4] = - (float)crop->left / (float)framePlaneStride[0]; - texCoords[5] = (float)(crop->top + crop->height) / - (float)_info.resolution.height; - texCoords[6] = (float)(crop->left + crop->width) / - (float)framePlaneStride[0]; - texCoords[7] = (float)(crop->top + crop->height) / - (float)_info.resolution.height; - } - - GLCHK(glVertexAttribPointer(mTexcoordHandle[prog], - 2, - GL_FLOAT, - false, - 0, - texCoords)); - GLCHK(glEnableVertexAttribArray(mTexcoordHandle[prog])); - - GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - - GLCHK(glDisableVertexAttribArray(mPositionHandle[prog])); - GLCHK(glDisableVertexAttribArray(mTexcoordHandle[prog])); - } - - return 0; -} - - -int Gles2Video::clear(Eigen::Matrix4f &viewProjMat) -{ - float vertices[12]; - float texCoords[8]; - - GLCHK(glUseProgram(mProgram[PROGRAM_NOCONV])); - - GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit)); -# ifdef BCM_VIDEOCORE - GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextures[0])); -# else /* BCM_VIDEOCORE */ - GLCHK(glBindTexture(GL_TEXTURE_2D, - (mExtTexture > 0) ? mExtTexture : mTextures[0])); -# endif /* BCM_VIDEOCORE */ - GLCHK(glUniform1i(mUniformSamplers[PROGRAM_NOCONV][0], mFirstTexUnit)); - - GLCHK(glUniformMatrix4fv(mProgramTransformMatrix[PROGRAM_NOCONV], - 1, - false, - viewProjMat.data())); - GLCHK(glUniform1f(mProgramDarkCoef[PROGRAM_NOCONV], 0.)); - - vertices[0] = -1.; - vertices[1] = -1.; - vertices[2] = 1.; - vertices[3] = 1.; - vertices[4] = -1.; - vertices[5] = 1.; - vertices[6] = -1.; - vertices[7] = 1.; - vertices[8] = 1.; - vertices[9] = 1.; - vertices[10] = 1.; - vertices[11] = 1.; - - GLCHK(glVertexAttribPointer(mPositionHandle[PROGRAM_NOCONV], - 3, - GL_FLOAT, - false, - 0, - vertices)); - GLCHK(glEnableVertexAttribArray(mPositionHandle[PROGRAM_NOCONV])); - - texCoords[0] = 0.; - texCoords[1] = 0.; - texCoords[2] = 1.; - texCoords[3] = 0.; - texCoords[4] = 0.; - texCoords[5] = 1.; - texCoords[6] = 1.; - texCoords[7] = 1.; - - GLCHK(glVertexAttribPointer(mTexcoordHandle[PROGRAM_NOCONV], - 2, - GL_FLOAT, - false, - 0, - texCoords)); - GLCHK(glEnableVertexAttribArray(mTexcoordHandle[PROGRAM_NOCONV])); - - GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); - - GLCHK(glDisableVertexAttribArray(mPositionHandle[PROGRAM_NOCONV])); - GLCHK(glDisableVertexAttribArray(mTexcoordHandle[PROGRAM_NOCONV])); - - return 0; -} - - -void Gles2Video::setExtTexture(GLuint texture) -{ - mExtTexture = texture; -} - -} /* namespace Pdraw */ - -#endif /* USE_GLES2 */ +/** + * Parrot Drones Awesome Video Viewer Library + * OpenGL ES 2.0 video rendering + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_gles2vid +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_gles2_video.hpp" + +#ifdef USE_GLES2 + +# include "pdraw_session.hpp" + +# include +# ifdef BCM_VIDEOCORE +# include +# include +# include +# endif /* BCM_VIDEOCORE */ + +# include + +namespace Pdraw { + + +/* Blurred padding dark coef */ +# define PDRAW_BLURRED_PADDING_DARK_COEF (0.75f) + +/* The temporal frequency of zebra pattern */ +# define PDRAW_ZEBRA_FREQUENCY_HZ (1.f) +/* The angle of zebra pattern relative to y axis */ +# define PDRAW_ZEBRA_ANGLE (60.f * M_PI / 180.f) +/* The weight in pixels of zebra pattern, relative to 1920 width */ +# define PDRAW_ZEBRA_WEIGHT (8.f) + +# define GLES2_VIDEO_BLUR_MIN_SIGMA 0.8f +# define GLES2_VIDEO_BLUR_MAX_SIGMA 6.0f +# define GLES2_VIDEO_BLURRED_PADDING_SIGMA 3.0f + +# define GLES2_VIDEO_HISTOGRAM_COMPUTE_INTERVAL_US 100000 + +# define GLES2_VIDEO_FLASH_LIGHT_COEF 0.3f +# define GLES2_VIDEO_FLASH_GAMMA_COEF 2.0f + + +static const GLchar *videoVertexShader = + "uniform mat4 transform_matrix;\n" + "attribute vec4 position;\n" + "attribute vec2 texcoord;\n" + "varying vec2 v_texcoord;\n" + "\n" + "void main()\n" + "{\n" + " gl_Position = transform_matrix * position;\n" + " v_texcoord = texcoord;\n" + "}\n"; + +static const GLchar *zebraFragmentShader = + "uniform float zebra_phase;\n" + "uniform float zebra_sat;\n" + "uniform float zebra_weight;\n" + "uniform mat2 zebra_mat;\n" + "\n" + "vec3 apply_zebra(vec3 avg, vec3 rgb)\n" + "{\n" + " vec2 rot_pos = zebra_mat * gl_FragCoord.xy;\n" + " float z = mod(rot_pos.x + 2.0 * zebra_weight * zebra_phase,\n" + " zebra_weight * 2.0) - zebra_weight;\n" + " float zebra = smoothstep(zebra_weight / 2.0 - 0.5,\n" + " zebra_weight / 2.0 + 0.5, abs(z));\n" + " float pixel_val = min(min(avg.r, avg.g), avg.b);\n" + " float zebra_factor = step(zebra_sat, pixel_val);\n" + " rgb = mix(rgb, zebra * rgb, zebra_factor);\n" + " return rgb;\n" + "}\n"; + +static const GLchar *textureNoconvFragmentShader = +# ifdef BCM_VIDEOCORE + "uniform samplerExternalOES s_texture_0;\n" + "uniform samplerExternalOES s_texture_1;\n" + "uniform samplerExternalOES s_texture_2;\n" +# else /* BCM_VIDEOCORE */ + "uniform sampler2D s_texture_0;\n" + "uniform sampler2D s_texture_1;\n" + "uniform sampler2D s_texture_2;\n" +# endif /* BCM_VIDEOCORE */ + "uniform vec2 stride[3];\n" + "uniform vec2 max_coords[3];\n" + "uniform mat3 yuv2rgb_mat;\n" + "uniform vec3 yuv2rgb_offset;\n" + "\n" + "vec3 read_rgb(vec2 coord)\n" + "{\n" + " return texture2D(s_texture_0, min(coord, max_coords[0] - stride[0] / 2.0)).rgb;\n" + "}\n" + "\n" + "vec3 read_rgb_with_offset(vec2 coord, vec2 offset_px)\n" + "{\n" + " return texture2D(s_texture_0, min(coord + offset_px * stride[0], max_coords[0] - stride[0] / 2.0)).rgb;\n" + "}\n"; + +static const GLchar *textureI420FragmentShader = + "uniform sampler2D s_texture_0;\n" + "uniform sampler2D s_texture_1;\n" + "uniform sampler2D s_texture_2;\n" + "uniform vec2 stride[3];\n" + "uniform vec2 max_coords[3];\n" + "uniform mat3 yuv2rgb_mat;\n" + "uniform vec3 yuv2rgb_offset;\n" + "\n" + "vec3 read_rgb(vec2 coord)\n" + "{\n" + " vec3 yuv;\n" + " yuv.r = texture2D(s_texture_0, min(coord, max_coords[0] - stride[0] / 2.0)).r;\n" + " yuv.g = texture2D(s_texture_1, min(coord, max_coords[1] - stride[1] / 2.0)).r;\n" + " yuv.b = texture2D(s_texture_2, min(coord, max_coords[2] - stride[2] / 2.0)).r;\n" + " return yuv2rgb_mat * (yuv.rgb + yuv2rgb_offset);\n" + "}\n" + "\n" + "vec3 read_rgb_with_offset(vec2 coord, vec2 offset_px)\n" + "{\n" + " vec3 yuv;\n" + " yuv.r = texture2D(s_texture_0, min(coord + offset_px * stride[0], max_coords[0] - stride[0] / 2.0)).r;\n" + " yuv.g = texture2D(s_texture_1, min(coord + offset_px * stride[1], max_coords[1] - stride[1] / 2.0)).r;\n" + " yuv.b = texture2D(s_texture_2, min(coord + offset_px * stride[2], max_coords[2] - stride[2] / 2.0)).r;\n" + " return yuv2rgb_mat * (yuv.rgb + yuv2rgb_offset);\n" + "}\n"; + +/* YUV 4:2:0 planar with 16 bits data format in little endian + * and 10 bits depth, padding in higher bits */ +static const GLchar *textureI42010LELowFragmentShader = + "uniform sampler2D s_texture_0;\n" + "uniform sampler2D s_texture_1;\n" + "uniform sampler2D s_texture_2;\n" + "uniform vec2 stride[3];\n" + "uniform vec2 max_coords[3];\n" + "uniform mat3 yuv2rgb_mat;\n" + "uniform vec3 yuv2rgb_offset;\n" + "\n" + "vec3 read_rgb(vec2 coord)\n" + "{\n" + " vec3 yuv;\n" + " vec4 y = texture2D(s_texture_0, min(coord, max_coords[0] - stride[0] / 2.0));\n" + " vec4 u = texture2D(s_texture_1, min(coord, max_coords[1] - stride[1] / 2.0));\n" + " vec4 v = texture2D(s_texture_2, min(coord, max_coords[2] - stride[2] / 2.0));\n" + " yuv.r = y.a * 64. + y.r / 4.;\n" + " yuv.g = u.a * 64. + u.r / 4.;\n" + " yuv.b = v.a * 64. + v.r / 4.;\n" + " return yuv2rgb_mat * (yuv.rgb + yuv2rgb_offset);\n" + "}\n" + "\n" + "vec3 read_rgb_with_offset(vec2 coord, vec2 offset_px)\n" + "{\n" + " vec3 yuv;\n" + " vec4 y = texture2D(s_texture_0, min(coord + offset_px * stride[0], max_coords[0] - stride[0] / 2.0));\n" + " vec4 u = texture2D(s_texture_1, min(coord + offset_px * stride[1], max_coords[1] - stride[1] / 2.0));\n" + " vec4 v = texture2D(s_texture_2, min(coord + offset_px * stride[2], max_coords[2] - stride[2] / 2.0));\n" + " yuv.r = y.a * 64. + y.r / 4.;\n" + " yuv.g = u.a * 64. + u.r / 4.;\n" + " yuv.b = v.a * 64. + v.r / 4.;\n" + " return yuv2rgb_mat * (yuv.rgb + yuv2rgb_offset);\n" + "}\n"; + +static const GLchar *textureNV12FragmentShader = + "uniform sampler2D s_texture_0;\n" + "uniform sampler2D s_texture_1;\n" + "uniform sampler2D s_texture_2;\n" + "uniform vec2 stride[3];\n" + "uniform vec2 max_coords[3];\n" + "uniform mat3 yuv2rgb_mat;\n" + "uniform vec3 yuv2rgb_offset;\n" + "\n" + "vec3 read_rgb(vec2 coord)\n" + "{\n" + " vec3 yuv;\n" + " yuv.r = texture2D(s_texture_0, min(coord, max_coords[0] - stride[0] / 2.0)).r;\n" + " yuv.gb = texture2D(s_texture_1, min(coord, max_coords[1] - stride[1] / 2.0)).ra;\n" + " return yuv2rgb_mat * (yuv.rgb + yuv2rgb_offset);\n" + "}\n" + "\n" + "vec3 read_rgb_with_offset(vec2 coord, vec2 offset_px)\n" + "{\n" + " vec3 yuv;\n" + " yuv.r = texture2D(s_texture_0, min(coord + offset_px * stride[0], max_coords[0] - stride[0] / 2.0)).r;\n" + " yuv.gb = texture2D(s_texture_1, min(coord + offset_px * stride[1], max_coords[1] - stride[1] / 2.0)).ra;\n" + " return yuv2rgb_mat * (yuv.rgb + yuv2rgb_offset);\n" + "}\n"; + +/* YUV 4:2:0 semi-planar with 16 bits data format in little endian + * and 10 bits depth, padding in lower bits */ +static const GLchar *textureNV1210LEHighFragmentShader = + "uniform sampler2D s_texture_0;\n" + "uniform sampler2D s_texture_1;\n" + "uniform sampler2D s_texture_2;\n" + "uniform vec2 stride[3];\n" + "uniform vec2 max_coords[3];\n" + "uniform mat3 yuv2rgb_mat;\n" + "uniform vec3 yuv2rgb_offset;\n" + "\n" + "vec3 read_rgb(vec2 coord)\n" + "{\n" + " vec3 yuv;\n" + " vec4 y = texture2D(s_texture_0, min(coord, max_coords[0] - stride[0] / 2.0));\n" + " vec4 uv = texture2D(s_texture_1, min(coord, max_coords[1] - stride[1] / 2.0));\n" + " yuv.r = y.a + y.r / 256.;\n" + " yuv.g = uv.g + uv.b / 256.;\n" + " yuv.b = uv.a + uv.r / 256.;\n" + " return yuv2rgb_mat * (yuv.rgb + yuv2rgb_offset);\n" + "}\n" + "\n" + "vec3 read_rgb_with_offset(vec2 coord, vec2 offset_px)\n" + "{\n" + " vec3 yuv;\n" + " vec4 y = texture2D(s_texture_0, min(coord + offset_px * stride[0], max_coords[0] - stride[0] / 2.0));\n" + " vec4 uv = texture2D(s_texture_1, min(coord + offset_px * stride[1], max_coords[1] - stride[1] / 2.0));\n" + " yuv.r = y.a + y.r / 256.;\n" + " yuv.g = uv.g + uv.b / 256.;\n" + " yuv.b = uv.a + uv.r / 256.;\n" + " return yuv2rgb_mat * (yuv.rgb + yuv2rgb_offset);\n" + "}\n"; + +static const GLchar *videoFragmentShader = +# ifdef BCM_VIDEOCORE + "#extension GL_OES_EGL_image_external : require\n" +# endif /* BCM_VIDEOCORE */ +# if defined(GL_ES_VERSION_2_0) && \ + (defined(ANDROID) || defined(__APPLE__) || defined(MESON)) + "precision highp float;\n" +# endif + "varying vec2 v_texcoord;\n" + "uniform float sat_coef;\n" + "uniform float light_coef;\n" + "uniform float dark_coef;\n" + "uniform float zebra_enable;\n" + "uniform float zebra_avg_weights[9];\n" + "\n" + "vec3 apply_zebra(vec3 avg, vec3 rgb);\n" + "vec3 read_rgb(vec2 coord);\n" + "vec3 read_rgb_with_offset(vec2 coord, vec2 offset_px);\n" + "\n" + "vec3 read_rgb_avg(vec2 coord)\n" + "{\n" + " vec3 rgb = vec3(0.0);\n" + " for (int y = -1; y <= 1; y++)\n" + " {\n" + " for (int x = -1; x <= 1; x++)\n" + " {\n" + " vec2 offset = vec2(float(x), float(y));\n" + " rgb += read_rgb_with_offset(coord , offset)\n" + " * zebra_avg_weights[(y + 1) * 3 + (x + 1)];\n" + " }\n" + " }\n" + " return rgb;\n" + "}\n" + "\n" + "void main()\n" + "{\n" + " vec3 rgb = read_rgb(v_texcoord);\n" + " \n" + " if (zebra_enable > 0.5)\n" + " {\n" + " vec3 avg = read_rgb_avg(v_texcoord);\n" + " rgb = apply_zebra(avg, rgb);\n" + " }\n" + " float luma = 0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b;\n" + " rgb = mix(vec3(luma), rgb, sat_coef);\n" + " rgb = mix(vec3(1.0), rgb, light_coef);\n" + " rgb = mix(vec3(0.0), rgb, dark_coef);\n" + " gl_FragColor = vec4(rgb, 1.0);\n" + "}\n"; + +const GLchar *Gles2Video::videoFragmentShaders[PROGRAM_MAX][3] = { + { + videoFragmentShader, + textureNoconvFragmentShader, + zebraFragmentShader, + }, + { + videoFragmentShader, + textureI420FragmentShader, + zebraFragmentShader, + }, + { + videoFragmentShader, + textureI42010LELowFragmentShader, + zebraFragmentShader, + }, + { + videoFragmentShader, + textureNV12FragmentShader, + zebraFragmentShader, + }, + { + videoFragmentShader, + textureNV1210LEHighFragmentShader, + zebraFragmentShader, + }, +}; + +static const GLchar *blurHVertexShader = + "attribute vec2 position;\n" + "varying float v_texcoord_x[15];\n" + "varying float v_texcoord_y;\n" + "uniform float pixel_size;\n" + "\n" + "void main()\n" + "{\n" + " gl_Position = vec4(position, 0.0, 1.0);\n" + " vec2 center_tex_coords = position * 0.5 + 0.5;\n" + " for (int i = -7; i <= 7; i++) {\n" + " float offset = pixel_size * float(i);\n" + " v_texcoord_x[i + 7] = center_tex_coords.x + offset;\n" + " }\n" + " v_texcoord_y = center_tex_coords.y;\n" + "}\n"; + +static const GLchar *blurVVertexShader = + "attribute vec2 position;\n" + "varying float v_texcoord_x;\n" + "varying float v_texcoord_y[15];\n" + "uniform float pixel_size;\n" + "\n" + "void main()\n" + "{\n" + " gl_Position = vec4(position, 0.0, 1.0);\n" + " vec2 center_tex_coords = position * 0.5 + 0.5;\n" + " for (int i = -7; i <= 7; i++) {\n" + " float offset = pixel_size * float(i);\n" + " v_texcoord_y[i + 7] = center_tex_coords.y + offset;\n" + " }\n" + " v_texcoord_x = center_tex_coords.x;\n" + "}\n"; + +static const GLchar *blurHFragmentShader = +# if defined(GL_ES_VERSION_2_0) && \ + (defined(ANDROID) || defined(__APPLE__) || defined(MESON)) + "precision mediump float;\n" +# endif + "varying float v_texcoord_x[15];\n" + "varying float v_texcoord_y;\n" + "uniform sampler2D s_texture;\n" + "uniform float blur_weights[15];\n" + "\n" + "void main()\n" + "{\n" + " vec3 rgb = vec3(0.0);\n" + " for (int i = 0; i < 15; i++) {\n" + " vec2 coords = vec2(v_texcoord_x[i], v_texcoord_y);\n" + " rgb += texture2D(s_texture, coords).rgb * blur_weights[i];\n" + " }\n" + " gl_FragColor = vec4(rgb, 1.0);\n" + "}\n"; + +static const GLchar *blurVFragmentShader = +# if defined(GL_ES_VERSION_2_0) && \ + (defined(ANDROID) || defined(__APPLE__) || defined(MESON)) + "precision mediump float;\n" +# endif + "varying float v_texcoord_x;\n" + "varying float v_texcoord_y[15];\n" + "uniform sampler2D s_texture;\n" + "uniform float blur_weights[15];\n" + "\n" + "void main()\n" + "{\n" + " vec3 rgb = vec3(0.0);\n" + " for (int i = 0; i < 15; i++) {\n" + " vec2 coords = vec2(v_texcoord_x, v_texcoord_y[i]);\n" + " rgb += texture2D(s_texture, coords).rgb * blur_weights[i];\n" + " }\n" + " gl_FragColor = vec4(rgb, 1.0);\n" + "}\n"; + +static const GLchar *histogramVertexShader = + "attribute vec4 position;\n" + "attribute vec2 texcoord;\n" + "varying vec2 v_texcoord;\n" + "\n" + "void main()\n" + "{\n" + " gl_Position = position;\n" + " v_texcoord = texcoord;\n" + "}\n"; + +static const GLchar *histogramFragmentShader = +# ifdef BCM_VIDEOCORE + "#extension GL_OES_EGL_image_external : require\n" +# endif /* BCM_VIDEOCORE */ +# if defined(GL_ES_VERSION_2_0) && \ + (defined(ANDROID) || defined(__APPLE__) || defined(MESON)) + "precision mediump float;\n" +# endif + "varying vec2 v_texcoord;\n" + "\n" + "vec3 read_rgb(vec2 coord);\n" + "\n" + "void main()\n" + "{\n" + " vec3 rgb = read_rgb(v_texcoord);\n" + " float luma = 0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b;\n" + " gl_FragColor = vec4(rgb, luma);\n" + "}\n"; + +const GLchar *Gles2Video::histogramFragmentShaders[PROGRAM_MAX][2] = { + { + histogramFragmentShader, + textureNoconvFragmentShader, + }, + { + histogramFragmentShader, + textureI420FragmentShader, + }, + { + histogramFragmentShader, + textureNV12FragmentShader, + }, + { + histogramFragmentShader, + textureI42010LELowFragmentShader, + }, + { + histogramFragmentShader, + textureNV1210LEHighFragmentShader, + }, +}; + +static const GLfloat zebraAvgWeights[9] = {0.077847f, + 0.123317f, + 0.077847f, + 0.123317f, + 0.195346f, + 0.123317f, + 0.077847f, + 0.123317f, + 0.077847f}; + + +Gles2Video::Gles2Video(Session *session, + GLuint defaultFbo, + unsigned int firstTexUnit) +{ + int ret; + GLint vertexShader = 0, fragmentShader[PROGRAM_MAX] = {}; + GLint success = 0; + unsigned int i; + + mSession = session; + mVideoWidth = 0; + mVideoHeight = 0; + mFirstTexUnit = firstTexUnit; + mDefaultFbo = defaultFbo; + mTransition = GLES2_VIDEO_TRANSITION_NONE; + mTransitionStartTime = 0; + mTransitionDuration = 0; + mTransitionHold = false; + memset(mProgram, 0, sizeof(mProgram)); + memset(mProgramTransformMatrix, 0, sizeof(mProgramTransformMatrix)); + memset(mProgramYuv2RgbMatrix, 0, sizeof(mProgramYuv2RgbMatrix)); + memset(mProgramYuv2RgbOffset, 0, sizeof(mProgramYuv2RgbOffset)); + memset(mProgramStride, 0, sizeof(mProgramStride)); + memset(mProgramMaxCoords, 0, sizeof(mProgramMaxCoords)); + memset(mProgramSatCoef, 0, sizeof(mProgramSatCoef)); + memset(mProgramLightCoef, 0, sizeof(mProgramLightCoef)); + memset(mProgramDarkCoef, 0, sizeof(mProgramDarkCoef)); + memset(mProgramZebraEnable, 0, sizeof(mProgramZebraEnable)); + memset(mProgramZebraThreshold, 0, sizeof(mProgramZebraThreshold)); + memset(mProgramZebraPhase, 0, sizeof(mProgramZebraPhase)); + memset(mProgramZebraWeight, 0, sizeof(mProgramZebraWeight)); + memset(mTextures, 0, sizeof(mTextures)); + mExtTexture = 0; + memset(mUniformSamplers, 0, sizeof(mUniformSamplers)); + memset(mPositionHandle, 0, sizeof(mPositionHandle)); + memset(mTexcoordHandle, 0, sizeof(mTexcoordHandle)); + mBlurInit = false; + mApplyBlur = false; + memset(mBlurWeights, 0, sizeof(mBlurWeights)); + mBlurFboWidth = 0; + mBlurFboHeight = 0; + memset(mBlurFbo, 0, sizeof(mBlurFbo)); + memset(mBlurFboTexture, 0, sizeof(mBlurFboTexture)); + memset(mBlurProgram, 0, sizeof(mBlurProgram)); + memset(mBlurUniformPixelSize, 0, sizeof(mBlurUniformPixelSize)); + memset(mBlurUniformWeights, 0, sizeof(mBlurUniformWeights)); + memset(mBlurUniformSampler, 0, sizeof(mBlurUniformSampler)); + memset(mBlurPositionHandle, 0, sizeof(mBlurPositionHandle)); + mPaddingPass1Width = 0; + mPaddingPass1Height = 0; + mPaddingPass2Width = 0; + mPaddingPass2Height = 0; + memset(mPaddingBlurWeights, 0, sizeof(mPaddingBlurWeights)); + memset(mPaddingFbo, 0, sizeof(mPaddingFbo)); + memset(mPaddingFboTexture, 0, sizeof(mPaddingFboTexture)); + mHistogramInit = false; + mHistogramLastComputeTime = 0; + memset(mHistogramProgram, 0, sizeof(mHistogramProgram)); + memset(mHistogramYuv2RgbMatrix, 0, sizeof(mHistogramYuv2RgbMatrix)); + memset(mHistogramYuv2RgbOffset, 0, sizeof(mHistogramYuv2RgbOffset)); + memset(mHistogramUniformSampler, 0, sizeof(mHistogramUniformSampler)); + memset(mHistogramPositionHandle, 0, sizeof(mHistogramPositionHandle)); + memset(mHistogramTexcoordHandle, 0, sizeof(mHistogramTexcoordHandle)); + mHistogramFbo = 0; + mHistogramFboTexture = 0; + mHistogramBuffer = nullptr; + for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) + mHistogramValid[i] = false; + memset(mHistogram, 0, sizeof(mHistogram)); + memset(mHistogramNorm, 0, sizeof(mHistogram)); + mSatCoef = 1.0f; + mBaseSatCoef = 1.0f; + mLightCoef = 1.0f; + mBaseLightCoef = 1.0f; + mDarkCoef = 1.0f; + mBaseDarkCoef = 1.0f; +# ifdef BCM_VIDEOCORE + mEglImage = EGL_NO_IMAGE_KHR; +# endif /* BCM_VIDEOCORE */ + + GLCHK(); + + /* Vertex shader */ + vertexShader = glCreateShader(GL_VERTEX_SHADER); + if ((vertexShader == 0) || (vertexShader == GL_INVALID_ENUM)) { + ULOGE("failed to create vertex shader"); + goto err; + } + + glShaderSource(vertexShader, 1, &videoVertexShader, nullptr); + glCompileShader(vertexShader); + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar infoLog[512]; + glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog); + ULOGE("vertex shader compilation failed '%s'", infoLog); + goto err; + } + + for (i = 0; i < PROGRAM_MAX; i++) { + /* Fragment shader */ + fragmentShader[i] = glCreateShader(GL_FRAGMENT_SHADER); + if ((fragmentShader[i] == 0) || + (fragmentShader[i] == GL_INVALID_ENUM)) { + ULOGE("failed to create fragment shader"); + goto err; + } + + glShaderSource( + fragmentShader[i], 3, videoFragmentShaders[i], nullptr); + glCompileShader(fragmentShader[i]); + glGetShaderiv(fragmentShader[i], GL_COMPILE_STATUS, &success); + if (!success) { + GLchar infoLog[512]; + glGetShaderInfoLog( + fragmentShader[i], 512, nullptr, infoLog); + ULOGE("fragment shader compilation failed '%s'", + infoLog); + goto err; + } + + /* Link shaders */ + mProgram[i] = glCreateProgram(); + glAttachShader(mProgram[i], vertexShader); + glAttachShader(mProgram[i], fragmentShader[i]); + glLinkProgram(mProgram[i]); + glGetProgramiv(mProgram[i], GL_LINK_STATUS, &success); + if (!success) { + GLchar infoLog[512]; + glGetProgramInfoLog(mProgram[i], 512, nullptr, infoLog); + ULOGE("program link failed '%s'", infoLog); + goto err; + } + + glDeleteShader(fragmentShader[i]); + fragmentShader[i] = 0; + } + + glDeleteShader(vertexShader); + vertexShader = 0; + + GLCHK(); + + for (i = 0; i < PROGRAM_MAX; i++) { + mProgramTransformMatrix[i] = + glGetUniformLocation(mProgram[i], "transform_matrix"); + mProgramYuv2RgbMatrix[i] = + glGetUniformLocation(mProgram[i], "yuv2rgb_mat"); + mProgramYuv2RgbOffset[i] = + glGetUniformLocation(mProgram[i], "yuv2rgb_offset"); + mProgramStride[i] = glGetUniformLocation(mProgram[i], "stride"); + mProgramMaxCoords[i] = + glGetUniformLocation(mProgram[i], "max_coords"); + mProgramSatCoef[i] = + glGetUniformLocation(mProgram[i], "sat_coef"); + mProgramLightCoef[i] = + glGetUniformLocation(mProgram[i], "light_coef"); + mProgramDarkCoef[i] = + glGetUniformLocation(mProgram[i], "dark_coef"); + mProgramZebraEnable[i] = + glGetUniformLocation(mProgram[i], "zebra_enable"); + mProgramZebraThreshold[i] = + glGetUniformLocation(mProgram[i], "zebra_sat"); + mProgramZebraPhase[i] = + glGetUniformLocation(mProgram[i], "zebra_phase"); + mProgramZebraWeight[i] = + glGetUniformLocation(mProgram[i], "zebra_weight"); + mUniformSamplers[i][0] = + glGetUniformLocation(mProgram[i], "s_texture_0"); + mUniformSamplers[i][1] = + glGetUniformLocation(mProgram[i], "s_texture_1"); + mUniformSamplers[i][2] = + glGetUniformLocation(mProgram[i], "s_texture_2"); + mPositionHandle[i] = + glGetAttribLocation(mProgram[i], "position"); + mTexcoordHandle[i] = + glGetAttribLocation(mProgram[i], "texcoord"); + } + + GLCHK(); + + ret = setupBlur(); + if (ret < 0) + ULOG_ERRNO("setupBlur", -ret); + + GLCHK(); + + ret = setupHistograms(); + if (ret < 0) + ULOG_ERRNO("setupHistograms", -ret); + + GLCHK(); + + /* Setup zebra shaders */ + setupZebra(PROGRAM_NOCONV); + setupZebra(PROGRAM_YUV_TO_RGB_PLANAR); + setupZebra(PROGRAM_YUV_TO_RGB_SEMIPLANAR); + setupZebra(PROGRAM_YUV_TO_RGB_PLANAR_10_16LE); + setupZebra(PROGRAM_YUV_TO_RGB_SEMIPLANAR_10_16LE_HIGH); + + GLCHK(glGenTextures(GLES2_VIDEO_TEX_UNIT_COUNT, mTextures)); + + for (i = 0; i < GLES2_VIDEO_TEX_UNIT_COUNT; i++) { + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + i)); +# ifdef BCM_VIDEOCORE + if (i == 0) { + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, + mTextures[i])); + } else { + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[i])); + } +# else /* BCM_VIDEOCORE */ + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[i])); +# endif /* BCM_VIDEOCORE */ + + GLCHK(glTexParameteri( + GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GLCHK(glTexParameteri( + GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GLCHK(glTexParameterf( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GLCHK(glTexParameterf( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + } + + GLCHK(glBindTexture(GL_TEXTURE_2D, 0)); + + return; + +err: + if (mTextures[0]) + GLCHK(glDeleteTextures(GLES2_VIDEO_TEX_UNIT_COUNT, mTextures)); + if (vertexShader) + GLCHK(glDeleteShader(vertexShader)); + for (i = 0; i < PROGRAM_MAX; i++) { + if (fragmentShader[i]) + GLCHK(glDeleteShader(fragmentShader[i])); + if (mProgram[i] > 0) + GLCHK(glDeleteProgram(mProgram[i])); + } + memset(mProgram, 0, sizeof(mProgram)); + memset(mTextures, 0, sizeof(mTextures)); + cleanupBlur(); + cleanupHistograms(); +} + + +Gles2Video::~Gles2Video(void) +{ + if (mTextures[0]) + GLCHK(glDeleteTextures(GLES2_VIDEO_TEX_UNIT_COUNT, mTextures)); + for (unsigned int i = 0; i < PROGRAM_MAX; i++) { + if (mProgram[i] > 0) + GLCHK(glDeleteProgram(mProgram[i])); + } + + cleanupBlur(); + cleanupPaddingFbo(); + cleanupHistograms(); +} + + +enum Gles2Video::program +Gles2Video::getProgram(const struct vdef_raw_format *format) +{ + if (vdef_raw_format_cmp(format, &vdef_i420)) { + return PROGRAM_YUV_TO_RGB_PLANAR; + } else if (vdef_raw_format_cmp(format, &vdef_nv12)) { + return PROGRAM_YUV_TO_RGB_SEMIPLANAR; + } else if (vdef_raw_format_cmp(format, &vdef_i420_10_16le)) { + return PROGRAM_YUV_TO_RGB_PLANAR_10_16LE; + } else if (vdef_raw_format_cmp(format, &vdef_nv12_10_16le_high)) { + return PROGRAM_YUV_TO_RGB_SEMIPLANAR_10_16LE_HIGH; + } else if (vdef_raw_format_cmp(format, &vdef_rgb)) { + return PROGRAM_NOCONV; + } else if (vdef_raw_format_cmp(format, &vdef_mmal_opaque)) { + return PROGRAM_NOCONV; + } else { + ULOGE("unsupported frame format"); + return PROGRAM_NOCONV; + } +} + + +int Gles2Video::setupBlur(void) +{ + int ret = 0; + GLint vertexShaderH = 0, vertexShaderV = 0; + GLint fragmentShaderH = 0, fragmentShaderV = 0; + GLint success = 0; + + /* Free previous resources */ + cleanupBlur(); + + /* Render sizes */ + mBlurFboWidth = GLES2_VIDEO_BLUR_FBO_TARGET_SIZE; + mBlurFboHeight = GLES2_VIDEO_BLUR_FBO_TARGET_SIZE; + + /* Shaders compilation */ + vertexShaderH = glCreateShader(GL_VERTEX_SHADER); + if ((vertexShaderH == 0) || (vertexShaderH == GL_INVALID_ENUM)) { + ULOGE("failed to create vertex shader"); + ret = -ENOMEM; + goto error; + } + + glShaderSource(vertexShaderH, 1, &blurHVertexShader, nullptr); + glCompileShader(vertexShaderH); + glGetShaderiv(vertexShaderH, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar infoLog[512]; + glGetShaderInfoLog(vertexShaderH, 512, nullptr, infoLog); + ULOGE("vertex shader (H) compilation failed '%s'", infoLog); + ret = -EPROTO; + goto error; + } + + vertexShaderV = glCreateShader(GL_VERTEX_SHADER); + if ((vertexShaderV == 0) || (vertexShaderV == GL_INVALID_ENUM)) { + ULOGE("failed to create vertex shader"); + ret = -ENOMEM; + goto error; + } + + glShaderSource(vertexShaderV, 1, &blurVVertexShader, nullptr); + glCompileShader(vertexShaderV); + glGetShaderiv(vertexShaderV, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar infoLog[512]; + glGetShaderInfoLog(vertexShaderV, 512, nullptr, infoLog); + ULOGE("vertex shader (V) compilation failed '%s'", infoLog); + ret = -EPROTO; + goto error; + } + + fragmentShaderH = glCreateShader(GL_FRAGMENT_SHADER); + if ((fragmentShaderH == 0) || (fragmentShaderH == GL_INVALID_ENUM)) { + ULOGE("failed to create fragment shader"); + ret = -ENOMEM; + goto error; + } + + glShaderSource(fragmentShaderH, 1, &blurHFragmentShader, nullptr); + glCompileShader(fragmentShaderH); + glGetShaderiv(fragmentShaderH, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar infoLog[512]; + glGetShaderInfoLog(fragmentShaderH, 512, nullptr, infoLog); + ULOGE("fragment shader compilation failed '%s'", infoLog); + ret = -EPROTO; + goto error; + } + + fragmentShaderV = glCreateShader(GL_FRAGMENT_SHADER); + if ((fragmentShaderV == 0) || (fragmentShaderV == GL_INVALID_ENUM)) { + ULOGE("failed to create fragment shader"); + ret = -ENOMEM; + goto error; + } + + glShaderSource(fragmentShaderV, 1, &blurVFragmentShader, nullptr); + glCompileShader(fragmentShaderV); + glGetShaderiv(fragmentShaderV, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar infoLog[512]; + glGetShaderInfoLog(fragmentShaderV, 512, nullptr, infoLog); + ULOGE("fragment shader compilation failed '%s'", infoLog); + ret = -EPROTO; + goto error; + } + + /* Shaders link */ + mBlurProgram[0] = glCreateProgram(); + glAttachShader(mBlurProgram[0], vertexShaderH); + glAttachShader(mBlurProgram[0], fragmentShaderH); + glLinkProgram(mBlurProgram[0]); + glGetProgramiv(mBlurProgram[0], GL_LINK_STATUS, &success); + if (!success) { + GLchar infoLog[512]; + glGetProgramInfoLog(mBlurProgram[0], 512, nullptr, infoLog); + ULOGE("program link failed '%s'", infoLog); + ret = -EPROTO; + goto error; + } + + mBlurProgram[1] = glCreateProgram(); + glAttachShader(mBlurProgram[1], vertexShaderV); + glAttachShader(mBlurProgram[1], fragmentShaderV); + glLinkProgram(mBlurProgram[1]); + glGetProgramiv(mBlurProgram[1], GL_LINK_STATUS, &success); + if (!success) { + GLchar infoLog[512]; + glGetProgramInfoLog(mBlurProgram[1], 512, nullptr, infoLog); + ULOGE("program link failed '%s'", infoLog); + ret = -EPROTO; + goto error; + } + + glDeleteShader(vertexShaderH); + vertexShaderH = 0; + glDeleteShader(vertexShaderV); + vertexShaderV = 0; + glDeleteShader(fragmentShaderH); + fragmentShaderH = 0; + glDeleteShader(fragmentShaderV); + fragmentShaderV = 0; + + /* Attributes and uniforms handles */ + mBlurUniformPixelSize[0] = + glGetUniformLocation(mBlurProgram[0], "pixel_size"); + mBlurUniformWeights[0] = + glGetUniformLocation(mBlurProgram[0], "blur_weights"); + mBlurUniformSampler[0] = + glGetUniformLocation(mBlurProgram[0], "s_texture"); + mBlurPositionHandle[0] = + glGetAttribLocation(mBlurProgram[0], "position"); + mBlurUniformPixelSize[1] = + glGetUniformLocation(mBlurProgram[1], "pixel_size"); + mBlurUniformWeights[1] = + glGetUniformLocation(mBlurProgram[1], "blur_weights"); + mBlurUniformSampler[1] = + glGetUniformLocation(mBlurProgram[1], "s_texture"); + mBlurPositionHandle[1] = + glGetAttribLocation(mBlurProgram[1], "position"); + + mBlurInit = true; + return 0; + +error: + cleanupBlur(); + return ret; +} + + +void Gles2Video::cleanupBlur(void) +{ + cleanupBlurFbo(); + if (mBlurProgram[0] > 0) { + GLCHK(glDeleteProgram(mBlurProgram[0])); + mBlurProgram[0] = 0; + } + if (mBlurProgram[1] > 0) { + GLCHK(glDeleteProgram(mBlurProgram[1])); + mBlurProgram[1] = 0; + } + mBlurInit = false; +} + + +int Gles2Video::setupBlurFbo(void) +{ + int ret = 0; + unsigned int i; + GLenum gle; + + /* Free previous resources */ + cleanupBlurFbo(); + + if (!mBlurInit) + return 0; + + /* Render sizes */ + if (mVideoWidth > mVideoHeight) { + mBlurFboWidth = GLES2_VIDEO_BLUR_FBO_TARGET_SIZE; + mBlurFboHeight = (GLES2_VIDEO_BLUR_FBO_TARGET_SIZE * + mVideoHeight / mVideoWidth + + 3) & + ~3; + } else { + mBlurFboWidth = (GLES2_VIDEO_BLUR_FBO_TARGET_SIZE * + mVideoWidth / mVideoHeight + + 3) & + ~3; + mBlurFboHeight = GLES2_VIDEO_BLUR_FBO_TARGET_SIZE; + } + + /* Allocate FBOs and textures */ + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + + GLES2_VIDEO_TEX_UNIT_COUNT)); + for (i = 0; i < 2; i++) { + GLCHK(glGenFramebuffers(1, &mBlurFbo[i])); + if (mBlurFbo[i] <= 0) { + ULOGE("failed to create framebuffer"); + ret = -ENOMEM; + goto error; + } + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mBlurFbo[i])); + + GLCHK(glGenTextures(1, &mBlurFboTexture[i])); + if (mBlurFboTexture[i] <= 0) { + ULOGE("failed to create texture"); + ret = -ENOMEM; + goto error; + } + GLCHK(glBindTexture(GL_TEXTURE_2D, mBlurFboTexture[i])); + GLCHK(glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGB, + mBlurFboWidth, + mBlurFboHeight, + 0, + GL_RGB, + GL_UNSIGNED_BYTE, + nullptr)); + + GLCHK(glTexParameteri( + GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GLCHK(glTexParameteri( + GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GLCHK(glTexParameterf( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GLCHK(glTexParameterf( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + + GLCHK(glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + mBlurFboTexture[i], + 0)); + + gle = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (gle != GL_FRAMEBUFFER_COMPLETE) { + ULOGE("invalid framebuffer status"); + ret = -EPROTO; + goto error; + } + } + + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); + return 0; + +error: + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); + cleanupBlur(); + return ret; +} + + +void Gles2Video::cleanupBlurFbo(void) +{ + if (mBlurFboTexture[0] > 0) { + GLCHK(glDeleteTextures(2, mBlurFboTexture)); + memset(mBlurFboTexture, 0, sizeof(mBlurFboTexture)); + } + if (mBlurFbo[0] > 0) { + GLCHK(glDeleteFramebuffers(2, mBlurFbo)); + memset(mBlurFbo, 0, sizeof(mBlurFbo)); + } + mBlurFboWidth = 0; + mBlurFboHeight = 0; +} + + +void Gles2Video::renderBlur(size_t framePlaneStride[3], + const struct vdef_raw_format *format, + const struct vdef_frame_info *info, + const struct vdef_rect *crop, + const struct pdraw_rect *renderPos, + float videoW, + float videoH, + Eigen::Matrix4f &viewProjMat) +{ + unsigned int i; + float stride[GLES2_VIDEO_TEX_UNIT_COUNT * 2] = {0}; + float maxCoords[GLES2_VIDEO_TEX_UNIT_COUNT * 2] = {0}; + float vertices[12]; + float texCoords[8]; + bool mirrorTexture = false; + + if (!mBlurInit) + return; + + enum program prog; + prog = getProgram(format); + + /* Pass 1 downscale */ + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mBlurFbo[0])); + GLCHK(glViewport(0, 0, mBlurFboWidth, mBlurFboHeight)); + + GLCHK(glUseProgram(mProgram[prog])); + + switch (prog) { + default: + case PROGRAM_NOCONV: + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit)); +# ifdef BCM_VIDEOCORE + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextures[0])); +# else /* BCM_VIDEOCORE */ + GLCHK(glBindTexture(GL_TEXTURE_2D, + (mExtTexture > 0) ? mExtTexture + : mTextures[0])); +# endif /* BCM_VIDEOCORE */ + GLCHK(glUniform1i(mUniformSamplers[prog][0], mFirstTexUnit)); + stride[0] = 1.f / framePlaneStride[0]; + stride[1] = 1.f / info->resolution.height; + maxCoords[0] = + (float)(crop->left + crop->width) / framePlaneStride[0]; + maxCoords[1] = (float)(crop->top + crop->height) / + info->resolution.height; + break; + case PROGRAM_YUV_TO_RGB_PLANAR: + case PROGRAM_YUV_TO_RGB_PLANAR_10_16LE: + mirrorTexture = true; + for (i = 0; i < GLES2_VIDEO_TEX_UNIT_COUNT; i++) { + int height = + info->resolution.height / ((i > 0) ? 2 : 1); + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + i)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[i])); + GLCHK(glUniform1i(mUniformSamplers[prog][i], + mFirstTexUnit + i)); + stride[2 * i] = 1.f / framePlaneStride[i]; + stride[2 * i + 1] = 1.f / height; + maxCoords[2 * i] = + (float)(crop->left + crop->width) / + (framePlaneStride[i] * ((i > 0) ? 2 : 1)); + maxCoords[2 * i + 1] = + (float)(crop->top + crop->height) / + info->resolution.height; + } + break; + case PROGRAM_YUV_TO_RGB_SEMIPLANAR: + case PROGRAM_YUV_TO_RGB_SEMIPLANAR_10_16LE_HIGH: + mirrorTexture = true; + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 0)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[0])); + GLCHK(glUniform1i(mUniformSamplers[prog][0], + mFirstTexUnit + 0)); + stride[0] = 1.f / framePlaneStride[0]; + stride[1] = 1.f / info->resolution.height; + maxCoords[0] = + (float)(crop->left + crop->width) / framePlaneStride[0]; + maxCoords[1] = (float)(crop->top + crop->height) / + info->resolution.height; + + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 1)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[1])); + GLCHK(glUniform1i(mUniformSamplers[prog][1], + mFirstTexUnit + 1)); + stride[2] = 1.f / (framePlaneStride[1] / 2); + stride[3] = 1.f / (info->resolution.height / 2); + maxCoords[2] = + (float)(crop->left + crop->width) / framePlaneStride[1]; + maxCoords[3] = (float)(crop->top + crop->height) / + info->resolution.height; + break; + } + + GLCHK(glUniform2fv( + mProgramStride[prog], GLES2_VIDEO_TEX_UNIT_COUNT, stride)); + GLCHK(glUniform2fv(mProgramMaxCoords[prog], + GLES2_VIDEO_TEX_UNIT_COUNT, + maxCoords)); + GLCHK(glUniform3f(mProgramYuv2RgbOffset[prog], + vdef_yuv_to_rgb_norm_offset[info->matrix_coefs] + [info->full_range][0], + vdef_yuv_to_rgb_norm_offset[info->matrix_coefs] + [info->full_range][1], + vdef_yuv_to_rgb_norm_offset[info->matrix_coefs] + [info->full_range][2])); + GLCHK(glUniformMatrix3fv( + mProgramYuv2RgbMatrix[prog], + 1, + GL_FALSE, + &vdef_yuv_to_rgb_norm_matrix[info->matrix_coefs] + [info->full_range][0])); + + /* Disable overexposure zebras */ + updateZebra(nullptr, prog, false, 0.f); + + vertices[0] = -1.; + vertices[1] = -1.; + vertices[2] = 1.; + vertices[3] = 1.; + vertices[4] = -1.; + vertices[5] = 1.; + vertices[6] = -1.; + vertices[7] = 1.; + vertices[8] = 1.; + vertices[9] = 1.; + vertices[10] = 1.; + vertices[11] = 1.; + + Eigen::Matrix4f id = Eigen::Matrix4f::Identity(); + GLCHK(glUniformMatrix4fv( + mProgramTransformMatrix[prog], 1, false, id.data())); + GLCHK(glUniform1f(mProgramSatCoef[prog], 1.f)); + GLCHK(glUniform1f(mProgramLightCoef[prog], 1.f)); + GLCHK(glUniform1f(mProgramDarkCoef[prog], 1.f)); + + GLCHK(glVertexAttribPointer( + mPositionHandle[prog], 3, GL_FLOAT, false, 0, vertices)); + GLCHK(glEnableVertexAttribArray(mPositionHandle[prog])); + + if (mirrorTexture) { + texCoords[0] = (float)crop->left / (float)framePlaneStride[0]; + texCoords[1] = (float)(crop->top + crop->height) / + (float)info->resolution.height; + texCoords[2] = (float)(crop->left + crop->width) / + (float)framePlaneStride[0]; + texCoords[3] = (float)(crop->top + crop->height) / + (float)info->resolution.height; + texCoords[4] = (float)crop->left / (float)framePlaneStride[0]; + texCoords[5] = + (float)crop->top / (float)info->resolution.height; + texCoords[6] = (float)(crop->left + crop->width) / + (float)framePlaneStride[0]; + texCoords[7] = + (float)crop->top / (float)info->resolution.height; + } else { + texCoords[0] = (float)crop->left / (float)framePlaneStride[0]; + texCoords[1] = + (float)crop->top / (float)info->resolution.height; + texCoords[2] = (float)(crop->left + crop->width) / + (float)framePlaneStride[0]; + texCoords[3] = + (float)crop->top / (float)info->resolution.height; + texCoords[4] = (float)crop->left / (float)framePlaneStride[0]; + texCoords[5] = (float)(crop->top + crop->height) / + (float)info->resolution.height; + texCoords[6] = (float)(crop->left + crop->width) / + (float)framePlaneStride[0]; + texCoords[7] = (float)(crop->top + crop->height) / + (float)info->resolution.height; + } + + GLCHK(glVertexAttribPointer( + mTexcoordHandle[prog], 2, GL_FLOAT, false, 0, texCoords)); + GLCHK(glEnableVertexAttribArray(mTexcoordHandle[prog])); + + GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + + GLCHK(glDisableVertexAttribArray(mPositionHandle[prog])); + GLCHK(glDisableVertexAttribArray(mTexcoordHandle[prog])); + + /* Horizontal blur pass */ + vertices[0] = -1.; + vertices[1] = -1.; + vertices[2] = 1.; + vertices[3] = -1.; + vertices[4] = -1.; + vertices[5] = 1.; + vertices[6] = 1.; + vertices[7] = 1.; + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mBlurFbo[1])); + GLCHK(glViewport(0, 0, mBlurFboWidth, mBlurFboHeight)); + GLCHK(glUseProgram(mBlurProgram[0])); + GLCHK(glUniform1fv(mBlurUniformWeights[0], + GLES2_VIDEO_BLUR_TAP_COUNT, + mBlurWeights)); + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + + GLES2_VIDEO_TEX_UNIT_COUNT)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mBlurFboTexture[0])); + GLCHK(glUniform1i(mBlurUniformSampler[0], + mFirstTexUnit + GLES2_VIDEO_TEX_UNIT_COUNT)); + GLCHK(glVertexAttribPointer( + mBlurPositionHandle[0], 2, GL_FLOAT, false, 0, vertices)); + GLCHK(glEnableVertexAttribArray(mBlurPositionHandle[0])); + GLCHK(glUniform1f(mBlurUniformPixelSize[0], 1.0 / mBlurFboWidth)); + GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + GLCHK(glDisableVertexAttribArray(mBlurPositionHandle[0])); + + /* Vertical blur pass */ + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mBlurFbo[0])); + GLCHK(glViewport(0, 0, mBlurFboWidth, mBlurFboHeight)); + GLCHK(glUseProgram(mBlurProgram[1])); + GLCHK(glUniform1fv(mBlurUniformWeights[1], + GLES2_VIDEO_BLUR_TAP_COUNT, + mBlurWeights)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mBlurFboTexture[1])); + GLCHK(glUniform1i(mBlurUniformSampler[1], + mFirstTexUnit + GLES2_VIDEO_TEX_UNIT_COUNT)); + GLCHK(glVertexAttribPointer( + mBlurPositionHandle[1], 2, GL_FLOAT, false, 0, vertices)); + GLCHK(glEnableVertexAttribArray(mBlurPositionHandle[1])); + GLCHK(glUniform1f(mBlurUniformPixelSize[1], 1.0 / mBlurFboHeight)); + GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + GLCHK(glDisableVertexAttribArray(mBlurPositionHandle[1])); + + /* Render to screen */ + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); + GLCHK(glViewport(renderPos->x, + renderPos->y, + renderPos->width, + renderPos->height)); + GLCHK(glUseProgram(mProgram[PROGRAM_NOCONV])); + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + + GLES2_VIDEO_TEX_UNIT_COUNT)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mBlurFboTexture[0])); + GLCHK(glUniform1i(mUniformSamplers[PROGRAM_NOCONV][0], + mFirstTexUnit + GLES2_VIDEO_TEX_UNIT_COUNT)); + GLCHK(glUniformMatrix4fv(mProgramTransformMatrix[PROGRAM_NOCONV], + 1, + false, + viewProjMat.data())); + GLCHK(glUniform1f(mProgramSatCoef[PROGRAM_NOCONV], mSatCoef)); + GLCHK(glUniform1f(mProgramLightCoef[PROGRAM_NOCONV], mLightCoef)); + GLCHK(glUniform1f(mProgramDarkCoef[PROGRAM_NOCONV], mDarkCoef)); + + maxCoords[0] = 1.; + maxCoords[1] = 1.; + stride[0] = 0.; + stride[1] = 0.; + GLCHK(glUniform2fv(mProgramStride[PROGRAM_NOCONV], + GLES2_VIDEO_TEX_UNIT_COUNT, + stride)); + GLCHK(glUniform2fv(mProgramMaxCoords[PROGRAM_NOCONV], + GLES2_VIDEO_TEX_UNIT_COUNT, + maxCoords)); + + vertices[0] = -videoW; + vertices[1] = -videoH; + vertices[2] = 1.; + vertices[3] = videoW; + vertices[4] = -videoH; + vertices[5] = 1.; + vertices[6] = -videoW; + vertices[7] = videoH; + vertices[8] = 1.; + vertices[9] = videoW; + vertices[10] = videoH; + vertices[11] = 1.; + + texCoords[0] = 0.; + texCoords[1] = 0.; + texCoords[2] = 1.; + texCoords[3] = 0.; + texCoords[4] = 0.; + texCoords[5] = 1.; + texCoords[6] = 1.; + texCoords[7] = 1.; + + GLCHK(glVertexAttribPointer(mPositionHandle[PROGRAM_NOCONV], + 3, + GL_FLOAT, + false, + 0, + vertices)); + GLCHK(glEnableVertexAttribArray(mPositionHandle[PROGRAM_NOCONV])); + + GLCHK(glVertexAttribPointer(mTexcoordHandle[PROGRAM_NOCONV], + 2, + GL_FLOAT, + false, + 0, + texCoords)); + GLCHK(glEnableVertexAttribArray(mTexcoordHandle[PROGRAM_NOCONV])); + + GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + + GLCHK(glDisableVertexAttribArray(mPositionHandle[PROGRAM_NOCONV])); + GLCHK(glDisableVertexAttribArray(mTexcoordHandle[PROGRAM_NOCONV])); + + GLCHK(glUseProgram(mProgram[prog])); +} + + +int Gles2Video::setupPaddingFbo(enum pdraw_video_renderer_fill_mode fillMode) +{ + GLenum gle; + unsigned int i; + + /* Free previous resources */ + cleanupPaddingFbo(); + + if (!mBlurInit) + return 0; + + if ((fillMode != PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_CROP) && + (fillMode != PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_EXTEND)) + return 0; + + /* Render sizes */ + if (mVideoWidth > mVideoHeight) { + mPaddingPass1Width = GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_1; + mPaddingPass1Height = (GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_1 * + mVideoHeight / mVideoWidth + + 3) & + ~3; + mPaddingPass2Width = GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_2; + mPaddingPass2Height = (GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_2 * + mVideoHeight / mVideoWidth + + 3) & + ~3; + } else { + mPaddingPass1Width = (GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_1 * + mVideoWidth / mVideoHeight + + 3) & + ~3; + mPaddingPass1Height = GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_1; + mPaddingPass2Width = (GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_2 * + mVideoWidth / mVideoHeight + + 3) & + ~3; + mPaddingPass2Height = GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_2; + } + + pdraw_gaussianDistribution(mPaddingBlurWeights, + GLES2_VIDEO_BLUR_TAP_COUNT, + GLES2_VIDEO_BLURRED_PADDING_SIGMA); + + /* Allocate new resources */ + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + + GLES2_VIDEO_TEX_UNIT_COUNT)); + for (i = 0; i < 4; i++) { + GLCHK(glGenFramebuffers(1, &mPaddingFbo[i])); + if (mPaddingFbo[i] <= 0) { + ULOGE("failed to create framebuffer"); + goto err; + } + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mPaddingFbo[i])); + + GLCHK(glGenTextures(1, &mPaddingFboTexture[i])); + if (mPaddingFboTexture[i] <= 0) { + ULOGE("failed to create texture"); + goto err; + } + GLCHK(glBindTexture(GL_TEXTURE_2D, mPaddingFboTexture[i])); + GLCHK(glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGB, + (i < 2) ? mPaddingPass1Width : mPaddingPass2Width, + (i < 2) ? mPaddingPass1Height : mPaddingPass2Height, + 0, + GL_RGB, + GL_UNSIGNED_BYTE, + nullptr)); + + GLCHK(glTexParameteri( + GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GLCHK(glTexParameteri( + GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GLCHK(glTexParameterf( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GLCHK(glTexParameterf( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + + GLCHK(glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + mPaddingFboTexture[i], + 0)); + + gle = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (gle != GL_FRAMEBUFFER_COMPLETE) { + ULOGE("invalid framebuffer status"); + goto err; + } + } + + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); + return 0; + +err: + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); + cleanupPaddingFbo(); + return -EPROTO; +} + + +void Gles2Video::cleanupPaddingFbo(void) +{ + if (mPaddingFboTexture[0] > 0) { + GLCHK(glDeleteTextures(4, mPaddingFboTexture)); + memset(mPaddingFboTexture, 0, sizeof(mPaddingFboTexture)); + } + if (mPaddingFbo[0] > 0) { + GLCHK(glDeleteFramebuffers(4, mPaddingFbo)); + memset(mPaddingFbo, 0, sizeof(mPaddingFbo)); + } + mPaddingPass1Width = 0; + mPaddingPass1Height = 0; + mPaddingPass2Width = 0; + mPaddingPass2Height = 0; +} + + +void Gles2Video::renderPadding(size_t framePlaneStride[3], + const struct vdef_raw_format *format, + const struct vdef_frame_info *info, + const struct vdef_rect *crop, + const struct pdraw_rect *renderPos, + float videoW, + float videoH, + float videoW2, + float videoH2, + float videoAR, + float windowAR, + enum pdraw_video_renderer_fill_mode fillMode, + bool immersive, + Eigen::Matrix4f &viewProjMat) +{ + unsigned int i; + float stride[GLES2_VIDEO_TEX_UNIT_COUNT * 2] = {0}; + float maxCoords[GLES2_VIDEO_TEX_UNIT_COUNT * 2] = {0}; + float vertices[12]; + float texCoords[8]; + bool mirrorTexture = false; + + if (!mBlurInit) + return; + + if ((fillMode != PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_CROP) && + (fillMode != PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_EXTEND)) + return; + + enum program prog; + prog = getProgram(format); + + /* Pass 1 downscale */ + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mPaddingFbo[0])); + GLCHK(glViewport(0, 0, mPaddingPass1Width, mPaddingPass1Height)); + + GLCHK(glUseProgram(mProgram[prog])); + + switch (prog) { + default: + case PROGRAM_NOCONV: + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit)); +# ifdef BCM_VIDEOCORE + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextures[0])); +# else /* BCM_VIDEOCORE */ + GLCHK(glBindTexture(GL_TEXTURE_2D, + (mExtTexture > 0) ? mExtTexture + : mTextures[0])); +# endif /* BCM_VIDEOCORE */ + GLCHK(glUniform1i(mUniformSamplers[prog][0], mFirstTexUnit)); + stride[0] = 1.f / framePlaneStride[0]; + stride[1] = 1.f / info->resolution.height; + maxCoords[0] = + (float)(crop->left + crop->width) / framePlaneStride[0]; + maxCoords[1] = (float)(crop->top + crop->height) / + info->resolution.height; + break; + case PROGRAM_YUV_TO_RGB_PLANAR: + case PROGRAM_YUV_TO_RGB_PLANAR_10_16LE: + mirrorTexture = true; + for (i = 0; i < GLES2_VIDEO_TEX_UNIT_COUNT; i++) { + int height = + info->resolution.height / ((i > 0) ? 2 : 1); + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + i)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[i])); + GLCHK(glUniform1i(mUniformSamplers[prog][i], + mFirstTexUnit + i)); + stride[2 * i] = 1.f / framePlaneStride[i]; + stride[2 * i + 1] = 1.f / height; + maxCoords[2 * i] = + (float)(crop->left + crop->width) / + (framePlaneStride[i] * ((i > 0) ? 2 : 1)); + maxCoords[2 * i + 1] = + (float)(crop->top + crop->height) / + info->resolution.height; + } + break; + case PROGRAM_YUV_TO_RGB_SEMIPLANAR: + case PROGRAM_YUV_TO_RGB_SEMIPLANAR_10_16LE_HIGH: + mirrorTexture = true; + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 0)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[0])); + GLCHK(glUniform1i(mUniformSamplers[prog][0], + mFirstTexUnit + 0)); + stride[0] = 1.f / framePlaneStride[0]; + stride[1] = 1.f / info->resolution.height; + maxCoords[0] = + (float)(crop->left + crop->width) / framePlaneStride[0]; + maxCoords[1] = (float)(crop->top + crop->height) / + info->resolution.height; + + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 1)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[1])); + GLCHK(glUniform1i(mUniformSamplers[prog][1], + mFirstTexUnit + 1)); + stride[2] = 1.f / (framePlaneStride[1] / 2); + stride[3] = 1.f / (info->resolution.height / 2); + maxCoords[2] = + (float)(crop->left + crop->width) / framePlaneStride[1]; + maxCoords[3] = (float)(crop->top + crop->height) / + info->resolution.height; + break; + } + + GLCHK(glUniform2fv( + mProgramStride[prog], GLES2_VIDEO_TEX_UNIT_COUNT, stride)); + GLCHK(glUniform2fv(mProgramMaxCoords[prog], + GLES2_VIDEO_TEX_UNIT_COUNT, + maxCoords)); + GLCHK(glUniform3f(mProgramYuv2RgbOffset[prog], + vdef_yuv_to_rgb_norm_offset[info->matrix_coefs] + [info->full_range][0], + vdef_yuv_to_rgb_norm_offset[info->matrix_coefs] + [info->full_range][1], + vdef_yuv_to_rgb_norm_offset[info->matrix_coefs] + [info->full_range][2])); + GLCHK(glUniformMatrix3fv( + mProgramYuv2RgbMatrix[prog], + 1, + GL_FALSE, + &vdef_yuv_to_rgb_norm_matrix[info->matrix_coefs] + [info->full_range][0])); + + /* Disable overexposure zebras */ + updateZebra(nullptr, prog, false, 0.f); + + vertices[0] = -1.; + vertices[1] = -1.; + vertices[2] = 1.; + vertices[3] = 1.; + vertices[4] = -1.; + vertices[5] = 1.; + vertices[6] = -1.; + vertices[7] = 1.; + vertices[8] = 1.; + vertices[9] = 1.; + vertices[10] = 1.; + vertices[11] = 1.; + + Eigen::Matrix4f id = Eigen::Matrix4f::Identity(); + GLCHK(glUniformMatrix4fv( + mProgramTransformMatrix[prog], 1, false, id.data())); + GLCHK(glUniform1f(mProgramSatCoef[prog], 1.f)); + GLCHK(glUniform1f(mProgramLightCoef[prog], 1.f)); + GLCHK(glUniform1f(mProgramDarkCoef[prog], 1.f)); + + GLCHK(glVertexAttribPointer( + mPositionHandle[prog], 3, GL_FLOAT, false, 0, vertices)); + GLCHK(glEnableVertexAttribArray(mPositionHandle[prog])); + + if (mirrorTexture) { + texCoords[0] = (float)crop->left / (float)framePlaneStride[0]; + texCoords[1] = (float)(crop->top + crop->height) / + (float)info->resolution.height; + texCoords[2] = (float)(crop->left + crop->width) / + (float)framePlaneStride[0]; + texCoords[3] = (float)(crop->top + crop->height) / + (float)info->resolution.height; + texCoords[4] = (float)crop->left / (float)framePlaneStride[0]; + texCoords[5] = + (float)crop->top / (float)info->resolution.height; + texCoords[6] = (float)(crop->left + crop->width) / + (float)framePlaneStride[0]; + texCoords[7] = + (float)crop->top / (float)info->resolution.height; + } else { + texCoords[0] = (float)crop->left / (float)framePlaneStride[0]; + texCoords[1] = + (float)crop->top / (float)info->resolution.height; + texCoords[2] = (float)(crop->left + crop->width) / + (float)framePlaneStride[0]; + texCoords[3] = + (float)crop->top / (float)info->resolution.height; + texCoords[4] = (float)crop->left / (float)framePlaneStride[0]; + texCoords[5] = (float)(crop->top + crop->height) / + (float)info->resolution.height; + texCoords[6] = (float)(crop->left + crop->width) / + (float)framePlaneStride[0]; + texCoords[7] = (float)(crop->top + crop->height) / + (float)info->resolution.height; + } + + GLCHK(glVertexAttribPointer( + mTexcoordHandle[prog], 2, GL_FLOAT, false, 0, texCoords)); + GLCHK(glEnableVertexAttribArray(mTexcoordHandle[prog])); + + GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + + GLCHK(glDisableVertexAttribArray(mPositionHandle[prog])); + GLCHK(glDisableVertexAttribArray(mTexcoordHandle[prog])); + + /* Pass 1 horizontal blur */ + vertices[0] = -1.; + vertices[1] = -1.; + vertices[2] = 1.; + vertices[3] = -1.; + vertices[4] = -1.; + vertices[5] = 1.; + vertices[6] = 1.; + vertices[7] = 1.; + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mPaddingFbo[1])); + GLCHK(glViewport(0, 0, mPaddingPass1Width, mPaddingPass1Height)); + GLCHK(glUseProgram(mBlurProgram[0])); + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + + GLES2_VIDEO_TEX_UNIT_COUNT)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mPaddingFboTexture[0])); + GLCHK(glUniform1i(mBlurUniformSampler[0], + mFirstTexUnit + GLES2_VIDEO_TEX_UNIT_COUNT)); + GLCHK(glVertexAttribPointer( + mBlurPositionHandle[0], 2, GL_FLOAT, false, 0, vertices)); + GLCHK(glEnableVertexAttribArray(mBlurPositionHandle[0])); + GLCHK(glUniform1f(mBlurUniformPixelSize[0], 1.0 / mPaddingPass1Width)); + GLCHK(glUniform1fv(mBlurUniformWeights[0], + GLES2_VIDEO_BLUR_TAP_COUNT, + mPaddingBlurWeights)); + GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + GLCHK(glDisableVertexAttribArray(mBlurPositionHandle[0])); + + /* Pass 1 vertical blur */ + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mPaddingFbo[2])); + GLCHK(glViewport(0, 0, mPaddingPass2Width, mPaddingPass2Height)); + GLCHK(glUseProgram(mBlurProgram[1])); + GLCHK(glBindTexture(GL_TEXTURE_2D, mPaddingFboTexture[1])); + GLCHK(glUniform1i(mBlurUniformSampler[1], + mFirstTexUnit + GLES2_VIDEO_TEX_UNIT_COUNT)); + GLCHK(glVertexAttribPointer( + mBlurPositionHandle[1], 2, GL_FLOAT, false, 0, vertices)); + GLCHK(glEnableVertexAttribArray(mBlurPositionHandle[1])); + GLCHK(glUniform1f(mBlurUniformPixelSize[1], 1.0 / mPaddingPass1Height)); + GLCHK(glUniform1fv(mBlurUniformWeights[1], + GLES2_VIDEO_BLUR_TAP_COUNT, + mPaddingBlurWeights)); + GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + GLCHK(glDisableVertexAttribArray(mBlurPositionHandle[1])); + + /* Pass 2 horizontal blur */ + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mPaddingFbo[3])); + GLCHK(glViewport(0, 0, mPaddingPass2Width, mPaddingPass2Height)); + GLCHK(glUseProgram(mBlurProgram[0])); + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + + GLES2_VIDEO_TEX_UNIT_COUNT)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mPaddingFboTexture[2])); + GLCHK(glUniform1i(mBlurUniformSampler[0], + mFirstTexUnit + GLES2_VIDEO_TEX_UNIT_COUNT)); + GLCHK(glVertexAttribPointer( + mBlurPositionHandle[0], 2, GL_FLOAT, false, 0, vertices)); + GLCHK(glEnableVertexAttribArray(mBlurPositionHandle[0])); + GLCHK(glUniform1f(mBlurUniformPixelSize[0], 1.0 / mPaddingPass2Width)); + GLCHK(glUniform1fv(mBlurUniformWeights[0], + GLES2_VIDEO_BLUR_TAP_COUNT, + mPaddingBlurWeights)); + GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + GLCHK(glDisableVertexAttribArray(mBlurPositionHandle[0])); + + /* Pass 2 vertical blur */ + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mPaddingFbo[2])); + GLCHK(glViewport(0, 0, mPaddingPass2Width, mPaddingPass2Height)); + GLCHK(glUseProgram(mBlurProgram[1])); + GLCHK(glBindTexture(GL_TEXTURE_2D, mPaddingFboTexture[3])); + GLCHK(glUniform1i(mBlurUniformSampler[1], + mFirstTexUnit + GLES2_VIDEO_TEX_UNIT_COUNT)); + GLCHK(glVertexAttribPointer( + mBlurPositionHandle[1], 2, GL_FLOAT, false, 0, vertices)); + GLCHK(glEnableVertexAttribArray(mBlurPositionHandle[1])); + GLCHK(glUniform1f(mBlurUniformPixelSize[1], 1.0 / mPaddingPass2Height)); + GLCHK(glUniform1fv(mBlurUniformWeights[1], + GLES2_VIDEO_BLUR_TAP_COUNT, + mPaddingBlurWeights)); + GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + GLCHK(glDisableVertexAttribArray(mBlurPositionHandle[1])); + + /* Render to screen */ + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); + GLCHK(glViewport(renderPos->x, + renderPos->y, + renderPos->width, + renderPos->height)); + GLCHK(glUseProgram(mProgram[PROGRAM_NOCONV])); + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + + GLES2_VIDEO_TEX_UNIT_COUNT)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mPaddingFboTexture[2])); + GLCHK(glUniform1i(mUniformSamplers[PROGRAM_NOCONV][0], + mFirstTexUnit + GLES2_VIDEO_TEX_UNIT_COUNT)); + GLCHK(glUniformMatrix4fv(mProgramTransformMatrix[PROGRAM_NOCONV], + 1, + false, + viewProjMat.data())); + GLCHK(glUniform1f(mProgramSatCoef[PROGRAM_NOCONV], mSatCoef)); + GLCHK(glUniform1f(mProgramLightCoef[PROGRAM_NOCONV], mLightCoef)); + GLCHK(glUniform1f(mProgramDarkCoef[PROGRAM_NOCONV], + (immersive) ? mDarkCoef + : PDRAW_BLURRED_PADDING_DARK_COEF * + mDarkCoef)); + + maxCoords[0] = 1.; + maxCoords[1] = 1.; + stride[0] = 0.; + stride[1] = 0.; + GLCHK(glUniform2fv(mProgramStride[PROGRAM_NOCONV], + GLES2_VIDEO_TEX_UNIT_COUNT, + stride)); + GLCHK(glUniform2fv(mProgramMaxCoords[PROGRAM_NOCONV], + GLES2_VIDEO_TEX_UNIT_COUNT, + maxCoords)); + + vertices[0] = -videoW2; + vertices[1] = -videoH2; + vertices[2] = 1.; + vertices[3] = videoW2; + vertices[4] = -videoH2; + vertices[5] = 1.; + vertices[6] = -videoW2; + vertices[7] = videoH2; + vertices[8] = 1.; + vertices[9] = videoW2; + vertices[10] = videoH2; + vertices[11] = 1.; + + if (fillMode == PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_EXTEND) { + texCoords[0] = -(videoW2 / videoW / 2.) + 0.5; + texCoords[1] = -(videoH2 / videoH / 2.) + 0.5; + texCoords[2] = videoW2 / videoW / 2. + 0.5; + texCoords[3] = -(videoH2 / videoH / 2.) + 0.5; + texCoords[4] = -(videoW2 / videoW / 2.) + 0.5; + texCoords[5] = videoH2 / videoH / 2. + 0.5; + texCoords[6] = videoW2 / videoW / 2. + 0.5; + texCoords[7] = videoH2 / videoH / 2. + 0.5; + } else { + texCoords[0] = 0.; + texCoords[1] = 0.; + texCoords[2] = 1.; + texCoords[3] = 0.; + texCoords[4] = 0.; + texCoords[5] = 1.; + texCoords[6] = 1.; + texCoords[7] = 1.; + } + + GLCHK(glVertexAttribPointer(mPositionHandle[PROGRAM_NOCONV], + 3, + GL_FLOAT, + false, + 0, + vertices)); + GLCHK(glEnableVertexAttribArray(mPositionHandle[PROGRAM_NOCONV])); + + GLCHK(glVertexAttribPointer(mTexcoordHandle[PROGRAM_NOCONV], + 2, + GL_FLOAT, + false, + 0, + texCoords)); + GLCHK(glEnableVertexAttribArray(mTexcoordHandle[PROGRAM_NOCONV])); + + GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + + GLCHK(glDisableVertexAttribArray(mPositionHandle[PROGRAM_NOCONV])); + GLCHK(glDisableVertexAttribArray(mTexcoordHandle[PROGRAM_NOCONV])); + + GLCHK(glUseProgram(mProgram[prog])); +} + + +void Gles2Video::setupZebra(enum program prog) +{ + float co = cosf(PDRAW_ZEBRA_ANGLE); + float si = sinf(PDRAW_ZEBRA_ANGLE); + const GLfloat zebra_mat[] = {co, si, -si, co}; + + GLCHK(glUseProgram(mProgram[prog])); + GLCHK(glUniformMatrix2fv( + glGetUniformLocation(mProgram[prog], "zebra_mat"), + 1, + GL_FALSE, + zebra_mat)); + GLCHK(glUniform1fv( + glGetUniformLocation(mProgram[prog], "zebra_avg_weights"), + 9, + zebraAvgWeights)); +} + + +void Gles2Video::updateZebra(struct pdraw_rect *contentPos, + enum program prog, + bool enable, + float threshold) +{ + GLCHK(glUniform1f(mProgramZebraEnable[prog], enable ? 1.f : 0.f)); + GLCHK(glUniform1f(mProgramZebraThreshold[prog], threshold)); + + if (enable && contentPos != nullptr) { + struct timespec ts; + uint64_t time_us; + if (time_get_monotonic(&ts) < 0) { + ULOGE("time_get_monotonic"); + return; + } + if (time_timespec_to_us(&ts, &time_us) < 0) { + ULOGE("time_timespec_to_us"); + return; + } + uint64_t zebra_period_us = + (uint64_t)(1000000.f / PDRAW_ZEBRA_FREQUENCY_HZ); + float zebra_phase = + (float)(time_us % zebra_period_us) / zebra_period_us; + GLCHK(glUniform1f(mProgramZebraPhase[prog], zebra_phase)); + float zebra_weight = + PDRAW_ZEBRA_WEIGHT * contentPos->width / 1920; + GLCHK(glUniform1f(mProgramZebraWeight[prog], zebra_weight)); + } +} + + +int Gles2Video::setupHistograms(void) +{ + int ret = 0; + GLenum gle; + GLint success = 0; + GLint vertexShaderHistogram = 0; + GLint fragmentShaderHistogram[PROGRAM_MAX] = {0}; + unsigned int i; + + /* Buffers allocation */ + mHistogramBuffer = + (uint8_t *)malloc(4 * GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE * + GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE); + if (mHistogramBuffer == nullptr) { + ULOG_ERRNO("malloc", ENOMEM); + ret = -ENOMEM; + goto error; + } + for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) { + mHistogram[i] = (uint32_t *)malloc(256 * sizeof(uint32_t)); + if (mHistogram[i] == nullptr) { + ULOG_ERRNO("malloc", ENOMEM); + ret = -ENOMEM; + goto error; + } + } + for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) { + mHistogramNorm[i] = (float *)calloc(256, sizeof(float)); + if (mHistogramNorm[i] == nullptr) { + ULOG_ERRNO("calloc", ENOMEM); + ret = -ENOMEM; + goto error; + } + } + + /* Shaders compilation */ + vertexShaderHistogram = glCreateShader(GL_VERTEX_SHADER); + if ((vertexShaderHistogram == 0) || + (vertexShaderHistogram == GL_INVALID_ENUM)) { + ULOGE("failed to create vertex shader"); + ret = -ENOMEM; + goto error; + } + + glShaderSource( + vertexShaderHistogram, 1, &histogramVertexShader, nullptr); + glCompileShader(vertexShaderHistogram); + glGetShaderiv(vertexShaderHistogram, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar infoLog[512]; + glGetShaderInfoLog( + vertexShaderHistogram, 512, nullptr, infoLog); + ULOGE("vertex shader compilation failed '%s'", infoLog); + ret = -EPROTO; + goto error; + } + + for (i = 0; i < PROGRAM_MAX; i++) { + fragmentShaderHistogram[i] = glCreateShader(GL_FRAGMENT_SHADER); + if ((fragmentShaderHistogram[i] == 0) || + (fragmentShaderHistogram[i] == GL_INVALID_ENUM)) { + ULOGE("failed to create fragment shader"); + ret = -ENOMEM; + goto error; + } + + glShaderSource(fragmentShaderHistogram[i], + 2, + histogramFragmentShaders[i], + nullptr); + glCompileShader(fragmentShaderHistogram[i]); + glGetShaderiv(fragmentShaderHistogram[i], + GL_COMPILE_STATUS, + &success); + if (!success) { + GLchar infoLog[512]; + glGetShaderInfoLog(fragmentShaderHistogram[i], + 512, + nullptr, + infoLog); + ULOGE("fragment shader compilation failed '%s'", + infoLog); + ret = -EPROTO; + goto error; + } + } + + /* Shaders link */ + for (i = 0; i < PROGRAM_MAX; i++) { + mHistogramProgram[i] = glCreateProgram(); + glAttachShader(mHistogramProgram[i], vertexShaderHistogram); + glAttachShader(mHistogramProgram[i], + fragmentShaderHistogram[i]); + glLinkProgram(mHistogramProgram[i]); + glGetProgramiv(mHistogramProgram[i], GL_LINK_STATUS, &success); + if (!success) { + GLchar infoLog[512]; + glGetProgramInfoLog( + mHistogramProgram[i], 512, nullptr, infoLog); + ULOGE("program link failed '%s'", infoLog); + ret = -EPROTO; + goto error; + } + } + + glDeleteShader(vertexShaderHistogram); + vertexShaderHistogram = 0; + for (i = 0; i < PROGRAM_MAX; i++) { + glDeleteShader(fragmentShaderHistogram[i]); + fragmentShaderHistogram[i] = 0; + } + + /* Uniforms and attribs */ + for (i = 0; i < PROGRAM_MAX; i++) { + mHistogramYuv2RgbMatrix[i] = glGetUniformLocation( + mHistogramProgram[i], "yuv2rgb_mat"); + mHistogramYuv2RgbOffset[i] = glGetUniformLocation( + mHistogramProgram[i], "yuv2rgb_offset"); + mHistogramUniformSampler[i][0] = glGetUniformLocation( + mHistogramProgram[i], "s_texture_0"); + mHistogramUniformSampler[i][1] = glGetUniformLocation( + mHistogramProgram[i], "s_texture_1"); + mHistogramUniformSampler[i][2] = glGetUniformLocation( + mHistogramProgram[i], "s_texture_2"); + mHistogramPositionHandle[i] = + glGetAttribLocation(mHistogramProgram[i], "position"); + mHistogramTexcoordHandle[i] = + glGetAttribLocation(mHistogramProgram[i], "texcoord"); + } + + /* Create the framebuffer */ + GLCHK(glGenFramebuffers(1, &mHistogramFbo)); + if (mHistogramFbo <= 0) { + ULOGE("failed to create framebuffer"); + ret = -ENOMEM; + goto error; + } + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mHistogramFbo)); + + GLCHK(glGenTextures(1, &mHistogramFboTexture)); + if (mHistogramFboTexture <= 0) { + ULOGE("failed to create texture"); + ret = -ENOMEM; + goto error; + } + GLCHK(glBindTexture(GL_TEXTURE_2D, mHistogramFboTexture)); + GLCHK(glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA, + GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE, + GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + nullptr)); + + GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GLCHK(glTexParameterf( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GLCHK(glTexParameterf( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + + GLCHK(glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + mHistogramFboTexture, + 0)); + + gle = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (gle != GL_FRAMEBUFFER_COMPLETE) { + ULOGE("invalid framebuffer status"); + ret = -EPROTO; + goto error; + } + + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); + mHistogramInit = true; + return 0; + +error: + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); + + if (vertexShaderHistogram) + glDeleteShader(vertexShaderHistogram); + for (i = 0; i < PROGRAM_MAX; i++) { + if (fragmentShaderHistogram[i]) + glDeleteShader(fragmentShaderHistogram[i]); + } + + cleanupHistograms(); + + return ret; +} + + +void Gles2Video::cleanupHistograms(void) +{ + unsigned int i; + + if (mHistogramFboTexture > 0) { + GLCHK(glDeleteTextures(1, &mHistogramFboTexture)); + mHistogramFboTexture = 0; + } + if (mHistogramFbo > 0) { + GLCHK(glDeleteFramebuffers(1, &mHistogramFbo)); + mHistogramFbo = 0; + } + for (i = 0; i < PROGRAM_MAX; i++) { + if (mHistogramProgram[i] > 0) { + GLCHK(glDeleteProgram(mHistogramProgram[i])); + mHistogramProgram[i] = 0; + } + } + free(mHistogramBuffer); + mHistogramBuffer = nullptr; + for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) { + free(mHistogram[i]); + mHistogram[i] = nullptr; + } + for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) { + free(mHistogramNorm[i]); + mHistogramNorm[i] = nullptr; + } + mHistogramInit = false; +} + + +void Gles2Video::computeHistograms(size_t framePlaneStride[3], + const struct vdef_raw_format *format, + const struct vdef_frame_info *info, + const struct vdef_rect *crop, + const struct pdraw_rect *renderPos, + bool enable) +{ + float vertices[12]; + float texCoords[8]; + unsigned int i, j; + uint8_t *buf; + uint32_t histoMax[PDRAW_HISTOGRAM_CHANNEL_MAX]; + struct timespec ts; + uint64_t time_us; + bool mirrorTexture = false; + + if ((!mHistogramInit) || (!enable)) { + mHistogramLastComputeTime = 0; + for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) + mHistogramValid[i] = false; + return; + } + + if (time_get_monotonic(&ts) < 0) { + ULOGE("time_get_monotonic"); + return; + } + if (time_timespec_to_us(&ts, &time_us) < 0) { + ULOGE("time_timespec_to_us"); + return; + } + if ((mHistogramLastComputeTime > 0) && + (time_us < mHistogramLastComputeTime + + GLES2_VIDEO_HISTOGRAM_COMPUTE_INTERVAL_US)) + return; + mHistogramLastComputeTime = time_us; + for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) + mHistogramValid[i] = false; + + enum program prog; + prog = getProgram(format); + + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mHistogramFbo)); + GLCHK(glDisable(GL_BLEND)); + GLCHK(glViewport(0, + 0, + GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE, + GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE)); + GLCHK(glUseProgram(mHistogramProgram[prog])); + + switch (prog) { + default: + case PROGRAM_NOCONV: + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit)); +# ifdef BCM_VIDEOCORE + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextures[0])); +# else /* BCM_VIDEOCORE */ + GLCHK(glBindTexture(GL_TEXTURE_2D, + (mExtTexture > 0) ? mExtTexture + : mTextures[0])); +# endif /* BCM_VIDEOCORE */ + GLCHK(glUniform1i(mHistogramUniformSampler[prog][0], + mFirstTexUnit)); + break; + case PROGRAM_YUV_TO_RGB_PLANAR: + case PROGRAM_YUV_TO_RGB_PLANAR_10_16LE: + mirrorTexture = true; + for (i = 0; i < GLES2_VIDEO_TEX_UNIT_COUNT; i++) { + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + i)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[i])); + GLCHK(glUniform1i(mHistogramUniformSampler[prog][i], + mFirstTexUnit + i)); + } + break; + case PROGRAM_YUV_TO_RGB_SEMIPLANAR: + case PROGRAM_YUV_TO_RGB_SEMIPLANAR_10_16LE_HIGH: + mirrorTexture = true; + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 0)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[0])); + GLCHK(glUniform1i(mHistogramUniformSampler[prog][0], + mFirstTexUnit + 0)); + + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 1)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[1])); + GLCHK(glUniform1i(mHistogramUniformSampler[prog][1], + mFirstTexUnit + 1)); + break; + } + + GLCHK(glUniform3f(mHistogramYuv2RgbOffset[prog], + vdef_yuv_to_rgb_norm_offset[info->matrix_coefs] + [info->full_range][0], + vdef_yuv_to_rgb_norm_offset[info->matrix_coefs] + [info->full_range][1], + vdef_yuv_to_rgb_norm_offset[info->matrix_coefs] + [info->full_range][2])); + GLCHK(glUniformMatrix3fv( + mHistogramYuv2RgbMatrix[prog], + 1, + GL_FALSE, + &vdef_yuv_to_rgb_norm_matrix[info->matrix_coefs] + [info->full_range][0])); + + vertices[0] = -1.; + vertices[1] = -1.; + vertices[2] = 1.; + vertices[3] = 1.; + vertices[4] = -1.; + vertices[5] = 1.; + vertices[6] = -1.; + vertices[7] = 1.; + vertices[8] = 1.; + vertices[9] = 1.; + vertices[10] = 1.; + vertices[11] = 1.; + + GLCHK(glVertexAttribPointer(mHistogramPositionHandle[prog], + 3, + GL_FLOAT, + false, + 0, + vertices)); + GLCHK(glEnableVertexAttribArray(mHistogramPositionHandle[prog])); + + if (mirrorTexture) { + texCoords[0] = (float)crop->left / (float)framePlaneStride[0]; + texCoords[1] = (float)(crop->top + crop->height) / + (float)info->resolution.height; + texCoords[2] = (float)(crop->left + crop->width) / + (float)framePlaneStride[0]; + texCoords[3] = (float)(crop->top + crop->height) / + (float)info->resolution.height; + texCoords[4] = (float)crop->left / (float)framePlaneStride[0]; + texCoords[5] = + (float)crop->top / (float)info->resolution.height; + texCoords[6] = (float)(crop->left + crop->width) / + (float)framePlaneStride[0]; + texCoords[7] = + (float)crop->top / (float)info->resolution.height; + } else { + texCoords[0] = (float)crop->left / (float)framePlaneStride[0]; + texCoords[1] = + (float)crop->top / (float)info->resolution.height; + texCoords[2] = (float)(crop->left + crop->width) / + (float)framePlaneStride[0]; + texCoords[3] = + (float)crop->top / (float)info->resolution.height; + texCoords[4] = (float)crop->left / (float)framePlaneStride[0]; + texCoords[5] = (float)(crop->top + crop->height) / + (float)info->resolution.height; + texCoords[6] = (float)(crop->left + crop->width) / + (float)framePlaneStride[0]; + texCoords[7] = (float)(crop->top + crop->height) / + (float)info->resolution.height; + } + + GLCHK(glVertexAttribPointer(mHistogramTexcoordHandle[prog], + 2, + GL_FLOAT, + false, + 0, + texCoords)); + GLCHK(glEnableVertexAttribArray(mHistogramTexcoordHandle[prog])); + + GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + + GLCHK(glDisableVertexAttribArray(mHistogramPositionHandle[prog])); + GLCHK(glDisableVertexAttribArray(mHistogramTexcoordHandle[prog])); + + GLCHK(glFinish()); + + /* Read pixels to CPU buffer */ + GLCHK(glReadPixels(0, + 0, + GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE, + GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE, + GL_RGBA, + GL_UNSIGNED_BYTE, + mHistogramBuffer)); + + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); + GLCHK(glViewport(renderPos->x, + renderPos->y, + renderPos->width, + renderPos->height)); + + /* Reset the histograms */ + for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) + memset(mHistogram[i], 0, 256 * sizeof(uint32_t)); + + /* Count the values */ + for (j = 0, buf = mHistogramBuffer; + j < GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE * + GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE; + j++) { + for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) + mHistogram[i][*buf++]++; + } + + /* Histograms normalization */ + memset(histoMax, 0, sizeof(histoMax)); + for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) { + for (j = 0; j < 256; j++) { + if (mHistogram[i][j] > histoMax[i]) + histoMax[i] = mHistogram[i][j]; + } + } + histoMax[1] = (histoMax[0] > histoMax[1]) ? histoMax[0] : histoMax[1]; + histoMax[1] = (histoMax[1] > histoMax[2]) ? histoMax[1] : histoMax[2]; + histoMax[2] = histoMax[1]; + histoMax[0] = histoMax[1]; + for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) { + if (histoMax[i] == 0.) { + memset(mHistogramNorm[i], 0, 256 * sizeof(float)); + continue; + } + for (j = 0; j < 256; j++) { + mHistogramNorm[i][j] = + (float)mHistogram[i][j] / (float)histoMax[i]; + } + mHistogramValid[i] = true; + } +} + + +void Gles2Video::getHistograms(float *histogram[PDRAW_HISTOGRAM_CHANNEL_MAX], + size_t histogramLen[PDRAW_HISTOGRAM_CHANNEL_MAX]) +{ + unsigned int i; + + for (i = 0; i < PDRAW_HISTOGRAM_CHANNEL_MAX; i++) { + if ((mHistogramValid[i]) && (mHistogramNorm[i] != nullptr)) { + histogram[i] = mHistogramNorm[i]; + histogramLen[i] = 256; + } + } +} + + +void Gles2Video::startTransition(enum gles2_video_transition transition, + uint64_t duration, + bool hold) +{ + if (mTransition != GLES2_VIDEO_TRANSITION_NONE) + abortTransition(); + mTransition = transition; + mTransitionDuration = duration; + mTransitionHold = hold; +} + + +void Gles2Video::abortTransition(void) +{ + mTransition = GLES2_VIDEO_TRANSITION_NONE; + mTransitionDuration = 0; + mTransitionStartTime = 0; + mTransitionHold = false; +} + + +void Gles2Video::updateTransition(void) +{ + int res; + struct timespec ts = {0, 0}; + uint64_t curTime = 0; + float progress, blurSigma; + + mApplyBlur = false; + mSatCoef = mBaseSatCoef; + mLightCoef = mBaseLightCoef; + mDarkCoef = mBaseDarkCoef; + + if (mTransition == GLES2_VIDEO_TRANSITION_NONE) + return; + + res = time_get_monotonic(&ts); + if (res < 0) + ULOG_ERRNO("time_get_monotonic", -res); + res = time_timespec_to_us(&ts, &curTime); + if (res < 0) + ULOG_ERRNO("time_timespec_to_us", -res); + if (mTransitionStartTime == 0) + mTransitionStartTime = curTime; + + progress = (float)(curTime - mTransitionStartTime) / + (float)mTransitionDuration; + if (progress > 1.0) { + progress = 1.0; + if (!mTransitionHold) { + /* Transition finished */ + abortTransition(); + return; + } + } + + switch (mTransition) { + case GLES2_VIDEO_TRANSITION_FADE_TO_BLACK: + mDarkCoef = mBaseDarkCoef * (1. - progress); + break; + case GLES2_VIDEO_TRANSITION_FADE_FROM_BLACK: + mDarkCoef = mBaseDarkCoef * progress; + break; + case GLES2_VIDEO_TRANSITION_FADE_TO_WHITE: + mLightCoef = mBaseLightCoef * (1. - progress); + break; + case GLES2_VIDEO_TRANSITION_FADE_FROM_WHITE: + mLightCoef = mBaseLightCoef * progress; + break; + case GLES2_VIDEO_TRANSITION_FADE_TO_BLACK_AND_WHITE: + mSatCoef = mBaseSatCoef * (1. - progress); + break; + case GLES2_VIDEO_TRANSITION_FADE_FROM_BLACK_AND_WHITE: + mSatCoef = mBaseSatCoef * progress; + break; + case GLES2_VIDEO_TRANSITION_FADE_TO_BLUR: + blurSigma = progress * (GLES2_VIDEO_BLUR_MAX_SIGMA - + GLES2_VIDEO_BLUR_MIN_SIGMA) + + GLES2_VIDEO_BLUR_MIN_SIGMA; + pdraw_gaussianDistribution( + mBlurWeights, GLES2_VIDEO_BLUR_TAP_COUNT, blurSigma); + mApplyBlur = mBlurInit; + break; + case GLES2_VIDEO_TRANSITION_FADE_FROM_BLUR: + blurSigma = (1. - progress) * (GLES2_VIDEO_BLUR_MAX_SIGMA - + GLES2_VIDEO_BLUR_MIN_SIGMA) + + GLES2_VIDEO_BLUR_MIN_SIGMA; + pdraw_gaussianDistribution( + mBlurWeights, GLES2_VIDEO_BLUR_TAP_COUNT, blurSigma); + mApplyBlur = mBlurInit; + break; + case GLES2_VIDEO_TRANSITION_FLASH: + mLightCoef = mBaseLightCoef * + (powf(progress, GLES2_VIDEO_FLASH_GAMMA_COEF) * + GLES2_VIDEO_FLASH_LIGHT_COEF + + 1. - GLES2_VIDEO_FLASH_LIGHT_COEF); + mSatCoef = mBaseSatCoef * + powf(progress, GLES2_VIDEO_FLASH_GAMMA_COEF); + break; + default: + ULOGE("unsupported transition type: %d", mTransition); + break; + } +} + + +int Gles2Video::loadFrame(const uint8_t *framePlanes[3], + size_t framePlaneStride[3], + const struct vdef_raw_format *format, + const struct vdef_frame_info *info, + struct egl_display *eglDisplay) +{ + unsigned int i; + + if ((info == nullptr) || (format == nullptr)) { + ULOGE("invalid frame info"); + return -EINVAL; + } + if ((info->resolution.width == 0) || (info->resolution.height == 0)) { + ULOGE("invalid dimensions"); + return -EINVAL; + } + + enum program prog; + prog = getProgram(format); + + GLCHK(glUseProgram(mProgram[prog])); + + switch (prog) { + default: + case PROGRAM_NOCONV: { +# ifdef BCM_VIDEOCORE + EGLDisplay display = (EGLDisplay)eglDisplay; + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit)); + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextures[0])); + if (mEglImage != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(display, mEglImage); + mEglImage = EGL_NO_IMAGE_KHR; + } + mEglImage = eglCreateImageKHR(display, + EGL_NO_CONTEXT, + EGL_IMAGE_BRCM_MULTIMEDIA, + (EGLClientBuffer)framePlanes[0], + nullptr); + if (mEglImage == EGL_NO_IMAGE_KHR) { + ULOGE("failed to create EGLImage"); + return -EPROTO; + } + GLCHK(glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, + mEglImage)); + GLCHK(glUniform1i(mUniformSamplers[prog][0], mFirstTexUnit)); +# endif /* BCM_VIDEOCORE */ + break; + } + case PROGRAM_YUV_TO_RGB_PLANAR: + if ((framePlanes == nullptr) || (framePlaneStride == nullptr)) { + ULOGE("invalid planes"); + return -EINVAL; + } + for (i = 0; i < GLES2_VIDEO_TEX_UNIT_COUNT; i++) { + int height = + info->resolution.height / ((i > 0) ? 2 : 1); + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + i)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[i])); + GLCHK(glTexImage2D(GL_TEXTURE_2D, + 0, + GL_LUMINANCE, + framePlaneStride[i], + height, + 0, + GL_LUMINANCE, + GL_UNSIGNED_BYTE, + framePlanes[i])); + } + break; + case PROGRAM_YUV_TO_RGB_PLANAR_10_16LE: + if ((framePlanes == nullptr) || (framePlaneStride == nullptr)) { + ULOGE("invalid planes"); + return -EINVAL; + } + for (i = 0; i < GLES2_VIDEO_TEX_UNIT_COUNT; i++) { + int height = + info->resolution.height / ((i > 0) ? 2 : 1); + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + i)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[i])); + GLCHK(glTexImage2D(GL_TEXTURE_2D, + 0, + GL_LUMINANCE_ALPHA, + framePlaneStride[i] / 2, + height, + 0, + GL_LUMINANCE_ALPHA, + GL_UNSIGNED_BYTE, + framePlanes[i])); + } + break; + case PROGRAM_YUV_TO_RGB_SEMIPLANAR: + if ((framePlanes == nullptr) || (framePlaneStride == nullptr)) { + ULOGE("invalid planes"); + return -EINVAL; + } + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 0)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[0])); + GLCHK(glTexImage2D(GL_TEXTURE_2D, + 0, + GL_LUMINANCE, + framePlaneStride[0], + info->resolution.height, + 0, + GL_LUMINANCE, + GL_UNSIGNED_BYTE, + framePlanes[0])); + + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 1)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[1])); + GLCHK(glTexImage2D(GL_TEXTURE_2D, + 0, + GL_LUMINANCE_ALPHA, + framePlaneStride[1] / 2, + info->resolution.height / 2, + 0, + GL_LUMINANCE_ALPHA, + GL_UNSIGNED_BYTE, + framePlanes[1])); + break; + case PROGRAM_YUV_TO_RGB_SEMIPLANAR_10_16LE_HIGH: + if ((framePlanes == nullptr) || (framePlaneStride == nullptr)) { + ULOGE("invalid planes"); + return -EINVAL; + } + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 0)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[0])); + GLCHK(glTexImage2D(GL_TEXTURE_2D, + 0, + GL_LUMINANCE_ALPHA, + framePlaneStride[0] / 2, + info->resolution.height, + 0, + GL_LUMINANCE_ALPHA, + GL_UNSIGNED_BYTE, + framePlanes[0])); + + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 1)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[1])); + GLCHK(glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA, + framePlaneStride[1] / 4, + info->resolution.height / 2, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + framePlanes[1])); + break; + } + + return 0; +} + + +int Gles2Video::renderFrame(const struct pdraw_rect *renderPos, + struct pdraw_rect *contentPos, + Eigen::Matrix4f &viewProjMat, + size_t framePlaneStride[3], + const struct vdef_raw_format *format, + const struct vdef_frame_info *info, + const struct vdef_rect *crop, + struct vmeta_frame *metadata, + const struct pdraw_video_renderer_params *params) +{ + int ret; + unsigned int i; + float stride[GLES2_VIDEO_TEX_UNIT_COUNT * 2] = {0}; + float maxCoords[GLES2_VIDEO_TEX_UNIT_COUNT * 2] = {0}; + float vertices[12]; + float texCoords[8]; + bool mirrorTexture = false; + float videoAR; + + if ((renderPos == nullptr) || (renderPos->width == 0) || + (renderPos->height == 0)) { + ULOGE("invalid render position"); + return -EINVAL; + } + if ((info == nullptr) || (format == nullptr)) { + ULOGE("invalid frame info"); + return -EINVAL; + } + if ((info->resolution.width == 0) || (info->resolution.height == 0) || + (info->sar.width == 0) || (info->sar.height == 0) || + (framePlaneStride[0] == 0)) { + ULOGE("invalid dimensions"); + return -EINVAL; + } + + struct vdef_frame_info _info = *info; + if (_info.matrix_coefs == VDEF_MATRIX_COEFS_UNKNOWN) { + /* Default to BT.709 */ + _info.matrix_coefs = VDEF_MATRIX_COEFS_BT709; + } + + enum program prog; + prog = getProgram(format); + + if ((mVideoWidth != _info.resolution.width) || + (mVideoHeight != _info.resolution.height)) { + mVideoWidth = _info.resolution.width; + mVideoHeight = _info.resolution.height; + ret = setupBlurFbo(); + if (ret < 0) + ULOG_ERRNO("setupBlurFbo", -ret); + ret = setupPaddingFbo(params->fill_mode); + if (ret < 0) + ULOG_ERRNO("setupPaddingFbo", -ret); + } + + if (prog == PROGRAM_YUV_TO_RGB_PLANAR_10_16LE) { + framePlaneStride[0] /= 2; + framePlaneStride[1] /= 2; + framePlaneStride[2] /= 2; + } else if (prog == PROGRAM_YUV_TO_RGB_SEMIPLANAR_10_16LE_HIGH) { + framePlaneStride[0] /= 2; + framePlaneStride[1] /= 2; + } + + updateTransition(); + + computeHistograms(framePlaneStride, + format, + &_info, + crop, + renderPos, + params->enable_histograms); + + /* Video fill mode */ + float windowAR = (float)renderPos->width / (float)renderPos->height; + float sar = (float)_info.sar.width / (float)_info.sar.height; + if (params->video_texture_dar_height != 0 && + params->video_texture_dar_width != 0) { + /* If the display aspect ratio is given, + * we apply it instead of the source width/height */ + videoAR = (float)params->video_texture_dar_width / + (float)params->video_texture_dar_height; + } else { + videoAR = (float)_info.resolution.width / + (float)_info.resolution.height * sar; + } + + float windowW = 1.; + float windowH = windowAR; + float ratioW = 1.; + float ratioH = 1.; + float ratioW2 = 1.; + float ratioH2 = 1.; + switch (params->fill_mode) { + default: + case PDRAW_VIDEO_RENDERER_FILL_MODE_FIT: + case PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_CROP: + case PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_EXTEND: + /* Maintain video aspect ratio without crop and add borders if + * window aspect ratio and video aspect ratio differ */ + if (videoAR >= windowAR) { + ratioW = 1.; + ratioH = windowAR / videoAR; + ratioW2 = videoAR / windowAR; + ratioH2 = 1.; + } else { + ratioW = videoAR / windowAR; + ratioH = 1.; + ratioW2 = 1.; + ratioH2 = windowAR / videoAR; + } + break; + case PDRAW_VIDEO_RENDERER_FILL_MODE_CROP: + /* Maintain video aspect ratio without borders and add video + * crop if window aspect ratio and video aspect ratio differ */ + if (videoAR >= windowAR) { + ratioW = videoAR / windowAR; + ratioH = 1.; + } else { + ratioW = 1.; + ratioH = windowAR / videoAR; + } + break; + } + float videoW = ratioW / windowW * params->video_scale_factor; + float videoH = ratioH / windowH * params->video_scale_factor; + float videoW2 = ratioW2 / windowW; + float videoH2 = ratioH2 / windowH; + + if (contentPos) { + int32_t dw; + int32_t dh; + contentPos->width = + ratioW * renderPos->width * params->video_scale_factor; + contentPos->height = + ratioH * renderPos->height * params->video_scale_factor; + + dw = (int32_t)renderPos->width - (int32_t)contentPos->width; + dh = (int32_t)renderPos->height - (int32_t)contentPos->height; + contentPos->x = dw / 2; + contentPos->y = dh / 2; + } + + renderPadding(framePlaneStride, + format, + &_info, + crop, + renderPos, + videoW, + videoH, + videoW2, + videoH2, + videoAR, + windowAR, + params->fill_mode, + false, + viewProjMat); + + if (mApplyBlur) { + renderBlur(framePlaneStride, + format, + &_info, + crop, + renderPos, + videoW, + videoH, + viewProjMat); + } else { + GLCHK(glUseProgram(mProgram[prog])); + + switch (prog) { + default: + case PROGRAM_NOCONV: + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit)); +# ifdef BCM_VIDEOCORE + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, + mTextures[0])); +# else /* BCM_VIDEOCORE */ + GLCHK(glBindTexture(GL_TEXTURE_2D, + (mExtTexture > 0) ? mExtTexture + : mTextures[0])); +# endif /* BCM_VIDEOCORE */ + GLCHK(glUniform1i(mUniformSamplers[prog][0], + mFirstTexUnit)); + stride[0] = 1.f / framePlaneStride[0]; + stride[1] = 1.f / _info.resolution.height; + maxCoords[0] = (float)(crop->left + crop->width) / + framePlaneStride[0]; + maxCoords[1] = (float)(crop->top + crop->height) / + _info.resolution.height; + break; + case PROGRAM_YUV_TO_RGB_PLANAR: + case PROGRAM_YUV_TO_RGB_PLANAR_10_16LE: + mirrorTexture = true; + for (i = 0; i < GLES2_VIDEO_TEX_UNIT_COUNT; i++) { + int height = _info.resolution.height / + ((i > 0) ? 2 : 1); + GLCHK(glActiveTexture(GL_TEXTURE0 + + mFirstTexUnit + i)); + GLCHK(glBindTexture(GL_TEXTURE_2D, + mTextures[i])); + GLCHK(glUniform1i(mUniformSamplers[prog][i], + mFirstTexUnit + i)); + stride[2 * i] = 1.f / framePlaneStride[i]; + stride[2 * i + 1] = 1.f / height; + maxCoords[2 * i] = + (float)(crop->left + crop->width) / + (framePlaneStride[i] * + ((i > 0) ? 2 : 1)); + maxCoords[2 * i + 1] = + (float)(crop->top + crop->height) / + _info.resolution.height; + } + break; + case PROGRAM_YUV_TO_RGB_SEMIPLANAR: + case PROGRAM_YUV_TO_RGB_SEMIPLANAR_10_16LE_HIGH: + mirrorTexture = true; + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 0)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[0])); + GLCHK(glUniform1i(mUniformSamplers[prog][0], + mFirstTexUnit + 0)); + stride[0] = 1.f / framePlaneStride[0]; + stride[1] = 1.f / _info.resolution.height; + maxCoords[0] = (float)(crop->left + crop->width) / + framePlaneStride[0]; + maxCoords[1] = (float)(crop->top + crop->height) / + _info.resolution.height; + + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit + 1)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mTextures[1])); + GLCHK(glUniform1i(mUniformSamplers[prog][1], + mFirstTexUnit + 1)); + stride[2] = 1.f / (framePlaneStride[1] / 2); + stride[3] = 1.f / (_info.resolution.height / 2); + maxCoords[2] = (float)(crop->left + crop->width) / + framePlaneStride[1]; + maxCoords[3] = (float)(crop->top + crop->height) / + _info.resolution.height; + break; + } + + GLCHK(glUniform2fv(mProgramStride[prog], + GLES2_VIDEO_TEX_UNIT_COUNT, + stride)); + GLCHK(glUniform2fv(mProgramMaxCoords[prog], + GLES2_VIDEO_TEX_UNIT_COUNT, + maxCoords)); + GLCHK(glUniform3f( + mProgramYuv2RgbOffset[prog], + vdef_yuv_to_rgb_norm_offset[_info.matrix_coefs] + [_info.full_range][0], + vdef_yuv_to_rgb_norm_offset[_info.matrix_coefs] + [_info.full_range][1], + vdef_yuv_to_rgb_norm_offset[_info.matrix_coefs] + [_info.full_range][2])); + GLCHK(glUniformMatrix3fv( + mProgramYuv2RgbMatrix[prog], + 1, + GL_FALSE, + &vdef_yuv_to_rgb_norm_matrix[_info.matrix_coefs] + [_info.full_range][0])); + + /* Update overexposure zebras */ + updateZebra(contentPos, + prog, + params->enable_overexposure_zebras, + params->overexposure_zebras_threshold); + + GLCHK(glUniformMatrix4fv(mProgramTransformMatrix[prog], + 1, + false, + viewProjMat.data())); + GLCHK(glUniform1f(mProgramSatCoef[prog], mSatCoef)); + GLCHK(glUniform1f(mProgramLightCoef[prog], mLightCoef)); + GLCHK(glUniform1f(mProgramDarkCoef[prog], mDarkCoef)); + + vertices[0] = -videoW; + vertices[1] = -videoH; + vertices[2] = 1.; + vertices[3] = videoW; + vertices[4] = -videoH; + vertices[5] = 1.; + vertices[6] = -videoW; + vertices[7] = videoH; + vertices[8] = 1.; + vertices[9] = videoW; + vertices[10] = videoH; + vertices[11] = 1.; + + GLCHK(glVertexAttribPointer(mPositionHandle[prog], + 3, + GL_FLOAT, + false, + 0, + vertices)); + GLCHK(glEnableVertexAttribArray(mPositionHandle[prog])); + + if (mirrorTexture) { + texCoords[0] = + (float)crop->left / (float)framePlaneStride[0]; + texCoords[1] = (float)(crop->top + crop->height) / + (float)_info.resolution.height; + texCoords[2] = (float)(crop->left + crop->width) / + (float)framePlaneStride[0]; + texCoords[3] = (float)(crop->top + crop->height) / + (float)_info.resolution.height; + texCoords[4] = + (float)crop->left / (float)framePlaneStride[0]; + texCoords[5] = (float)crop->top / + (float)_info.resolution.height; + texCoords[6] = (float)(crop->left + crop->width) / + (float)framePlaneStride[0]; + texCoords[7] = (float)crop->top / + (float)_info.resolution.height; + } else { + texCoords[0] = + (float)crop->left / (float)framePlaneStride[0]; + texCoords[1] = (float)crop->top / + (float)_info.resolution.height; + texCoords[2] = (float)(crop->left + crop->width) / + (float)framePlaneStride[0]; + texCoords[3] = (float)crop->top / + (float)_info.resolution.height; + texCoords[4] = + (float)crop->left / (float)framePlaneStride[0]; + texCoords[5] = (float)(crop->top + crop->height) / + (float)_info.resolution.height; + texCoords[6] = (float)(crop->left + crop->width) / + (float)framePlaneStride[0]; + texCoords[7] = (float)(crop->top + crop->height) / + (float)_info.resolution.height; + } + + GLCHK(glVertexAttribPointer(mTexcoordHandle[prog], + 2, + GL_FLOAT, + false, + 0, + texCoords)); + GLCHK(glEnableVertexAttribArray(mTexcoordHandle[prog])); + + GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + + GLCHK(glDisableVertexAttribArray(mPositionHandle[prog])); + GLCHK(glDisableVertexAttribArray(mTexcoordHandle[prog])); + } + + return 0; +} + + +int Gles2Video::clear(Eigen::Matrix4f &viewProjMat) +{ + float vertices[12]; + float texCoords[8]; + + GLCHK(glUseProgram(mProgram[PROGRAM_NOCONV])); + + GLCHK(glActiveTexture(GL_TEXTURE0 + mFirstTexUnit)); +# ifdef BCM_VIDEOCORE + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextures[0])); +# else /* BCM_VIDEOCORE */ + GLCHK(glBindTexture(GL_TEXTURE_2D, + (mExtTexture > 0) ? mExtTexture : mTextures[0])); +# endif /* BCM_VIDEOCORE */ + GLCHK(glUniform1i(mUniformSamplers[PROGRAM_NOCONV][0], mFirstTexUnit)); + + GLCHK(glUniformMatrix4fv(mProgramTransformMatrix[PROGRAM_NOCONV], + 1, + false, + viewProjMat.data())); + GLCHK(glUniform1f(mProgramDarkCoef[PROGRAM_NOCONV], 0.)); + + vertices[0] = -1.; + vertices[1] = -1.; + vertices[2] = 1.; + vertices[3] = 1.; + vertices[4] = -1.; + vertices[5] = 1.; + vertices[6] = -1.; + vertices[7] = 1.; + vertices[8] = 1.; + vertices[9] = 1.; + vertices[10] = 1.; + vertices[11] = 1.; + + GLCHK(glVertexAttribPointer(mPositionHandle[PROGRAM_NOCONV], + 3, + GL_FLOAT, + false, + 0, + vertices)); + GLCHK(glEnableVertexAttribArray(mPositionHandle[PROGRAM_NOCONV])); + + texCoords[0] = 0.; + texCoords[1] = 0.; + texCoords[2] = 1.; + texCoords[3] = 0.; + texCoords[4] = 0.; + texCoords[5] = 1.; + texCoords[6] = 1.; + texCoords[7] = 1.; + + GLCHK(glVertexAttribPointer(mTexcoordHandle[PROGRAM_NOCONV], + 2, + GL_FLOAT, + false, + 0, + texCoords)); + GLCHK(glEnableVertexAttribArray(mTexcoordHandle[PROGRAM_NOCONV])); + + GLCHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + + GLCHK(glDisableVertexAttribArray(mPositionHandle[PROGRAM_NOCONV])); + GLCHK(glDisableVertexAttribArray(mTexcoordHandle[PROGRAM_NOCONV])); + + return 0; +} + + +void Gles2Video::setExtTexture(GLuint texture) +{ + mExtTexture = texture; +} + +} /* namespace Pdraw */ + +#endif /* USE_GLES2 */ diff --git a/libpdraw/src/pdraw_gles2_video.hpp b/libpdraw/src/pdraw_gles2_video.hpp index 9f316f7..429e61d 100644 --- a/libpdraw/src/pdraw_gles2_video.hpp +++ b/libpdraw/src/pdraw_gles2_video.hpp @@ -1,302 +1,302 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * OpenGL ES 2.0 video rendering - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_GLES2_VIDEO_HPP_ -#define _PDRAW_GLES2_VIDEO_HPP_ - -#ifdef USE_GLES2 - -# include "pdraw_gles2_common.hpp" -# include "pdraw_utils.hpp" - -# ifdef BCM_VIDEOCORE -# include -# include -# endif /* BCM_VIDEOCORE */ - - -namespace Pdraw { - - -# define GLES2_VIDEO_TEX_UNIT_COUNT 3 -# define GLES2_VIDEO_FBO_TEX_UNIT_COUNT 1 -# define GLES2_VIDEO_BLUR_FBO_TARGET_SIZE 512 -# define GLES2_VIDEO_BLUR_TAP_COUNT 15 -# define GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_1 256 -# define GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_2 16 -# define GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE 256 - - -enum gles2_video_transition { - GLES2_VIDEO_TRANSITION_NONE = 0, - GLES2_VIDEO_TRANSITION_FADE_TO_BLACK, - GLES2_VIDEO_TRANSITION_FADE_FROM_BLACK, - GLES2_VIDEO_TRANSITION_FADE_TO_WHITE, - GLES2_VIDEO_TRANSITION_FADE_FROM_WHITE, - GLES2_VIDEO_TRANSITION_FADE_TO_BLACK_AND_WHITE, - GLES2_VIDEO_TRANSITION_FADE_FROM_BLACK_AND_WHITE, - GLES2_VIDEO_TRANSITION_FADE_TO_BLUR, - GLES2_VIDEO_TRANSITION_FADE_FROM_BLUR, - GLES2_VIDEO_TRANSITION_FLASH, -}; - -class Session; - - -class Gles2Video { -public: - Gles2Video(Session *session, - GLuint defaultFbo, - unsigned int firstTexUnit); - - ~Gles2Video(void); - - static int getTexUnitCount(void) - { - return GLES2_VIDEO_TEX_UNIT_COUNT + - GLES2_VIDEO_FBO_TEX_UNIT_COUNT; - } - - GLuint getDefaultFbo(void) - { - return mDefaultFbo; - } - - void setDefaultFbo(GLuint defaultFbo) - { - mDefaultFbo = defaultFbo; - } - - float getSatCoef(void) - { - return mBaseSatCoef; - } - - void setSatCoef(float coef) - { - mBaseSatCoef = coef < 0. ? 0. : (coef > 1. ? 1. : coef); - } - - float getLightCoef(void) - { - return mBaseLightCoef; - } - - void setLightCoef(float coef) - { - mBaseLightCoef = coef < 0. ? 0. : (coef > 1. ? 1. : coef); - } - - float getDarkCoef(void) - { - return mBaseDarkCoef; - } - - void setDarkCoef(float coef) - { - mBaseDarkCoef = coef < 0. ? 0. : (coef > 1. ? 1. : coef); - } - - void startTransition(enum gles2_video_transition transition, - uint64_t duration, - bool hold); - - void abortTransition(void); - - int loadFrame(const uint8_t *framePlanes[3], - size_t framePlaneStride[3], - const struct vdef_raw_format *format, - const struct vdef_frame_info *info, - struct egl_display *eglDisplay = nullptr); - - int renderFrame(const struct pdraw_rect *renderPos, - struct pdraw_rect *contentPos, - Eigen::Matrix4f &viewProjMat, - size_t framePlaneStride[3], - const struct vdef_raw_format *format, - const struct vdef_frame_info *info, - const struct vdef_rect *crop, - struct vmeta_frame *metadata, - const struct pdraw_video_renderer_params *params); - - int clear(Eigen::Matrix4f &viewProjMat); - - void setExtTexture(GLuint texture); - - void getHistograms(float *histogram[PDRAW_HISTOGRAM_CHANNEL_MAX], - size_t histogramLen[PDRAW_HISTOGRAM_CHANNEL_MAX]); - -private: - enum program { - PROGRAM_NOCONV = 0, - PROGRAM_YUV_TO_RGB_PLANAR, - PROGRAM_YUV_TO_RGB_PLANAR_10_16LE, - PROGRAM_YUV_TO_RGB_SEMIPLANAR, - PROGRAM_YUV_TO_RGB_SEMIPLANAR_10_16LE_HIGH, - PROGRAM_MAX, - }; - - enum program getProgram(const struct vdef_raw_format *format); - - int setupBlur(void); - - void cleanupBlur(void); - - int setupBlurFbo(void); - - void cleanupBlurFbo(void); - - void renderBlur(size_t framePlaneStride[3], - const struct vdef_raw_format *format, - const struct vdef_frame_info *info, - const struct vdef_rect *crop, - const struct pdraw_rect *renderPos, - float videoW, - float videoH, - Eigen::Matrix4f &viewProjMat); - - int setupPaddingFbo(enum pdraw_video_renderer_fill_mode fillMode); - - void cleanupPaddingFbo(void); - - void renderPadding(size_t framePlaneStride[3], - const struct vdef_raw_format *format, - const struct vdef_frame_info *info, - const struct vdef_rect *crop, - const struct pdraw_rect *renderPos, - float videoW, - float videoH, - float videoW2, - float videoH2, - float videoAR, - float windowAR, - enum pdraw_video_renderer_fill_mode fillMode, - bool immersive, - Eigen::Matrix4f &viewProjMat); - - void setupZebra(enum program prog); - - void updateZebra(struct pdraw_rect *contentPos, - enum program prog, - bool enable, - float threshold); - - int setupHistograms(void); - - void cleanupHistograms(void); - - void computeHistograms(size_t framePlaneStride[3], - const struct vdef_raw_format *format, - const struct vdef_frame_info *info, - const struct vdef_rect *crop, - const struct pdraw_rect *renderPos, - bool enable); - - void updateTransition(void); - - Session *mSession; - unsigned int mVideoWidth; - unsigned int mVideoHeight; - unsigned int mFirstTexUnit; - GLuint mDefaultFbo; - enum gles2_video_transition mTransition; - uint64_t mTransitionStartTime; - uint64_t mTransitionDuration; - bool mTransitionHold; - GLint mProgram[PROGRAM_MAX]; - GLint mProgramTransformMatrix[PROGRAM_MAX]; - GLint mProgramYuv2RgbMatrix[PROGRAM_MAX]; - GLint mProgramYuv2RgbOffset[PROGRAM_MAX]; - GLint mProgramStride[PROGRAM_MAX]; - GLint mProgramMaxCoords[PROGRAM_MAX]; - GLint mProgramSatCoef[PROGRAM_MAX]; - GLint mProgramLightCoef[PROGRAM_MAX]; - GLint mProgramDarkCoef[PROGRAM_MAX]; - GLint mProgramZebraEnable[PROGRAM_MAX]; - GLint mProgramZebraThreshold[PROGRAM_MAX]; - GLint mProgramZebraPhase[PROGRAM_MAX]; - GLint mProgramZebraWeight[PROGRAM_MAX]; - GLuint mTextures[GLES2_VIDEO_TEX_UNIT_COUNT]; - GLuint mExtTexture; - GLint mUniformSamplers[PROGRAM_MAX][GLES2_VIDEO_TEX_UNIT_COUNT]; - GLint mPositionHandle[PROGRAM_MAX]; - GLint mTexcoordHandle[PROGRAM_MAX]; - bool mBlurInit; - bool mApplyBlur; - float mBlurWeights[GLES2_VIDEO_BLUR_TAP_COUNT]; - unsigned int mBlurFboWidth; - unsigned int mBlurFboHeight; - GLuint mBlurFbo[2]; - GLuint mBlurFboTexture[2]; - GLint mBlurProgram[2]; - GLint mBlurUniformPixelSize[2]; - GLint mBlurUniformWeights[2]; - GLint mBlurUniformSampler[2]; - GLint mBlurPositionHandle[2]; - unsigned int mPaddingPass1Width; - unsigned int mPaddingPass1Height; - unsigned int mPaddingPass2Width; - unsigned int mPaddingPass2Height; - float mPaddingBlurWeights[GLES2_VIDEO_BLUR_TAP_COUNT]; - GLuint mPaddingFbo[4]; - GLuint mPaddingFboTexture[4]; - bool mHistogramInit; - uint64_t mHistogramLastComputeTime; - GLint mHistogramProgram[PROGRAM_MAX]; - GLint mHistogramYuv2RgbMatrix[PROGRAM_MAX]; - GLint mHistogramYuv2RgbOffset[PROGRAM_MAX]; - GLint mHistogramUniformSampler[PROGRAM_MAX][GLES2_VIDEO_TEX_UNIT_COUNT]; - GLint mHistogramPositionHandle[PROGRAM_MAX]; - GLint mHistogramTexcoordHandle[PROGRAM_MAX]; - GLuint mHistogramFbo; - GLuint mHistogramFboTexture; - uint8_t *mHistogramBuffer; - bool mHistogramValid[PDRAW_HISTOGRAM_CHANNEL_MAX]; - uint32_t *mHistogram[PDRAW_HISTOGRAM_CHANNEL_MAX]; - float *mHistogramNorm[PDRAW_HISTOGRAM_CHANNEL_MAX]; - float mSatCoef; /* 0.0 (greyscale) .. 1.0 (original video) */ - float mBaseSatCoef; - float mLightCoef; /* 0.0 (white) .. 1.0 (video) */ - float mBaseLightCoef; - float mDarkCoef; /* 0.0 (black) .. 1.0 (video) */ - float mBaseDarkCoef; -# ifdef BCM_VIDEOCORE - EGLImageKHR mEglImage; -# endif /* BCM_VIDEOCORE */ - - static const GLchar *videoFragmentShaders[PROGRAM_MAX][3]; - static const GLchar *histogramFragmentShaders[PROGRAM_MAX][2]; -}; - -} /* namespace Pdraw */ - -#endif /* USE_GLES2 */ - -#endif /* !_PDRAW_GLES2_VIDEO_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * OpenGL ES 2.0 video rendering + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_GLES2_VIDEO_HPP_ +#define _PDRAW_GLES2_VIDEO_HPP_ + +#ifdef USE_GLES2 + +# include "pdraw_gles2_common.hpp" +# include "pdraw_utils.hpp" + +# ifdef BCM_VIDEOCORE +# include +# include +# endif /* BCM_VIDEOCORE */ + + +namespace Pdraw { + + +# define GLES2_VIDEO_TEX_UNIT_COUNT 3 +# define GLES2_VIDEO_FBO_TEX_UNIT_COUNT 1 +# define GLES2_VIDEO_BLUR_FBO_TARGET_SIZE 512 +# define GLES2_VIDEO_BLUR_TAP_COUNT 15 +# define GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_1 256 +# define GLES2_VIDEO_PADDING_FBO_TARGET_SIZE_2 16 +# define GLES2_VIDEO_HISTOGRAM_FBO_TARGET_SIZE 256 + + +enum gles2_video_transition { + GLES2_VIDEO_TRANSITION_NONE = 0, + GLES2_VIDEO_TRANSITION_FADE_TO_BLACK, + GLES2_VIDEO_TRANSITION_FADE_FROM_BLACK, + GLES2_VIDEO_TRANSITION_FADE_TO_WHITE, + GLES2_VIDEO_TRANSITION_FADE_FROM_WHITE, + GLES2_VIDEO_TRANSITION_FADE_TO_BLACK_AND_WHITE, + GLES2_VIDEO_TRANSITION_FADE_FROM_BLACK_AND_WHITE, + GLES2_VIDEO_TRANSITION_FADE_TO_BLUR, + GLES2_VIDEO_TRANSITION_FADE_FROM_BLUR, + GLES2_VIDEO_TRANSITION_FLASH, +}; + +class Session; + + +class Gles2Video { +public: + Gles2Video(Session *session, + GLuint defaultFbo, + unsigned int firstTexUnit); + + ~Gles2Video(void); + + static int getTexUnitCount(void) + { + return GLES2_VIDEO_TEX_UNIT_COUNT + + GLES2_VIDEO_FBO_TEX_UNIT_COUNT; + } + + GLuint getDefaultFbo(void) + { + return mDefaultFbo; + } + + void setDefaultFbo(GLuint defaultFbo) + { + mDefaultFbo = defaultFbo; + } + + float getSatCoef(void) + { + return mBaseSatCoef; + } + + void setSatCoef(float coef) + { + mBaseSatCoef = coef < 0. ? 0. : (coef > 1. ? 1. : coef); + } + + float getLightCoef(void) + { + return mBaseLightCoef; + } + + void setLightCoef(float coef) + { + mBaseLightCoef = coef < 0. ? 0. : (coef > 1. ? 1. : coef); + } + + float getDarkCoef(void) + { + return mBaseDarkCoef; + } + + void setDarkCoef(float coef) + { + mBaseDarkCoef = coef < 0. ? 0. : (coef > 1. ? 1. : coef); + } + + void startTransition(enum gles2_video_transition transition, + uint64_t duration, + bool hold); + + void abortTransition(void); + + int loadFrame(const uint8_t *framePlanes[3], + size_t framePlaneStride[3], + const struct vdef_raw_format *format, + const struct vdef_frame_info *info, + struct egl_display *eglDisplay = nullptr); + + int renderFrame(const struct pdraw_rect *renderPos, + struct pdraw_rect *contentPos, + Eigen::Matrix4f &viewProjMat, + size_t framePlaneStride[3], + const struct vdef_raw_format *format, + const struct vdef_frame_info *info, + const struct vdef_rect *crop, + struct vmeta_frame *metadata, + const struct pdraw_video_renderer_params *params); + + int clear(Eigen::Matrix4f &viewProjMat); + + void setExtTexture(GLuint texture); + + void getHistograms(float *histogram[PDRAW_HISTOGRAM_CHANNEL_MAX], + size_t histogramLen[PDRAW_HISTOGRAM_CHANNEL_MAX]); + +private: + enum program { + PROGRAM_NOCONV = 0, + PROGRAM_YUV_TO_RGB_PLANAR, + PROGRAM_YUV_TO_RGB_PLANAR_10_16LE, + PROGRAM_YUV_TO_RGB_SEMIPLANAR, + PROGRAM_YUV_TO_RGB_SEMIPLANAR_10_16LE_HIGH, + PROGRAM_MAX, + }; + + enum program getProgram(const struct vdef_raw_format *format); + + int setupBlur(void); + + void cleanupBlur(void); + + int setupBlurFbo(void); + + void cleanupBlurFbo(void); + + void renderBlur(size_t framePlaneStride[3], + const struct vdef_raw_format *format, + const struct vdef_frame_info *info, + const struct vdef_rect *crop, + const struct pdraw_rect *renderPos, + float videoW, + float videoH, + Eigen::Matrix4f &viewProjMat); + + int setupPaddingFbo(enum pdraw_video_renderer_fill_mode fillMode); + + void cleanupPaddingFbo(void); + + void renderPadding(size_t framePlaneStride[3], + const struct vdef_raw_format *format, + const struct vdef_frame_info *info, + const struct vdef_rect *crop, + const struct pdraw_rect *renderPos, + float videoW, + float videoH, + float videoW2, + float videoH2, + float videoAR, + float windowAR, + enum pdraw_video_renderer_fill_mode fillMode, + bool immersive, + Eigen::Matrix4f &viewProjMat); + + void setupZebra(enum program prog); + + void updateZebra(struct pdraw_rect *contentPos, + enum program prog, + bool enable, + float threshold); + + int setupHistograms(void); + + void cleanupHistograms(void); + + void computeHistograms(size_t framePlaneStride[3], + const struct vdef_raw_format *format, + const struct vdef_frame_info *info, + const struct vdef_rect *crop, + const struct pdraw_rect *renderPos, + bool enable); + + void updateTransition(void); + + Session *mSession; + unsigned int mVideoWidth; + unsigned int mVideoHeight; + unsigned int mFirstTexUnit; + GLuint mDefaultFbo; + enum gles2_video_transition mTransition; + uint64_t mTransitionStartTime; + uint64_t mTransitionDuration; + bool mTransitionHold; + GLint mProgram[PROGRAM_MAX]; + GLint mProgramTransformMatrix[PROGRAM_MAX]; + GLint mProgramYuv2RgbMatrix[PROGRAM_MAX]; + GLint mProgramYuv2RgbOffset[PROGRAM_MAX]; + GLint mProgramStride[PROGRAM_MAX]; + GLint mProgramMaxCoords[PROGRAM_MAX]; + GLint mProgramSatCoef[PROGRAM_MAX]; + GLint mProgramLightCoef[PROGRAM_MAX]; + GLint mProgramDarkCoef[PROGRAM_MAX]; + GLint mProgramZebraEnable[PROGRAM_MAX]; + GLint mProgramZebraThreshold[PROGRAM_MAX]; + GLint mProgramZebraPhase[PROGRAM_MAX]; + GLint mProgramZebraWeight[PROGRAM_MAX]; + GLuint mTextures[GLES2_VIDEO_TEX_UNIT_COUNT]; + GLuint mExtTexture; + GLint mUniformSamplers[PROGRAM_MAX][GLES2_VIDEO_TEX_UNIT_COUNT]; + GLint mPositionHandle[PROGRAM_MAX]; + GLint mTexcoordHandle[PROGRAM_MAX]; + bool mBlurInit; + bool mApplyBlur; + float mBlurWeights[GLES2_VIDEO_BLUR_TAP_COUNT]; + unsigned int mBlurFboWidth; + unsigned int mBlurFboHeight; + GLuint mBlurFbo[2]; + GLuint mBlurFboTexture[2]; + GLint mBlurProgram[2]; + GLint mBlurUniformPixelSize[2]; + GLint mBlurUniformWeights[2]; + GLint mBlurUniformSampler[2]; + GLint mBlurPositionHandle[2]; + unsigned int mPaddingPass1Width; + unsigned int mPaddingPass1Height; + unsigned int mPaddingPass2Width; + unsigned int mPaddingPass2Height; + float mPaddingBlurWeights[GLES2_VIDEO_BLUR_TAP_COUNT]; + GLuint mPaddingFbo[4]; + GLuint mPaddingFboTexture[4]; + bool mHistogramInit; + uint64_t mHistogramLastComputeTime; + GLint mHistogramProgram[PROGRAM_MAX]; + GLint mHistogramYuv2RgbMatrix[PROGRAM_MAX]; + GLint mHistogramYuv2RgbOffset[PROGRAM_MAX]; + GLint mHistogramUniformSampler[PROGRAM_MAX][GLES2_VIDEO_TEX_UNIT_COUNT]; + GLint mHistogramPositionHandle[PROGRAM_MAX]; + GLint mHistogramTexcoordHandle[PROGRAM_MAX]; + GLuint mHistogramFbo; + GLuint mHistogramFboTexture; + uint8_t *mHistogramBuffer; + bool mHistogramValid[PDRAW_HISTOGRAM_CHANNEL_MAX]; + uint32_t *mHistogram[PDRAW_HISTOGRAM_CHANNEL_MAX]; + float *mHistogramNorm[PDRAW_HISTOGRAM_CHANNEL_MAX]; + float mSatCoef; /* 0.0 (greyscale) .. 1.0 (original video) */ + float mBaseSatCoef; + float mLightCoef; /* 0.0 (white) .. 1.0 (video) */ + float mBaseLightCoef; + float mDarkCoef; /* 0.0 (black) .. 1.0 (video) */ + float mBaseDarkCoef; +# ifdef BCM_VIDEOCORE + EGLImageKHR mEglImage; +# endif /* BCM_VIDEOCORE */ + + static const GLchar *videoFragmentShaders[PROGRAM_MAX][3]; + static const GLchar *histogramFragmentShaders[PROGRAM_MAX][2]; +}; + +} /* namespace Pdraw */ + +#endif /* USE_GLES2 */ + +#endif /* !_PDRAW_GLES2_VIDEO_HPP_ */ diff --git a/libpdraw/src/pdraw_media.cpp b/libpdraw/src/pdraw_media.cpp index 4adc7fe..893010c 100644 --- a/libpdraw/src/pdraw_media.cpp +++ b/libpdraw/src/pdraw_media.cpp @@ -1,394 +1,394 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Pipeline media - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_media -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_media.hpp" - -#include - -#include -#include - -namespace Pdraw { - - -std::atomic Media::mIdCounter(0); - - -Media::Media(Session *session, Type t) : - type(t), sessionMeta({}), - playbackType(PDRAW_PLAYBACK_TYPE_UNKNOWN), duration(0), - mSession(session) - -{ - id = ++mIdCounter; - mName = std::string(__func__) + "#" + std::to_string(id); -} - - -std::string &Media::getName(void) -{ - return mName; -} - - -void Media::setClassName(std::string &name) -{ - mName = name + "#" + std::to_string(id); -} - - -void Media::setClassName(const char *name) -{ - mName = std::string(name) + "#" + std::to_string(id); -} - - -std::string &Media::getPath(void) -{ - return mPath; -} - - -void Media::setPath(std::string &path) -{ - mPath = path; -} - - -void Media::setPath(const char *path) -{ - mPath = path; -} - - -const char *Media::getMediaTypeStr(Type val) -{ - switch (val) { - case UNKNOWN: - return "UNKNOWN"; - case RAW_VIDEO: - return "RAW_VIDEO"; - case CODED_VIDEO: - return "CODED_VIDEO"; - case RAW_AUDIO: - return "RAW_AUDIO"; - case CODED_AUDIO: - return "CODED_AUDIO"; - default: - return nullptr; - } -} - - -void Media::cleanupMediaInfo(struct pdraw_media_info *minfo) -{ - free((void *)minfo->name); - minfo->name = nullptr; - free((void *)minfo->path); - minfo->path = nullptr; -} - - -RawVideoMedia::RawVideoMedia(Session *session) : Media(session, RAW_VIDEO) -{ - Media::setClassName(__func__); - format = {}; - info = {}; -} - - -RawVideoMedia::~RawVideoMedia(void) -{ - return; -} - - -void RawVideoMedia::fillMediaInfo(struct pdraw_media_info *minfo) -{ - if (!minfo) - return; - - *minfo = {}; - - minfo->type = PDRAW_MEDIA_TYPE_VIDEO; - minfo->id = id; - minfo->name = strdup(getName().c_str()); - minfo->path = strdup(getPath().c_str()); - minfo->playback_type = playbackType; - minfo->duration = duration; - minfo->session_meta = &sessionMeta; - minfo->video.format = VDEF_FRAME_TYPE_RAW; - minfo->video.type = PDRAW_VIDEO_TYPE_DEFAULT_CAMERA; - minfo->video.raw.format = format; - minfo->video.raw.info = info; -} - - -CodedVideoMedia::CodedVideoMedia(Session *session) : Media(session, CODED_VIDEO) -{ - Media::setClassName(__func__); - mVps = nullptr; - mVpsSize = 0; - mSps = nullptr; - mSpsSize = 0; - mPps = nullptr; - mPpsSize = 0; - - format = {}; - info = {}; -} - - -CodedVideoMedia::~CodedVideoMedia(void) -{ - free(mVps); - free(mSps); - free(mPps); -} - - -int CodedVideoMedia::getPs(const uint8_t **vps, - size_t *vpsSize, - const uint8_t **sps, - size_t *spsSize, - const uint8_t **pps, - size_t *ppsSize) -{ - enum vdef_encoding encoding = format.encoding; - if ((encoding != VDEF_ENCODING_H264) && - (encoding != VDEF_ENCODING_H265)) - return -EPROTO; - - if (encoding == VDEF_ENCODING_H265) { - /* VPS is for H.265 only */ - if (vps) - *vps = mVps; - if (vpsSize) - *vpsSize = mVpsSize; - } - if (sps) - *sps = mSps; - if (spsSize) - *spsSize = mSpsSize; - if (pps) - *pps = mPps; - if (ppsSize) - *ppsSize = mPpsSize; - - return 0; -} - - -int CodedVideoMedia::setPs(const uint8_t *vps, - size_t vpsSize, - const uint8_t *sps, - size_t spsSize, - const uint8_t *pps, - size_t ppsSize) -{ - int ret; - struct h264_info h264Info; - struct h265_info h265Info; - enum vdef_encoding encoding = format.encoding; - - if ((encoding != VDEF_ENCODING_H264) && - (encoding != VDEF_ENCODING_H265)) - return -EPROTO; - if ((encoding == VDEF_ENCODING_H265) && - ((vps == nullptr) || (vpsSize == 0))) - return -EINVAL; - if ((sps == nullptr) || (spsSize == 0)) - return -EINVAL; - if ((pps == nullptr) || (ppsSize == 0)) - return -EINVAL; - - free(mVps); - mVps = nullptr; - mVpsSize = 0; - if (encoding == VDEF_ENCODING_H265) { - mVps = (uint8_t *)malloc(vpsSize); - if (mVps == nullptr) { - ret = -ENOMEM; - ULOG_ERRNO("malloc", -ret); - goto error; - } - mVpsSize = vpsSize; - memcpy(mVps, vps, mVpsSize); - } - - free(mSps); - mSpsSize = 0; - mSps = (uint8_t *)malloc(spsSize); - if (mSps == nullptr) { - ret = -ENOMEM; - ULOG_ERRNO("malloc", -ret); - goto error; - } - mSpsSize = spsSize; - memcpy(mSps, sps, mSpsSize); - - free(mPps); - mPpsSize = 0; - mPps = (uint8_t *)malloc(ppsSize); - if (mPps == nullptr) { - ret = -ENOMEM; - ULOG_ERRNO("malloc", -ret); - goto error; - } - mPpsSize = ppsSize; - memcpy(mPps, pps, mPpsSize); - - switch (encoding) { - case VDEF_ENCODING_H264: - ret = h264_get_info(sps, spsSize, pps, ppsSize, &h264Info); - if (ret < 0) { - ULOG_ERRNO("h264_get_info", -ret); - goto error; - } - - info.bit_depth = h264Info.bit_depth_luma; - info.full_range = h264Info.full_range; - info.color_primaries = vdef_color_primaries_from_h264( - h264Info.colour_primaries); - info.transfer_function = vdef_transfer_function_from_h264( - h264Info.transfer_characteristics); - info.matrix_coefs = vdef_matrix_coefs_from_h264( - h264Info.matrix_coefficients); - info.resolution.width = h264Info.crop_width; - info.resolution.height = h264Info.crop_height; - info.sar.width = h264Info.sar_width; - info.sar.height = h264Info.sar_height; - info.framerate.num = h264Info.framerate_num; - info.framerate.den = h264Info.framerate_den; - break; - - case VDEF_ENCODING_H265: - ret = h265_get_info( - vps, vpsSize, sps, spsSize, pps, ppsSize, &h265Info); - if (ret < 0) { - ULOG_ERRNO("h265_get_info", -ret); - goto error; - } - - info.bit_depth = h265Info.bit_depth_luma; - info.full_range = h265Info.full_range; - info.color_primaries = vdef_color_primaries_from_h265( - h265Info.colour_primaries); - info.transfer_function = vdef_transfer_function_from_h265( - h265Info.transfer_characteristics); - info.matrix_coefs = vdef_matrix_coefs_from_h265( - h265Info.matrix_coefficients); - info.resolution.width = h265Info.crop_width; - info.resolution.height = h265Info.crop_height; - info.sar.width = h265Info.sar_width; - info.sar.height = h265Info.sar_height; - info.framerate.num = h265Info.framerate_num; - info.framerate.den = h265Info.framerate_den; - break; - default: - break; - } - - return 0; - -error: - free(mVps); - free(mSps); - free(mPps); - mVps = nullptr; - mSps = nullptr; - mPps = nullptr; - mVpsSize = 0; - mSpsSize = 0; - mPpsSize = 0; - return ret; -} - - -void CodedVideoMedia::fillMediaInfo(struct pdraw_media_info *minfo) -{ - size_t cplen; - - if (!minfo) - return; - - *minfo = {}; - - minfo->type = PDRAW_MEDIA_TYPE_VIDEO; - minfo->id = id; - minfo->name = strdup(getName().c_str()); - minfo->path = strdup(getPath().c_str()); - minfo->playback_type = playbackType; - minfo->duration = duration; - minfo->session_meta = &sessionMeta; - minfo->video.format = VDEF_FRAME_TYPE_CODED; - minfo->video.type = PDRAW_VIDEO_TYPE_DEFAULT_CAMERA; - minfo->video.coded.format = format; - minfo->video.coded.info = info; - switch (format.encoding) { - case VDEF_ENCODING_H264: - if (sizeof(minfo->video.coded.h264.sps) < mSpsSize) - ULOGW("%s: truncated SPS", __func__); - cplen = std::min(mSpsSize, sizeof(minfo->video.coded.h264.sps)); - memcpy(minfo->video.coded.h264.sps, mSps, cplen); - minfo->video.coded.h264.spslen = cplen; - if (sizeof(minfo->video.coded.h264.pps) < mPpsSize) - ULOGW("%s: truncated PPS", __func__); - cplen = std::min(mPpsSize, sizeof(minfo->video.coded.h264.pps)); - memcpy(minfo->video.coded.h264.pps, mPps, cplen); - minfo->video.coded.h264.ppslen = cplen; - break; - case VDEF_ENCODING_H265: - if (sizeof(minfo->video.coded.h265.vps) < mVpsSize) - ULOGW("%s: truncated VPS", __func__); - cplen = std::min(mVpsSize, sizeof(minfo->video.coded.h265.vps)); - memcpy(minfo->video.coded.h265.vps, mVps, cplen); - minfo->video.coded.h265.vpslen = cplen; - if (sizeof(minfo->video.coded.h265.sps) < mSpsSize) - ULOGW("%s: truncated SPS", __func__); - cplen = std::min(mSpsSize, sizeof(minfo->video.coded.h265.sps)); - memcpy(minfo->video.coded.h265.sps, mSps, cplen); - minfo->video.coded.h265.spslen = cplen; - if (sizeof(minfo->video.coded.h265.pps) < mPpsSize) - ULOGW("%s: truncated PPS", __func__); - cplen = std::min(mPpsSize, sizeof(minfo->video.coded.h265.pps)); - memcpy(minfo->video.coded.h265.pps, mPps, cplen); - minfo->video.coded.h265.ppslen = cplen; - break; - default: - break; - } -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Pipeline media + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_media +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_media.hpp" + +#include + +#include +#include + +namespace Pdraw { + + +std::atomic Media::mIdCounter(0); + + +Media::Media(Session *session, Type t) : + type(t), sessionMeta({}), + playbackType(PDRAW_PLAYBACK_TYPE_UNKNOWN), duration(0), + mSession(session) + +{ + id = ++mIdCounter; + mName = std::string(__func__) + "#" + std::to_string(id); +} + + +std::string &Media::getName(void) +{ + return mName; +} + + +void Media::setClassName(std::string &name) +{ + mName = name + "#" + std::to_string(id); +} + + +void Media::setClassName(const char *name) +{ + mName = std::string(name) + "#" + std::to_string(id); +} + + +std::string &Media::getPath(void) +{ + return mPath; +} + + +void Media::setPath(std::string &path) +{ + mPath = path; +} + + +void Media::setPath(const char *path) +{ + mPath = path; +} + + +const char *Media::getMediaTypeStr(Type val) +{ + switch (val) { + case UNKNOWN: + return "UNKNOWN"; + case RAW_VIDEO: + return "RAW_VIDEO"; + case CODED_VIDEO: + return "CODED_VIDEO"; + case RAW_AUDIO: + return "RAW_AUDIO"; + case CODED_AUDIO: + return "CODED_AUDIO"; + default: + return nullptr; + } +} + + +void Media::cleanupMediaInfo(struct pdraw_media_info *minfo) +{ + free((void *)minfo->name); + minfo->name = nullptr; + free((void *)minfo->path); + minfo->path = nullptr; +} + + +RawVideoMedia::RawVideoMedia(Session *session) : Media(session, RAW_VIDEO) +{ + Media::setClassName(__func__); + format = {}; + info = {}; +} + + +RawVideoMedia::~RawVideoMedia(void) +{ + return; +} + + +void RawVideoMedia::fillMediaInfo(struct pdraw_media_info *minfo) +{ + if (!minfo) + return; + + *minfo = {}; + + minfo->type = PDRAW_MEDIA_TYPE_VIDEO; + minfo->id = id; + minfo->name = strdup(getName().c_str()); + minfo->path = strdup(getPath().c_str()); + minfo->playback_type = playbackType; + minfo->duration = duration; + minfo->session_meta = &sessionMeta; + minfo->video.format = VDEF_FRAME_TYPE_RAW; + minfo->video.type = PDRAW_VIDEO_TYPE_DEFAULT_CAMERA; + minfo->video.raw.format = format; + minfo->video.raw.info = info; +} + + +CodedVideoMedia::CodedVideoMedia(Session *session) : Media(session, CODED_VIDEO) +{ + Media::setClassName(__func__); + mVps = nullptr; + mVpsSize = 0; + mSps = nullptr; + mSpsSize = 0; + mPps = nullptr; + mPpsSize = 0; + + format = {}; + info = {}; +} + + +CodedVideoMedia::~CodedVideoMedia(void) +{ + free(mVps); + free(mSps); + free(mPps); +} + + +int CodedVideoMedia::getPs(const uint8_t **vps, + size_t *vpsSize, + const uint8_t **sps, + size_t *spsSize, + const uint8_t **pps, + size_t *ppsSize) +{ + enum vdef_encoding encoding = format.encoding; + if ((encoding != VDEF_ENCODING_H264) && + (encoding != VDEF_ENCODING_H265)) + return -EPROTO; + + if (encoding == VDEF_ENCODING_H265) { + /* VPS is for H.265 only */ + if (vps) + *vps = mVps; + if (vpsSize) + *vpsSize = mVpsSize; + } + if (sps) + *sps = mSps; + if (spsSize) + *spsSize = mSpsSize; + if (pps) + *pps = mPps; + if (ppsSize) + *ppsSize = mPpsSize; + + return 0; +} + + +int CodedVideoMedia::setPs(const uint8_t *vps, + size_t vpsSize, + const uint8_t *sps, + size_t spsSize, + const uint8_t *pps, + size_t ppsSize) +{ + int ret; + struct h264_info h264Info; + struct h265_info h265Info; + enum vdef_encoding encoding = format.encoding; + + if ((encoding != VDEF_ENCODING_H264) && + (encoding != VDEF_ENCODING_H265)) + return -EPROTO; + if ((encoding == VDEF_ENCODING_H265) && + ((vps == nullptr) || (vpsSize == 0))) + return -EINVAL; + if ((sps == nullptr) || (spsSize == 0)) + return -EINVAL; + if ((pps == nullptr) || (ppsSize == 0)) + return -EINVAL; + + free(mVps); + mVps = nullptr; + mVpsSize = 0; + if (encoding == VDEF_ENCODING_H265) { + mVps = (uint8_t *)malloc(vpsSize); + if (mVps == nullptr) { + ret = -ENOMEM; + ULOG_ERRNO("malloc", -ret); + goto error; + } + mVpsSize = vpsSize; + memcpy(mVps, vps, mVpsSize); + } + + free(mSps); + mSpsSize = 0; + mSps = (uint8_t *)malloc(spsSize); + if (mSps == nullptr) { + ret = -ENOMEM; + ULOG_ERRNO("malloc", -ret); + goto error; + } + mSpsSize = spsSize; + memcpy(mSps, sps, mSpsSize); + + free(mPps); + mPpsSize = 0; + mPps = (uint8_t *)malloc(ppsSize); + if (mPps == nullptr) { + ret = -ENOMEM; + ULOG_ERRNO("malloc", -ret); + goto error; + } + mPpsSize = ppsSize; + memcpy(mPps, pps, mPpsSize); + + switch (encoding) { + case VDEF_ENCODING_H264: + ret = h264_get_info(sps, spsSize, pps, ppsSize, &h264Info); + if (ret < 0) { + ULOG_ERRNO("h264_get_info", -ret); + goto error; + } + + info.bit_depth = h264Info.bit_depth_luma; + info.full_range = h264Info.full_range; + info.color_primaries = vdef_color_primaries_from_h264( + h264Info.colour_primaries); + info.transfer_function = vdef_transfer_function_from_h264( + h264Info.transfer_characteristics); + info.matrix_coefs = vdef_matrix_coefs_from_h264( + h264Info.matrix_coefficients); + info.resolution.width = h264Info.crop_width; + info.resolution.height = h264Info.crop_height; + info.sar.width = h264Info.sar_width; + info.sar.height = h264Info.sar_height; + info.framerate.num = h264Info.framerate_num; + info.framerate.den = h264Info.framerate_den; + break; + + case VDEF_ENCODING_H265: + ret = h265_get_info( + vps, vpsSize, sps, spsSize, pps, ppsSize, &h265Info); + if (ret < 0) { + ULOG_ERRNO("h265_get_info", -ret); + goto error; + } + + info.bit_depth = h265Info.bit_depth_luma; + info.full_range = h265Info.full_range; + info.color_primaries = vdef_color_primaries_from_h265( + h265Info.colour_primaries); + info.transfer_function = vdef_transfer_function_from_h265( + h265Info.transfer_characteristics); + info.matrix_coefs = vdef_matrix_coefs_from_h265( + h265Info.matrix_coefficients); + info.resolution.width = h265Info.crop_width; + info.resolution.height = h265Info.crop_height; + info.sar.width = h265Info.sar_width; + info.sar.height = h265Info.sar_height; + info.framerate.num = h265Info.framerate_num; + info.framerate.den = h265Info.framerate_den; + break; + default: + break; + } + + return 0; + +error: + free(mVps); + free(mSps); + free(mPps); + mVps = nullptr; + mSps = nullptr; + mPps = nullptr; + mVpsSize = 0; + mSpsSize = 0; + mPpsSize = 0; + return ret; +} + + +void CodedVideoMedia::fillMediaInfo(struct pdraw_media_info *minfo) +{ + size_t cplen; + + if (!minfo) + return; + + *minfo = {}; + + minfo->type = PDRAW_MEDIA_TYPE_VIDEO; + minfo->id = id; + minfo->name = strdup(getName().c_str()); + minfo->path = strdup(getPath().c_str()); + minfo->playback_type = playbackType; + minfo->duration = duration; + minfo->session_meta = &sessionMeta; + minfo->video.format = VDEF_FRAME_TYPE_CODED; + minfo->video.type = PDRAW_VIDEO_TYPE_DEFAULT_CAMERA; + minfo->video.coded.format = format; + minfo->video.coded.info = info; + switch (format.encoding) { + case VDEF_ENCODING_H264: + if (sizeof(minfo->video.coded.h264.sps) < mSpsSize) + ULOGW("%s: truncated SPS", __func__); + cplen = std::min(mSpsSize, sizeof(minfo->video.coded.h264.sps)); + memcpy(minfo->video.coded.h264.sps, mSps, cplen); + minfo->video.coded.h264.spslen = cplen; + if (sizeof(minfo->video.coded.h264.pps) < mPpsSize) + ULOGW("%s: truncated PPS", __func__); + cplen = std::min(mPpsSize, sizeof(minfo->video.coded.h264.pps)); + memcpy(minfo->video.coded.h264.pps, mPps, cplen); + minfo->video.coded.h264.ppslen = cplen; + break; + case VDEF_ENCODING_H265: + if (sizeof(minfo->video.coded.h265.vps) < mVpsSize) + ULOGW("%s: truncated VPS", __func__); + cplen = std::min(mVpsSize, sizeof(minfo->video.coded.h265.vps)); + memcpy(minfo->video.coded.h265.vps, mVps, cplen); + minfo->video.coded.h265.vpslen = cplen; + if (sizeof(minfo->video.coded.h265.sps) < mSpsSize) + ULOGW("%s: truncated SPS", __func__); + cplen = std::min(mSpsSize, sizeof(minfo->video.coded.h265.sps)); + memcpy(minfo->video.coded.h265.sps, mSps, cplen); + minfo->video.coded.h265.spslen = cplen; + if (sizeof(minfo->video.coded.h265.pps) < mPpsSize) + ULOGW("%s: truncated PPS", __func__); + cplen = std::min(mPpsSize, sizeof(minfo->video.coded.h265.pps)); + memcpy(minfo->video.coded.h265.pps, mPps, cplen); + minfo->video.coded.h265.ppslen = cplen; + break; + default: + break; + } +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_media.hpp b/libpdraw/src/pdraw_media.hpp index 805a000..be472a3 100644 --- a/libpdraw/src/pdraw_media.hpp +++ b/libpdraw/src/pdraw_media.hpp @@ -1,189 +1,189 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Pipeline media - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_MEDIA_HPP_ -#define _PDRAW_MEDIA_HPP_ - -#include -#include - -#include -#include - -#include - -/** - * mbuf ancillary data key for CodedVideoMedia::Frame objects - */ -#define PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME "pdraw.coded_video_media.frame" - -/** - * mbuf ancillary data key for RawVideoMedia::Frame objects - */ -#define PDRAW_ANCILLARY_DATA_KEY_RAWVIDEOFRAME "pdraw.raw_video_media.frame" - -namespace Pdraw { - -class Session; - -class Media { -public: - enum Type { - UNKNOWN = 0, - RAW_VIDEO = (1 << 0), - CODED_VIDEO = (1 << 1), - RAW_AUDIO = (1 << 2), - CODED_AUDIO = (1 << 3), - }; - - Media(Session *session, Type t); - - virtual ~Media(void) {} - - std::string &getName(void); - - std::string &getPath(void); - - void setPath(std::string &name); - - void setPath(const char *name); - - static const char *getMediaTypeStr(Type val); - - virtual void fillMediaInfo(struct pdraw_media_info *minfo) = 0; - - static void cleanupMediaInfo(struct pdraw_media_info *minfo); - - Type type; - unsigned int id; - struct vmeta_session sessionMeta; - enum pdraw_playback_type playbackType; - uint64_t duration; - -protected: - void setClassName(std::string &name); - - void setClassName(const char *name); - -private: - Session *mSession; - std::string mName; - std::string mPath; - static std::atomic mIdCounter; -}; - - -class RawVideoMedia : public Media { -public: - struct Frame { - uint64_t ntpTimestamp; - uint64_t ntpUnskewedTimestamp; - uint64_t ntpRawTimestamp; - uint64_t ntpRawUnskewedTimestamp; - uint64_t playTimestamp; - uint64_t captureTimestamp; - uint64_t localTimestamp; - uint32_t localTimestampPrecision; - uint64_t recvStartTimestamp; - uint64_t recvEndTimestamp; - uint64_t demuxOutputTimestamp; - uint64_t decoderOutputTimestamp; - uint64_t scalerOutputTimestamp; - uint64_t renderTimestamp; - }; - - RawVideoMedia(Session *session); - - ~RawVideoMedia(void); - - virtual void fillMediaInfo(struct pdraw_media_info *minfo); - - struct vdef_raw_format format; - struct vdef_format_info info; -}; - - -class CodedVideoMedia : public Media { -public: - struct CodedFrame { - }; - - struct Frame { - bool isSync; - bool isRef; - uint64_t ntpTimestamp; - uint64_t ntpUnskewedTimestamp; - uint64_t ntpRawTimestamp; - uint64_t ntpRawUnskewedTimestamp; - uint64_t playTimestamp; - uint64_t captureTimestamp; - uint64_t localTimestamp; - uint32_t localTimestampPrecision; - uint64_t recvStartTimestamp; - uint64_t recvEndTimestamp; - uint64_t demuxOutputTimestamp; - uint64_t encoderOutputTimestamp; - }; - - CodedVideoMedia(Session *session); - - ~CodedVideoMedia(void); - - int getPs(const uint8_t **vps, - size_t *vpsSize, - const uint8_t **sps, - size_t *spsSize, - const uint8_t **pps, - size_t *ppsSize); - - int setPs(const uint8_t *vps, - size_t vpsSize, - const uint8_t *sps, - size_t spsSize, - const uint8_t *pps, - size_t ppsSize); - - virtual void fillMediaInfo(struct pdraw_media_info *minfo); - - struct vdef_coded_format format; - struct vdef_format_info info; - -private: - uint8_t *mVps; - size_t mVpsSize; - uint8_t *mSps; - size_t mSpsSize; - uint8_t *mPps; - size_t mPpsSize; -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_MEDIA_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Pipeline media + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_MEDIA_HPP_ +#define _PDRAW_MEDIA_HPP_ + +#include +#include + +#include +#include + +#include + +/** + * mbuf ancillary data key for CodedVideoMedia::Frame objects + */ +#define PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME "pdraw.coded_video_media.frame" + +/** + * mbuf ancillary data key for RawVideoMedia::Frame objects + */ +#define PDRAW_ANCILLARY_DATA_KEY_RAWVIDEOFRAME "pdraw.raw_video_media.frame" + +namespace Pdraw { + +class Session; + +class Media { +public: + enum Type { + UNKNOWN = 0, + RAW_VIDEO = (1 << 0), + CODED_VIDEO = (1 << 1), + RAW_AUDIO = (1 << 2), + CODED_AUDIO = (1 << 3), + }; + + Media(Session *session, Type t); + + virtual ~Media(void) {} + + std::string &getName(void); + + std::string &getPath(void); + + void setPath(std::string &name); + + void setPath(const char *name); + + static const char *getMediaTypeStr(Type val); + + virtual void fillMediaInfo(struct pdraw_media_info *minfo) = 0; + + static void cleanupMediaInfo(struct pdraw_media_info *minfo); + + Type type; + unsigned int id; + struct vmeta_session sessionMeta; + enum pdraw_playback_type playbackType; + uint64_t duration; + +protected: + void setClassName(std::string &name); + + void setClassName(const char *name); + +private: + Session *mSession; + std::string mName; + std::string mPath; + static std::atomic mIdCounter; +}; + + +class RawVideoMedia : public Media { +public: + struct Frame { + uint64_t ntpTimestamp; + uint64_t ntpUnskewedTimestamp; + uint64_t ntpRawTimestamp; + uint64_t ntpRawUnskewedTimestamp; + uint64_t playTimestamp; + uint64_t captureTimestamp; + uint64_t localTimestamp; + uint32_t localTimestampPrecision; + uint64_t recvStartTimestamp; + uint64_t recvEndTimestamp; + uint64_t demuxOutputTimestamp; + uint64_t decoderOutputTimestamp; + uint64_t scalerOutputTimestamp; + uint64_t renderTimestamp; + }; + + RawVideoMedia(Session *session); + + ~RawVideoMedia(void); + + virtual void fillMediaInfo(struct pdraw_media_info *minfo); + + struct vdef_raw_format format; + struct vdef_format_info info; +}; + + +class CodedVideoMedia : public Media { +public: + struct CodedFrame { + }; + + struct Frame { + bool isSync; + bool isRef; + uint64_t ntpTimestamp; + uint64_t ntpUnskewedTimestamp; + uint64_t ntpRawTimestamp; + uint64_t ntpRawUnskewedTimestamp; + uint64_t playTimestamp; + uint64_t captureTimestamp; + uint64_t localTimestamp; + uint32_t localTimestampPrecision; + uint64_t recvStartTimestamp; + uint64_t recvEndTimestamp; + uint64_t demuxOutputTimestamp; + uint64_t encoderOutputTimestamp; + }; + + CodedVideoMedia(Session *session); + + ~CodedVideoMedia(void); + + int getPs(const uint8_t **vps, + size_t *vpsSize, + const uint8_t **sps, + size_t *spsSize, + const uint8_t **pps, + size_t *ppsSize); + + int setPs(const uint8_t *vps, + size_t vpsSize, + const uint8_t *sps, + size_t spsSize, + const uint8_t *pps, + size_t ppsSize); + + virtual void fillMediaInfo(struct pdraw_media_info *minfo); + + struct vdef_coded_format format; + struct vdef_format_info info; + +private: + uint8_t *mVps; + size_t mVpsSize; + uint8_t *mSps; + size_t mSpsSize; + uint8_t *mPps; + size_t mPpsSize; +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_MEDIA_HPP_ */ diff --git a/libpdraw/src/pdraw_muxer.cpp b/libpdraw/src/pdraw_muxer.cpp index 1f6f944..5341eb4 100644 --- a/libpdraw/src/pdraw_muxer.cpp +++ b/libpdraw/src/pdraw_muxer.cpp @@ -1,321 +1,321 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Generic muxer - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_muxer -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_muxer.hpp" -#include "pdraw_session.hpp" - -#include - -#include -#include - -namespace Pdraw { - - -Muxer::Muxer(Session *session, Element::Listener *elementListener) : - CodedSinkElement(session, elementListener, UINT_MAX, nullptr, 0) -{ - Element::setClassName(__func__); - - setState(CREATED); -} - - -Muxer::~Muxer(void) -{ - int res; - - if ((mState == STARTED) || (mState == STARTING)) - PDRAW_LOGW("%s: still running (%s)", - __func__, - Element::getElementStateStr(mState)); - - unsigned int count = getInputMediaCount(); - if (count > 0) { - PDRAW_LOGW("%s: not all input media have been removed", - __func__); - res = removeInputMedias(); - } -} - - -int Muxer::start(void) -{ - int res, err; - - if ((mState == STARTED) || (mState == STARTING)) { - return 0; - } - if (mState != CREATED) { - PDRAW_LOGE("%s: invalid state (%s)", - __func__, - Element::getElementStateStr(mState)); - return -EPROTO; - } - setState(STARTING); - - res = internalStart(); - if (res < 0) - goto error; - - setState(STARTED); - - return 0; - -error: - err = stop(); - return res; -} - - -int Muxer::stop(void) -{ - int res; - - if ((mState == STOPPED) || (mState == STOPPING)) - return 0; - if ((mState != STARTED) && (mState != STARTING)) { - PDRAW_LOGE("%s: invalid state (%s)", - __func__, - Element::getElementStateStr(mState)); - return -EPROTO; - } - setState(STOPPING); - - res = internalStop(); - if (res < 0) - return res; - - res = removeInputMedias(); - - setState(STOPPED); - - return 0; -} - - -int Muxer::addInputMedia(CodedVideoMedia *media) -{ - int res; - CodedChannel *channel = nullptr; - struct mbuf_coded_video_frame_queue *queue = nullptr; - - CodedSink::lock(); - - res = CodedSink::addInputMedia(media); - if (res == -EEXIST) { - CodedSink::unlock(); - return res; - } else if (res < 0) { - CodedSink::unlock(); - PDRAW_LOG_ERRNO("Sink::addInputMedia", -res); - return res; - } - - channel = getInputChannel(media); - if (channel == nullptr) { - CodedSink::unlock(); - res = -ENODEV; - PDRAW_LOG_ERRNO("Sink::getInputChannel", -res); - goto error; - } - channel->setKey(this); - - res = mbuf_coded_video_frame_queue_new(&queue); - if (res < 0) { - CodedSink::unlock(); - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_queue_new", -res); - goto error; - } - channel->setQueue(queue); - - res = addQueueEvtToLoop(queue, mSession->getLoop()); - if (res < 0) - goto error; - - CodedSink::unlock(); - - return 0; - -error: - removeInputMedia(media); - CodedSink::unlock(); - return res; -} - - -int Muxer::removeInputMedia(CodedVideoMedia *media) -{ - int res; - - CodedSink::lock(); - - CodedChannel *channel = getInputChannel(media); - if (channel == nullptr) { - CodedSink::unlock(); - res = -ENODEV; - PDRAW_LOG_ERRNO("Sink::getInputChannel", -res); - return res; - } - - /* Keep a reference on the queue to destroy it after removing the - * input media (avoids deadlocks when trying to push new frames out - * of upstream elements whereas the queue is already destroyed) */ - struct mbuf_coded_video_frame_queue *queue = channel->getQueue(); - - res = CodedSink::removeInputMedia(media); - if (res < 0) { - CodedSink::unlock(); - PDRAW_LOG_ERRNO("Sink::removeInputMedia", -res); - return res; - } - - if (queue != nullptr) { - res = removeQueueEvtFromLoop(queue, mSession->getLoop()); - res = mbuf_coded_video_frame_queue_flush(queue); - if (res < 0) - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_queue_flush", - -res); - res = mbuf_coded_video_frame_queue_destroy(queue); - if (res < 0) - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_queue_destroy", - -res); - } - - CodedSink::unlock(); - - return 0; -} - - -int Muxer::removeInputMedias(void) -{ - int res, inputMediaCount, i; - - CodedSink::lock(); - - inputMediaCount = getInputMediaCount(); - - /* Note: loop downwards because calling removeInputMedia removes - * input ports and decreases the media count */ - for (i = inputMediaCount - 1; i >= 0; i--) { - CodedVideoMedia *media = getInputMedia(i); - if (media == nullptr) { - PDRAW_LOG_ERRNO("getInputMedia", ENOENT); - continue; - } - res = removeInputMedia(media); - } - - CodedSink::unlock(); - - return 0; -} - - -int Muxer::addQueueEvtToLoop(struct mbuf_coded_video_frame_queue *queue, - struct pomp_loop *loop) -{ - int res; - struct pomp_evt *evt = nullptr; - - PDRAW_LOG_ERRNO_RETURN_ERR_IF(queue == nullptr, EINVAL); - PDRAW_LOG_ERRNO_RETURN_ERR_IF(loop == nullptr, EINVAL); - - res = mbuf_coded_video_frame_queue_get_event(queue, &evt); - if (res < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_queue_get_event", -res); - return res; - } - - res = pomp_evt_attach_to_loop(evt, loop, &queueEventCb, this); - if (res < 0) { - PDRAW_LOG_ERRNO("pomp_evt_attach_to_loop", -res); - return res; - } - - return 0; -} - - -int Muxer::removeQueueEvtFromLoop(struct mbuf_coded_video_frame_queue *queue, - struct pomp_loop *loop) -{ - int res; - struct pomp_evt *evt = nullptr; - - PDRAW_LOG_ERRNO_RETURN_ERR_IF(queue == nullptr, EINVAL); - PDRAW_LOG_ERRNO_RETURN_ERR_IF(loop == nullptr, EINVAL); - - res = mbuf_coded_video_frame_queue_get_event(queue, &evt); - if (res < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_queue_get_event", -res); - return res; - } - - res = pomp_evt_detach_from_loop(evt, loop); - if (res < 0) { - PDRAW_LOG_ERRNO("pomp_evt_detach_from_loop", -res); - return res; - } - - return 0; -} - - -/* Called on the loop thread */ -void Muxer::queueEventCb(struct pomp_evt *evt, void *userdata) -{ - int res; - Muxer *self = (Muxer *)userdata; - - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - res = self->process(); -} - - -void Muxer::onChannelTeardown(CodedChannel *channel) -{ - CodedSink::onChannelTeardown(channel); - - if (!mInputPorts.empty()) - return; - - int ret = stop(); - if (ret < 0) - PDRAW_LOG_ERRNO("stop", -ret); -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Generic muxer + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_muxer +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_muxer.hpp" +#include "pdraw_session.hpp" + +#include + +#include +#include + +namespace Pdraw { + + +Muxer::Muxer(Session *session, Element::Listener *elementListener) : + CodedSinkElement(session, elementListener, UINT_MAX, nullptr, 0) +{ + Element::setClassName(__func__); + + setState(CREATED); +} + + +Muxer::~Muxer(void) +{ + int res; + + if ((mState == STARTED) || (mState == STARTING)) + PDRAW_LOGW("%s: still running (%s)", + __func__, + Element::getElementStateStr(mState)); + + unsigned int count = getInputMediaCount(); + if (count > 0) { + PDRAW_LOGW("%s: not all input media have been removed", + __func__); + res = removeInputMedias(); + } +} + + +int Muxer::start(void) +{ + int res, err; + + if ((mState == STARTED) || (mState == STARTING)) { + return 0; + } + if (mState != CREATED) { + PDRAW_LOGE("%s: invalid state (%s)", + __func__, + Element::getElementStateStr(mState)); + return -EPROTO; + } + setState(STARTING); + + res = internalStart(); + if (res < 0) + goto error; + + setState(STARTED); + + return 0; + +error: + err = stop(); + return res; +} + + +int Muxer::stop(void) +{ + int res; + + if ((mState == STOPPED) || (mState == STOPPING)) + return 0; + if ((mState != STARTED) && (mState != STARTING)) { + PDRAW_LOGE("%s: invalid state (%s)", + __func__, + Element::getElementStateStr(mState)); + return -EPROTO; + } + setState(STOPPING); + + res = internalStop(); + if (res < 0) + return res; + + res = removeInputMedias(); + + setState(STOPPED); + + return 0; +} + + +int Muxer::addInputMedia(CodedVideoMedia *media) +{ + int res; + CodedChannel *channel = nullptr; + struct mbuf_coded_video_frame_queue *queue = nullptr; + + CodedSink::lock(); + + res = CodedSink::addInputMedia(media); + if (res == -EEXIST) { + CodedSink::unlock(); + return res; + } else if (res < 0) { + CodedSink::unlock(); + PDRAW_LOG_ERRNO("Sink::addInputMedia", -res); + return res; + } + + channel = getInputChannel(media); + if (channel == nullptr) { + CodedSink::unlock(); + res = -ENODEV; + PDRAW_LOG_ERRNO("Sink::getInputChannel", -res); + goto error; + } + channel->setKey(this); + + res = mbuf_coded_video_frame_queue_new(&queue); + if (res < 0) { + CodedSink::unlock(); + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_queue_new", -res); + goto error; + } + channel->setQueue(queue); + + res = addQueueEvtToLoop(queue, mSession->getLoop()); + if (res < 0) + goto error; + + CodedSink::unlock(); + + return 0; + +error: + removeInputMedia(media); + CodedSink::unlock(); + return res; +} + + +int Muxer::removeInputMedia(CodedVideoMedia *media) +{ + int res; + + CodedSink::lock(); + + CodedChannel *channel = getInputChannel(media); + if (channel == nullptr) { + CodedSink::unlock(); + res = -ENODEV; + PDRAW_LOG_ERRNO("Sink::getInputChannel", -res); + return res; + } + + /* Keep a reference on the queue to destroy it after removing the + * input media (avoids deadlocks when trying to push new frames out + * of upstream elements whereas the queue is already destroyed) */ + struct mbuf_coded_video_frame_queue *queue = channel->getQueue(); + + res = CodedSink::removeInputMedia(media); + if (res < 0) { + CodedSink::unlock(); + PDRAW_LOG_ERRNO("Sink::removeInputMedia", -res); + return res; + } + + if (queue != nullptr) { + res = removeQueueEvtFromLoop(queue, mSession->getLoop()); + res = mbuf_coded_video_frame_queue_flush(queue); + if (res < 0) + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_queue_flush", + -res); + res = mbuf_coded_video_frame_queue_destroy(queue); + if (res < 0) + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_queue_destroy", + -res); + } + + CodedSink::unlock(); + + return 0; +} + + +int Muxer::removeInputMedias(void) +{ + int res, inputMediaCount, i; + + CodedSink::lock(); + + inputMediaCount = getInputMediaCount(); + + /* Note: loop downwards because calling removeInputMedia removes + * input ports and decreases the media count */ + for (i = inputMediaCount - 1; i >= 0; i--) { + CodedVideoMedia *media = getInputMedia(i); + if (media == nullptr) { + PDRAW_LOG_ERRNO("getInputMedia", ENOENT); + continue; + } + res = removeInputMedia(media); + } + + CodedSink::unlock(); + + return 0; +} + + +int Muxer::addQueueEvtToLoop(struct mbuf_coded_video_frame_queue *queue, + struct pomp_loop *loop) +{ + int res; + struct pomp_evt *evt = nullptr; + + PDRAW_LOG_ERRNO_RETURN_ERR_IF(queue == nullptr, EINVAL); + PDRAW_LOG_ERRNO_RETURN_ERR_IF(loop == nullptr, EINVAL); + + res = mbuf_coded_video_frame_queue_get_event(queue, &evt); + if (res < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_queue_get_event", -res); + return res; + } + + res = pomp_evt_attach_to_loop(evt, loop, &queueEventCb, this); + if (res < 0) { + PDRAW_LOG_ERRNO("pomp_evt_attach_to_loop", -res); + return res; + } + + return 0; +} + + +int Muxer::removeQueueEvtFromLoop(struct mbuf_coded_video_frame_queue *queue, + struct pomp_loop *loop) +{ + int res; + struct pomp_evt *evt = nullptr; + + PDRAW_LOG_ERRNO_RETURN_ERR_IF(queue == nullptr, EINVAL); + PDRAW_LOG_ERRNO_RETURN_ERR_IF(loop == nullptr, EINVAL); + + res = mbuf_coded_video_frame_queue_get_event(queue, &evt); + if (res < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_queue_get_event", -res); + return res; + } + + res = pomp_evt_detach_from_loop(evt, loop); + if (res < 0) { + PDRAW_LOG_ERRNO("pomp_evt_detach_from_loop", -res); + return res; + } + + return 0; +} + + +/* Called on the loop thread */ +void Muxer::queueEventCb(struct pomp_evt *evt, void *userdata) +{ + int res; + Muxer *self = (Muxer *)userdata; + + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + res = self->process(); +} + + +void Muxer::onChannelTeardown(CodedChannel *channel) +{ + CodedSink::onChannelTeardown(channel); + + if (!mInputPorts.empty()) + return; + + int ret = stop(); + if (ret < 0) + PDRAW_LOG_ERRNO("stop", -ret); +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_muxer.hpp b/libpdraw/src/pdraw_muxer.hpp index acecc35..a574fdb 100644 --- a/libpdraw/src/pdraw_muxer.hpp +++ b/libpdraw/src/pdraw_muxer.hpp @@ -1,86 +1,86 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Generic muxer - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_MUXER_HPP_ -#define _PDRAW_MUXER_HPP_ - -#include "pdraw_element.hpp" -#include "pdraw_sink_raw_video.hpp" - -#include - -#include -#include - -namespace Pdraw { - - -class Muxer : public CodedSinkElement { -public: - Muxer(Session *session, Element::Listener *elementListener); - - virtual ~Muxer(void) = 0; - - int start(void); - - int stop(void); - - /* Must be called on the loop thread */ - virtual int addInputMedia(CodedVideoMedia *media); - - /* Must be called on the loop thread */ - virtual int removeInputMedia(CodedVideoMedia *media); - - /* Must be called on the loop thread */ - virtual int removeInputMedias(void); - -protected: - virtual int internalStart(void) = 0; - - virtual int internalStop(void) = 0; - - virtual int process(void) = 0; - - /* Must be called on the loop thread */ - int addQueueEvtToLoop(struct mbuf_coded_video_frame_queue *queue, - struct pomp_loop *loop); - - /* Must be called on the loop thread */ - int removeQueueEvtFromLoop(struct mbuf_coded_video_frame_queue *queue, - struct pomp_loop *loop); - - static void queueEventCb(struct pomp_evt *evt, void *userdata); - - virtual void onChannelTeardown(CodedChannel *channel); -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_MUXER_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Generic muxer + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_MUXER_HPP_ +#define _PDRAW_MUXER_HPP_ + +#include "pdraw_element.hpp" +#include "pdraw_sink_raw_video.hpp" + +#include + +#include +#include + +namespace Pdraw { + + +class Muxer : public CodedSinkElement { +public: + Muxer(Session *session, Element::Listener *elementListener); + + virtual ~Muxer(void) = 0; + + int start(void); + + int stop(void); + + /* Must be called on the loop thread */ + virtual int addInputMedia(CodedVideoMedia *media); + + /* Must be called on the loop thread */ + virtual int removeInputMedia(CodedVideoMedia *media); + + /* Must be called on the loop thread */ + virtual int removeInputMedias(void); + +protected: + virtual int internalStart(void) = 0; + + virtual int internalStop(void) = 0; + + virtual int process(void) = 0; + + /* Must be called on the loop thread */ + int addQueueEvtToLoop(struct mbuf_coded_video_frame_queue *queue, + struct pomp_loop *loop); + + /* Must be called on the loop thread */ + int removeQueueEvtFromLoop(struct mbuf_coded_video_frame_queue *queue, + struct pomp_loop *loop); + + static void queueEventCb(struct pomp_evt *evt, void *userdata); + + virtual void onChannelTeardown(CodedChannel *channel); +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_MUXER_HPP_ */ diff --git a/libpdraw/src/pdraw_muxer_record.cpp b/libpdraw/src/pdraw_muxer_record.cpp index 83bba8e..097e22f 100644 --- a/libpdraw/src/pdraw_muxer_record.cpp +++ b/libpdraw/src/pdraw_muxer_record.cpp @@ -1,838 +1,838 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Record muxer - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_recmuxer -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_muxer_record.hpp" -#include "pdraw_session.hpp" - -#include - -#ifdef _WIN32 -# include -#else /* !_WIN32 */ -# include -#endif /* !_WIN32 */ - -#include - -#define MP4_TIMESCALE 90000 - - -namespace Pdraw { - - -#define NB_SUPPORTED_FORMATS 2 -static struct vdef_coded_format supportedFormats[NB_SUPPORTED_FORMATS]; -static pthread_once_t supportedFormatsIsInit = PTHREAD_ONCE_INIT; -static void initializeSupportedFormats(void) -{ - supportedFormats[0] = vdef_h264_avcc; - supportedFormats[1] = vdef_h265_hvcc; -} - - -RecordMuxer::RecordMuxer(Session *session, - Element::Listener *elementListener, - const std::string &fileName) : - Muxer(session, elementListener), - mFileName(fileName), mMux(nullptr), mMediaDate(0), - mHasMetadataTrack(false), mMetaBuffer(nullptr) -{ - - (void)pthread_once(&supportedFormatsIsInit, initializeSupportedFormats); - - Element::setClassName(__func__); - setCodedVideoMediaFormatCaps(supportedFormats, NB_SUPPORTED_FORMATS); -} - - -RecordMuxer::~RecordMuxer(void) -{ - int err; - - err = internalStop(); - - return; -} - - -/* Must be called on the loop thread */ -int RecordMuxer::addInputMedia(CodedVideoMedia *media) -{ - int res; - - res = Muxer::addInputMedia(media); - if (res < 0) - return res; - - if (mMux != nullptr) { - /* Add a track for the media if the muxer is already opened */ - res = addTrackForMedia(media, 0); - if (res < 0) - return res; - } - - return 0; -} - - -int RecordMuxer::internalStart(void) -{ - int res, inputMediaCount, i; - uint64_t now; - - mMediaDate = time(nullptr); - now = mMediaDate; - - /* Create the metadata buffer */ - mMetaBuffer = (uint8_t *)malloc(VMETA_FRAME_MAX_SIZE); - if (mMetaBuffer == nullptr) { - res = -ENOMEM; - PDRAW_LOG_ERRNO("malloc", -res); - return res; - } - - /* Create the MP4 muxer */ - res = mp4_mux_open(mFileName.c_str(), MP4_TIMESCALE, now, now, &mMux); - if (res < 0) { - PDRAW_LOG_ERRNO("mp4_mux_open", -res); - return res; - } - - CodedSink::lock(); - - inputMediaCount = getInputMediaCount(); - - /* Add a track for all existing medias */ - for (i = 0; i < inputMediaCount; i++) { - CodedVideoMedia *media = getInputMedia(i); - if (media == nullptr) { - PDRAW_LOG_ERRNO("getInputMedia", ENOENT); - continue; - } - res = addTrackForMedia(media, now); - if (res < 0) - continue; - } - - CodedSink::unlock(); - - return 0; -} - - -int RecordMuxer::internalStop(void) -{ - int err; - - if (mMux != nullptr) { - mergeSessionMetadata(); - - /* Finalize the MP4 file */ - err = mp4_mux_close(mMux); - if (err < 0) - PDRAW_LOG_ERRNO("mp4_mux_close", -err); - mMux = nullptr; - } - - /* Free the metadata buffer */ - free(mMetaBuffer); - mMetaBuffer = nullptr; - - return 0; -} - - -void RecordMuxer::mergeSessionMetadata(void) -{ - int err; - struct vmeta_session fileSessionMeta = {}; - - /* First pass: fill fileSessionMeta with identical values */ - bool first = true; - for (auto it = mTracks.begin(); it != mTracks.end(); it++) { - CodedVideoMedia *media = it->first; - - if (first) { - fileSessionMeta = media->sessionMeta; - first = false; - continue; - } - - if (strcmp(fileSessionMeta.friendly_name, - media->sessionMeta.friendly_name) != 0) - fileSessionMeta.friendly_name[0] = '\0'; - - if (strcmp(fileSessionMeta.maker, media->sessionMeta.maker) != - 0) - fileSessionMeta.maker[0] = '\0'; - - if (strcmp(fileSessionMeta.model, media->sessionMeta.model) != - 0) - fileSessionMeta.model[0] = '\0'; - - if (strcmp(fileSessionMeta.model_id, - media->sessionMeta.model_id) != 0) - fileSessionMeta.model_id[0] = '\0'; - - if (strcmp(fileSessionMeta.serial_number, - media->sessionMeta.serial_number) != 0) - fileSessionMeta.serial_number[0] = '\0'; - - if (strcmp(fileSessionMeta.software_version, - media->sessionMeta.software_version) != 0) - fileSessionMeta.software_version[0] = '\0'; - - if (strcmp(fileSessionMeta.build_id, - media->sessionMeta.build_id) != 0) - fileSessionMeta.build_id[0] = '\0'; - - if (strcmp(fileSessionMeta.title, media->sessionMeta.title) != - 0) - fileSessionMeta.title[0] = '\0'; - - if (strcmp(fileSessionMeta.comment, - media->sessionMeta.comment) != 0) - fileSessionMeta.comment[0] = '\0'; - - if (strcmp(fileSessionMeta.copyright, - media->sessionMeta.copyright) != 0) - fileSessionMeta.copyright[0] = '\0'; - - if (fileSessionMeta.media_date != - media->sessionMeta.media_date) { - fileSessionMeta.media_date = 0; - fileSessionMeta.media_date_gmtoff = 0; - } - - if (fileSessionMeta.run_date != media->sessionMeta.run_date) { - fileSessionMeta.run_date = 0; - fileSessionMeta.run_date_gmtoff = 0; - } - - if (strcmp(fileSessionMeta.run_id, media->sessionMeta.run_id) != - 0) - fileSessionMeta.run_id[0] = '\0'; - - if (strcmp(fileSessionMeta.boot_id, - media->sessionMeta.boot_id) != 0) - fileSessionMeta.boot_id[0] = '\0'; - - if (strcmp(fileSessionMeta.flight_id, - media->sessionMeta.flight_id) != 0) - fileSessionMeta.flight_id[0] = '\0'; - - if (strcmp(fileSessionMeta.custom_id, - media->sessionMeta.custom_id) != 0) - fileSessionMeta.custom_id[0] = '\0'; - - if (memcmp(&fileSessionMeta.takeoff_loc, - &media->sessionMeta.takeoff_loc, - sizeof(struct vmeta_location)) != 0) - fileSessionMeta.takeoff_loc.valid = 0; - - if (memcmp(&fileSessionMeta.picture_fov, - &media->sessionMeta.picture_fov, - sizeof(struct vmeta_fov)) != 0) { - fileSessionMeta.picture_fov.has_horz = 0; - fileSessionMeta.picture_fov.has_vert = 0; - } - - if ((fileSessionMeta.has_thermal != - media->sessionMeta.has_thermal) || - (memcmp(&fileSessionMeta.thermal, - &media->sessionMeta.thermal, - sizeof(struct vmeta_thermal)) != 0)) - fileSessionMeta.has_thermal = 0; - - if (fileSessionMeta.camera_type != - media->sessionMeta.camera_type) - fileSessionMeta.camera_type = VMETA_CAMERA_TYPE_UNKNOWN; - - if (fileSessionMeta.video_mode != media->sessionMeta.video_mode) - fileSessionMeta.video_mode = VMETA_VIDEO_MODE_UNKNOWN; - } - - /* Second pass: cancel identical values from trackSessionMeta and - * write track-level metadata */ - for (auto it = mTracks.begin(); it != mTracks.end(); it++) { - CodedVideoMedia *media = it->first; - Track &track = it->second; - struct vmeta_session trackSessionMeta = media->sessionMeta; - - if (fileSessionMeta.friendly_name[0] != '\0') - trackSessionMeta.friendly_name[0] = '\0'; - - if (fileSessionMeta.maker[0] != '\0') - trackSessionMeta.maker[0] = '\0'; - - if (fileSessionMeta.model[0] != '\0') - trackSessionMeta.model[0] = '\0'; - - if (fileSessionMeta.model_id[0] != '\0') - trackSessionMeta.model_id[0] = '\0'; - - if (fileSessionMeta.serial_number[0] != '\0') - trackSessionMeta.serial_number[0] = '\0'; - - if (fileSessionMeta.software_version[0] != '\0') - trackSessionMeta.software_version[0] = '\0'; - - if (fileSessionMeta.build_id[0] != '\0') - trackSessionMeta.build_id[0] = '\0'; - - if (fileSessionMeta.title[0] != '\0') - trackSessionMeta.title[0] = '\0'; - - if (fileSessionMeta.comment[0] != '\0') - trackSessionMeta.comment[0] = '\0'; - - if (fileSessionMeta.copyright[0] != '\0') - trackSessionMeta.copyright[0] = '\0'; - - if (fileSessionMeta.media_date != 0) { - trackSessionMeta.media_date = 0; - trackSessionMeta.media_date_gmtoff = 0; - } - - if (fileSessionMeta.run_date != 0) { - trackSessionMeta.run_date = 0; - trackSessionMeta.run_date_gmtoff = 0; - } - - if (fileSessionMeta.run_id[0] != '\0') - trackSessionMeta.run_id[0] = '\0'; - - if (fileSessionMeta.boot_id[0] != '\0') - trackSessionMeta.boot_id[0] = '\0'; - - if (fileSessionMeta.flight_id[0] != '\0') - trackSessionMeta.flight_id[0] = '\0'; - - if (fileSessionMeta.custom_id[0] != '\0') - trackSessionMeta.custom_id[0] = '\0'; - - if (fileSessionMeta.takeoff_loc.valid) - trackSessionMeta.takeoff_loc.valid = 0; - - if (fileSessionMeta.picture_fov.has_horz || - fileSessionMeta.picture_fov.has_vert) { - trackSessionMeta.picture_fov.has_horz = 0; - trackSessionMeta.picture_fov.has_vert = 0; - } - - if (fileSessionMeta.has_thermal) - trackSessionMeta.has_thermal = 0; - - if (fileSessionMeta.camera_type != VMETA_CAMERA_TYPE_UNKNOWN) { - trackSessionMeta.camera_type = - VMETA_CAMERA_TYPE_UNKNOWN; - } - - if (fileSessionMeta.video_mode != VMETA_VIDEO_MODE_UNKNOWN) - trackSessionMeta.video_mode = VMETA_VIDEO_MODE_UNKNOWN; - - struct SessionMetaWriteTrackCbUserdata ud = { - .muxer = this, - .trackId = track.trackId, - }; - err = vmeta_session_recording_write( - &trackSessionMeta, &sessionMetaWriteTrackCb, &ud); - if (err < 0) - PDRAW_LOG_ERRNO("vmeta_session_recording_write", -err); - } - - if (fileSessionMeta.media_date == 0) { - uint64_t mediaDate = 0; - int32_t mediaDateGmtoff = 0; - err = time_local_get(&mediaDate, &mediaDateGmtoff); - if (err < 0) - PDRAW_LOG_ERRNO("time_local_get", -err); - fileSessionMeta.media_date = mediaDate; - fileSessionMeta.media_date_gmtoff = mediaDateGmtoff; - } - if (fileSessionMeta.title[0] == '\0') { - err = time_local_format(fileSessionMeta.media_date, - fileSessionMeta.media_date_gmtoff, - TIME_FMT_RFC1123, - fileSessionMeta.title, - sizeof(fileSessionMeta.title)); - if (err < 0) - PDRAW_LOG_ERRNO("time_local_format", -err); - } - - err = vmeta_session_recording_write( - &fileSessionMeta, &sessionMetaWriteFileCb, this); - if (err < 0) - PDRAW_LOG_ERRNO("vmeta_session_recording_write", -err); -} - - -int RecordMuxer::addTrackForMedia(CodedVideoMedia *media, uint64_t trackTime) -{ - int res, trackId; - const uint8_t *vps = nullptr, *sps = nullptr, *pps = nullptr; - size_t vpsSize = 0, spsSize = 0, ppsSize = 0; - struct mp4_video_decoder_config cfg = {}; - - PDRAW_LOG_ERRNO_RETURN_ERR_IF(mMux == nullptr, EAGAIN); - - if (trackTime == 0) - trackTime = time(nullptr); - - /* Add a new video track */ - struct mp4_mux_track_params params = { - .type = MP4_TRACK_TYPE_VIDEO, - .name = "DefaultVideo", /* TODO */ - .enabled = 1, /* TODO */ - .in_movie = 1, /* TODO */ - .in_preview = 1, /* TODO */ - .timescale = MP4_TIMESCALE, /* TODO */ - .creation_time = trackTime, - .modification_time = trackTime, - }; - res = mp4_mux_add_track(mMux, ¶ms); - if (res < 0) { - PDRAW_LOG_ERRNO("mp4_mux_add_track", -res); - return res; - } - trackId = res; - mTracks.insert( - {media, Track((uint32_t)trackId, 0, trackTime, INT64_MAX)}); - - switch (media->format.encoding) { - case VDEF_ENCODING_H264: - /* Set the H.264 parameter sets */ - res = media->getPs( - nullptr, nullptr, &sps, &spsSize, &pps, &ppsSize); - if (res < 0) { - PDRAW_LOG_ERRNO("CodedVideoMedia::getPs", -res); - return res; - } - cfg.codec = MP4_VIDEO_CODEC_AVC; - cfg.width = media->info.resolution.width; - cfg.height = media->info.resolution.height; - cfg.avc.c_sps = sps; - cfg.avc.sps_size = spsSize; - cfg.avc.c_pps = pps; - cfg.avc.pps_size = ppsSize; - break; - case VDEF_ENCODING_H265: - /* Set the H.265 parameter sets */ - res = media->getPs( - &vps, &vpsSize, &sps, &spsSize, &pps, &ppsSize); - if (res < 0) { - PDRAW_LOG_ERRNO("CodedVideoMedia::getPs", -res); - return res; - } - cfg.codec = MP4_VIDEO_CODEC_HEVC; - cfg.width = media->info.resolution.width; - cfg.height = media->info.resolution.height; - cfg.hevc.c_vps = vps; - cfg.hevc.vps_size = vpsSize; - cfg.hevc.c_sps = sps; - cfg.hevc.sps_size = spsSize; - cfg.hevc.c_pps = pps; - cfg.hevc.pps_size = ppsSize; - /* TODO: fill cfg.hevc.hvcc_info */ - break; - default: - break; - } - - res = mp4_mux_track_set_video_decoder_config(mMux, trackId, &cfg); - if (res < 0) { - PDRAW_LOG_ERRNO("mp4_mux_track_set_video_decoder_config", -res); - return res; - } - - return 0; -} - - -int RecordMuxer::addMetadataTrack(Track *ref, enum vmeta_frame_type metaType) -{ - int res; - const char *mimeType = nullptr; - const char *contentEncoding = nullptr; - - PDRAW_LOG_ERRNO_RETURN_ERR_IF(mMux == nullptr, EAGAIN); - PDRAW_LOG_ERRNO_RETURN_ERR_IF(mHasMetadataTrack, EALREADY); - - switch (metaType) { - case VMETA_FRAME_TYPE_V2: - mimeType = VMETA_FRAME_V2_MIME_TYPE; - contentEncoding = VMETA_FRAME_V2_CONTENT_ENCODING; - break; - case VMETA_FRAME_TYPE_V3: - mimeType = VMETA_FRAME_V3_MIME_TYPE; - contentEncoding = VMETA_FRAME_V3_CONTENT_ENCODING; - break; - case VMETA_FRAME_TYPE_PROTO: - mimeType = VMETA_FRAME_PROTO_MIME_TYPE; - contentEncoding = VMETA_FRAME_PROTO_CONTENT_ENCODING; - break; - default: - PDRAW_LOGE("%s: unsupported metadata type '%s'", - __func__, - vmeta_frame_type_str(metaType)); - return -ENOSYS; - } - - /* Add a new metadata track */ - struct mp4_mux_track_params params = { - .type = MP4_TRACK_TYPE_METADATA, - .name = "ParrotVideoMetadata", /* TODO */ - .enabled = 1, /* TODO */ - .in_movie = 1, /* TODO */ - .in_preview = 1, /* TODO */ - .timescale = MP4_TIMESCALE, /* TODO */ - .creation_time = ref->trackTime, - .modification_time = ref->trackTime, - }; - res = mp4_mux_add_track(mMux, ¶ms); - if (res < 0) { - PDRAW_LOG_ERRNO("mp4_mux_add_track", -res); - return res; - } - ref->metaTrackId = (uint32_t)res; - - /* Set the metadata mime type */ - res = mp4_mux_track_set_metadata_mime_type( - mMux, ref->metaTrackId, contentEncoding, mimeType); - if (res < 0) { - PDRAW_LOG_ERRNO("mp4_mux_track_set_metadata_mime_type", -res); - ref->metaTrackId = 0; - return res; - } - - /* Add track reference */ - res = mp4_mux_add_ref_to_track(mMux, ref->metaTrackId, ref->trackId); - if (res < 0) { - PDRAW_LOG_ERRNO("mp4_mux_add_ref_to_track", -res); - ref->metaTrackId = 0; - return res; - } - - mHasMetadataTrack = true; - - return 0; -} - - -int RecordMuxer::process(void) -{ - int res, inputMediaCount, i; - - if (mState != STARTED) - return 0; - - PDRAW_LOG_ERRNO_RETURN_ERR_IF(mMux == nullptr, EAGAIN); - - CodedSink::lock(); - - inputMediaCount = getInputMediaCount(); - - for (i = 0; i < inputMediaCount; i++) { - CodedVideoMedia *media = getInputMedia(i); - if (media == nullptr) { - res = -ENOENT; - PDRAW_LOG_ERRNO("getInputMedia", -res); - continue; - } - - /* Process each media */ - processMedia(media); - } - - CodedSink::unlock(); - - return 0; -} - - -int RecordMuxer::processMedia(CodedVideoMedia *media) -{ - int res, err; - struct mbuf_coded_video_frame *frame = nullptr; - Track &track = mTracks[media]; - - if (track.trackId == 0) { - res = -ENOENT; - PDRAW_LOG_ERRNO("track->trackId", -res); - return res; - } - CodedChannel *channel = getInputChannel(media); - if (channel == nullptr) { - res = -ENODEV; - PDRAW_LOG_ERRNO("Sink::getInputChannel", -res); - return res; - } - struct mbuf_coded_video_frame_queue *queue = channel->getQueue(); - if (queue == nullptr) { - res = -ENODEV; - PDRAW_LOG_ERRNO("Channel::getQueue", -res); - return res; - } - - /* TODO: This loops drops frames if the processFrame function fails. - * This is a problem for most coded streams, so the current behavior - * will result in a buggy output file. - * We should instead check what the error was, and decide to either: - * - Retry later, which can be achieved by using queue_peek() instead of - * queue_pop(), or - * - Discard the frame, flush the queue, and ask the upstream elements - * for a complete resync (or mabye even terminate the record and warn - * the app ?) - * The second choice is important to have, because if an error persists, - * then this whole element will be stuck on the buggy frame. */ - do { - res = mbuf_coded_video_frame_queue_pop(queue, &frame); - if (res < 0) { - if (res != -EAGAIN) - PDRAW_LOG_ERRNO( - "mbuf_coded_video_frame_queue_pop", - -res); - continue; - } - - /* Process each buffer */ - res = processFrame(media, &track, frame); - - err = mbuf_coded_video_frame_unref(frame); - if (err < 0) - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_unref", -err); - } while (res == 0); - - return 0; -} - - -int RecordMuxer::processFrame(CodedVideoMedia *media, - Track *track, - struct mbuf_coded_video_frame *frame) -{ - int res; - struct mbuf_ancillary_data *ancillaryData = nullptr; - struct vmeta_frame *metadata = nullptr; - CodedVideoMedia::Frame *meta; - struct mp4_mux_scattered_sample sample = {}; - const void *aData; - const void **frameNalus = nullptr; - size_t *frameNalusSize = nullptr; - - sample.nbuffers = mbuf_coded_video_frame_get_nalu_count(frame); - if (sample.nbuffers < 0) { - res = sample.nbuffers; - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_nalu_count", -res); - goto out; - } - frameNalus = - (const void **)calloc(sample.nbuffers, sizeof(*sample.buffers)); - frameNalusSize = (size_t *)calloc(sample.nbuffers, sizeof(*sample.len)); - if (!frameNalus || !frameNalusSize) { - res = -ENOMEM; - PDRAW_LOG_ERRNO("calloc", -res); - goto out; - } - - for (int i = 0; i < sample.nbuffers; i++) { - struct vdef_nalu nalu; - res = mbuf_coded_video_frame_get_nalu( - frame, i, &frameNalus[i], &nalu); - if (res < 0) { - PDRAW_LOG_ERRNO( - "mbuf_coded_video_frame_get_nalu(%d)", -res, i); - goto out; - } - frameNalusSize[i] = nalu.size; - } - - res = mbuf_coded_video_frame_get_ancillary_data( - frame, - PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME, - &ancillaryData); - if (res < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_ancillary_data", - -res); - goto out; - } - aData = mbuf_ancillary_data_get_buffer(ancillaryData, NULL); - meta = (CodedVideoMedia::Frame *)aData; - - /* Add a video sample to the MP4 muxer */ - sample.buffers = (const uint8_t *const *)frameNalus; - sample.len = (const size_t *)frameNalusSize; - sample.sync = meta->isSync; - sample.dts = - mp4_usec_to_sample_time(meta->ntpRawTimestamp, MP4_TIMESCALE); - - /* Decoding timestamps must be monotonic; avoid duplicate or - * rollback timestamps by faking the timestamp as the previous - * timestamp + 1 */ - if ((track->lastSampleTs != INT64_MAX) && - (sample.dts == track->lastSampleTs)) { - PDRAW_LOGW("duplicate timestamp (%" PRIu64 "), incrementing", - track->lastSampleTs); - sample.dts = track->lastSampleTs + 1; - } else if ((track->lastSampleTs != INT64_MAX) && - (sample.dts < track->lastSampleTs)) { - PDRAW_LOGW("timestamp rollback from %" PRIu64 " to %" PRIu64 - ", incrementing", - track->lastSampleTs, - sample.dts); - sample.dts = track->lastSampleTs + 1; - } - track->lastSampleTs = sample.dts; - - res = mp4_mux_track_add_scattered_sample(mMux, track->trackId, &sample); - if (res < 0) { - PDRAW_LOG_ERRNO("mp4_mux_track_add_scattered_sample", -res); - goto out; - } - - res = mbuf_coded_video_frame_get_metadata(frame, &metadata); - if (res == -ENOENT) { - /* No metadata, skip to the end */ - res = 0; - goto out; - } else if (res < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_metadata", -res); - goto out; - } - if (!mHasMetadataTrack) - addMetadataTrack(track, metadata->type); - if (track->metaTrackId) { - /* Add a metadata sample to the MP4 muxer */ - const uint8_t *metaContent = nullptr; - size_t metaLen = 0; - if (metadata->type != VMETA_FRAME_TYPE_PROTO) { - struct vmeta_buffer metaBuf; - vmeta_buffer_set_data( - &metaBuf, mMetaBuffer, VMETA_FRAME_MAX_SIZE, 0); - res = vmeta_frame_write(&metaBuf, metadata); - if (res < 0) { - PDRAW_LOG_ERRNO("vmeta_frame_write", -res); - goto out; - } - metaContent = metaBuf.data; - metaLen = metaBuf.pos; - } else { - res = vmeta_frame_proto_get_buffer( - metadata, &metaContent, &metaLen); - if (res < 0) { - PDRAW_LOG_ERRNO("vmeta_frame_proto_get_buffer", - -res); - } - } - if ((metaContent != nullptr) && (metaLen > 0)) { - struct mp4_mux_sample metaSample = { - .buffer = metaContent, - .len = metaLen, - .sync = 1, - .dts = sample.dts, - }; - res = mp4_mux_track_add_sample( - mMux, track->metaTrackId, &metaSample); - if (res < 0) { - PDRAW_LOG_ERRNO("mp4_mux_track_add_sample", - -res); - goto out; - } - } - if (metadata->type == VMETA_FRAME_TYPE_PROTO) { - vmeta_frame_proto_release_buffer(metadata, metaContent); - } - } -out: - if (metadata) - vmeta_frame_unref(metadata); - if (ancillaryData) - mbuf_ancillary_data_unref(ancillaryData); - if (frame && frameNalus) { - for (int i = 0; i < sample.nbuffers; i++) { - if (!frameNalus[i]) - continue; - int err = mbuf_coded_video_frame_release_nalu( - frame, i, frameNalus[i]); - if (err < 0) - PDRAW_LOG_ERRNO( - "mbuf_coded_video_frame_release_nalu", - -err); - } - } - free(frameNalus); - free(frameNalusSize); - - return res; -} - - -void RecordMuxer::sessionMetaWriteFileCb(enum vmeta_record_type type, - const char *key, - const char *value, - void *userdata) -{ - int res; - RecordMuxer *self = (RecordMuxer *)userdata; - - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - res = mp4_mux_add_file_metadata(self->mMux, key, value); - if (res < 0) - PDRAW_LOG_ERRNO("mp4_mux_add_file_metadata", -res); -} - - -void RecordMuxer::sessionMetaWriteTrackCb(enum vmeta_record_type type, - const char *key, - const char *value, - void *userdata) -{ - int res; - struct SessionMetaWriteTrackCbUserdata *ud = - (struct SessionMetaWriteTrackCbUserdata *)userdata; - - ULOG_ERRNO_RETURN_IF(ud == nullptr, EINVAL); - - RecordMuxer *self = ud->muxer; - uint32_t trackId = ud->trackId; - - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - res = mp4_mux_add_track_metadata(self->mMux, trackId, key, value); - if (res < 0) - PDRAW_LOG_ERRNO("mp4_mux_add_track_metadata", -res); -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Record muxer + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_recmuxer +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_muxer_record.hpp" +#include "pdraw_session.hpp" + +#include + +#ifdef _WIN32 +# include +#else /* !_WIN32 */ +# include +#endif /* !_WIN32 */ + +#include + +#define MP4_TIMESCALE 90000 + + +namespace Pdraw { + + +#define NB_SUPPORTED_FORMATS 2 +static struct vdef_coded_format supportedFormats[NB_SUPPORTED_FORMATS]; +static pthread_once_t supportedFormatsIsInit = PTHREAD_ONCE_INIT; +static void initializeSupportedFormats(void) +{ + supportedFormats[0] = vdef_h264_avcc; + supportedFormats[1] = vdef_h265_hvcc; +} + + +RecordMuxer::RecordMuxer(Session *session, + Element::Listener *elementListener, + const std::string &fileName) : + Muxer(session, elementListener), + mFileName(fileName), mMux(nullptr), mMediaDate(0), + mHasMetadataTrack(false), mMetaBuffer(nullptr) +{ + + (void)pthread_once(&supportedFormatsIsInit, initializeSupportedFormats); + + Element::setClassName(__func__); + setCodedVideoMediaFormatCaps(supportedFormats, NB_SUPPORTED_FORMATS); +} + + +RecordMuxer::~RecordMuxer(void) +{ + int err; + + err = internalStop(); + + return; +} + + +/* Must be called on the loop thread */ +int RecordMuxer::addInputMedia(CodedVideoMedia *media) +{ + int res; + + res = Muxer::addInputMedia(media); + if (res < 0) + return res; + + if (mMux != nullptr) { + /* Add a track for the media if the muxer is already opened */ + res = addTrackForMedia(media, 0); + if (res < 0) + return res; + } + + return 0; +} + + +int RecordMuxer::internalStart(void) +{ + int res, inputMediaCount, i; + uint64_t now; + + mMediaDate = time(nullptr); + now = mMediaDate; + + /* Create the metadata buffer */ + mMetaBuffer = (uint8_t *)malloc(VMETA_FRAME_MAX_SIZE); + if (mMetaBuffer == nullptr) { + res = -ENOMEM; + PDRAW_LOG_ERRNO("malloc", -res); + return res; + } + + /* Create the MP4 muxer */ + res = mp4_mux_open(mFileName.c_str(), MP4_TIMESCALE, now, now, &mMux); + if (res < 0) { + PDRAW_LOG_ERRNO("mp4_mux_open", -res); + return res; + } + + CodedSink::lock(); + + inputMediaCount = getInputMediaCount(); + + /* Add a track for all existing medias */ + for (i = 0; i < inputMediaCount; i++) { + CodedVideoMedia *media = getInputMedia(i); + if (media == nullptr) { + PDRAW_LOG_ERRNO("getInputMedia", ENOENT); + continue; + } + res = addTrackForMedia(media, now); + if (res < 0) + continue; + } + + CodedSink::unlock(); + + return 0; +} + + +int RecordMuxer::internalStop(void) +{ + int err; + + if (mMux != nullptr) { + mergeSessionMetadata(); + + /* Finalize the MP4 file */ + err = mp4_mux_close(mMux); + if (err < 0) + PDRAW_LOG_ERRNO("mp4_mux_close", -err); + mMux = nullptr; + } + + /* Free the metadata buffer */ + free(mMetaBuffer); + mMetaBuffer = nullptr; + + return 0; +} + + +void RecordMuxer::mergeSessionMetadata(void) +{ + int err; + struct vmeta_session fileSessionMeta = {}; + + /* First pass: fill fileSessionMeta with identical values */ + bool first = true; + for (auto it = mTracks.begin(); it != mTracks.end(); it++) { + CodedVideoMedia *media = it->first; + + if (first) { + fileSessionMeta = media->sessionMeta; + first = false; + continue; + } + + if (strcmp(fileSessionMeta.friendly_name, + media->sessionMeta.friendly_name) != 0) + fileSessionMeta.friendly_name[0] = '\0'; + + if (strcmp(fileSessionMeta.maker, media->sessionMeta.maker) != + 0) + fileSessionMeta.maker[0] = '\0'; + + if (strcmp(fileSessionMeta.model, media->sessionMeta.model) != + 0) + fileSessionMeta.model[0] = '\0'; + + if (strcmp(fileSessionMeta.model_id, + media->sessionMeta.model_id) != 0) + fileSessionMeta.model_id[0] = '\0'; + + if (strcmp(fileSessionMeta.serial_number, + media->sessionMeta.serial_number) != 0) + fileSessionMeta.serial_number[0] = '\0'; + + if (strcmp(fileSessionMeta.software_version, + media->sessionMeta.software_version) != 0) + fileSessionMeta.software_version[0] = '\0'; + + if (strcmp(fileSessionMeta.build_id, + media->sessionMeta.build_id) != 0) + fileSessionMeta.build_id[0] = '\0'; + + if (strcmp(fileSessionMeta.title, media->sessionMeta.title) != + 0) + fileSessionMeta.title[0] = '\0'; + + if (strcmp(fileSessionMeta.comment, + media->sessionMeta.comment) != 0) + fileSessionMeta.comment[0] = '\0'; + + if (strcmp(fileSessionMeta.copyright, + media->sessionMeta.copyright) != 0) + fileSessionMeta.copyright[0] = '\0'; + + if (fileSessionMeta.media_date != + media->sessionMeta.media_date) { + fileSessionMeta.media_date = 0; + fileSessionMeta.media_date_gmtoff = 0; + } + + if (fileSessionMeta.run_date != media->sessionMeta.run_date) { + fileSessionMeta.run_date = 0; + fileSessionMeta.run_date_gmtoff = 0; + } + + if (strcmp(fileSessionMeta.run_id, media->sessionMeta.run_id) != + 0) + fileSessionMeta.run_id[0] = '\0'; + + if (strcmp(fileSessionMeta.boot_id, + media->sessionMeta.boot_id) != 0) + fileSessionMeta.boot_id[0] = '\0'; + + if (strcmp(fileSessionMeta.flight_id, + media->sessionMeta.flight_id) != 0) + fileSessionMeta.flight_id[0] = '\0'; + + if (strcmp(fileSessionMeta.custom_id, + media->sessionMeta.custom_id) != 0) + fileSessionMeta.custom_id[0] = '\0'; + + if (memcmp(&fileSessionMeta.takeoff_loc, + &media->sessionMeta.takeoff_loc, + sizeof(struct vmeta_location)) != 0) + fileSessionMeta.takeoff_loc.valid = 0; + + if (memcmp(&fileSessionMeta.picture_fov, + &media->sessionMeta.picture_fov, + sizeof(struct vmeta_fov)) != 0) { + fileSessionMeta.picture_fov.has_horz = 0; + fileSessionMeta.picture_fov.has_vert = 0; + } + + if ((fileSessionMeta.has_thermal != + media->sessionMeta.has_thermal) || + (memcmp(&fileSessionMeta.thermal, + &media->sessionMeta.thermal, + sizeof(struct vmeta_thermal)) != 0)) + fileSessionMeta.has_thermal = 0; + + if (fileSessionMeta.camera_type != + media->sessionMeta.camera_type) + fileSessionMeta.camera_type = VMETA_CAMERA_TYPE_UNKNOWN; + + if (fileSessionMeta.video_mode != media->sessionMeta.video_mode) + fileSessionMeta.video_mode = VMETA_VIDEO_MODE_UNKNOWN; + } + + /* Second pass: cancel identical values from trackSessionMeta and + * write track-level metadata */ + for (auto it = mTracks.begin(); it != mTracks.end(); it++) { + CodedVideoMedia *media = it->first; + Track &track = it->second; + struct vmeta_session trackSessionMeta = media->sessionMeta; + + if (fileSessionMeta.friendly_name[0] != '\0') + trackSessionMeta.friendly_name[0] = '\0'; + + if (fileSessionMeta.maker[0] != '\0') + trackSessionMeta.maker[0] = '\0'; + + if (fileSessionMeta.model[0] != '\0') + trackSessionMeta.model[0] = '\0'; + + if (fileSessionMeta.model_id[0] != '\0') + trackSessionMeta.model_id[0] = '\0'; + + if (fileSessionMeta.serial_number[0] != '\0') + trackSessionMeta.serial_number[0] = '\0'; + + if (fileSessionMeta.software_version[0] != '\0') + trackSessionMeta.software_version[0] = '\0'; + + if (fileSessionMeta.build_id[0] != '\0') + trackSessionMeta.build_id[0] = '\0'; + + if (fileSessionMeta.title[0] != '\0') + trackSessionMeta.title[0] = '\0'; + + if (fileSessionMeta.comment[0] != '\0') + trackSessionMeta.comment[0] = '\0'; + + if (fileSessionMeta.copyright[0] != '\0') + trackSessionMeta.copyright[0] = '\0'; + + if (fileSessionMeta.media_date != 0) { + trackSessionMeta.media_date = 0; + trackSessionMeta.media_date_gmtoff = 0; + } + + if (fileSessionMeta.run_date != 0) { + trackSessionMeta.run_date = 0; + trackSessionMeta.run_date_gmtoff = 0; + } + + if (fileSessionMeta.run_id[0] != '\0') + trackSessionMeta.run_id[0] = '\0'; + + if (fileSessionMeta.boot_id[0] != '\0') + trackSessionMeta.boot_id[0] = '\0'; + + if (fileSessionMeta.flight_id[0] != '\0') + trackSessionMeta.flight_id[0] = '\0'; + + if (fileSessionMeta.custom_id[0] != '\0') + trackSessionMeta.custom_id[0] = '\0'; + + if (fileSessionMeta.takeoff_loc.valid) + trackSessionMeta.takeoff_loc.valid = 0; + + if (fileSessionMeta.picture_fov.has_horz || + fileSessionMeta.picture_fov.has_vert) { + trackSessionMeta.picture_fov.has_horz = 0; + trackSessionMeta.picture_fov.has_vert = 0; + } + + if (fileSessionMeta.has_thermal) + trackSessionMeta.has_thermal = 0; + + if (fileSessionMeta.camera_type != VMETA_CAMERA_TYPE_UNKNOWN) { + trackSessionMeta.camera_type = + VMETA_CAMERA_TYPE_UNKNOWN; + } + + if (fileSessionMeta.video_mode != VMETA_VIDEO_MODE_UNKNOWN) + trackSessionMeta.video_mode = VMETA_VIDEO_MODE_UNKNOWN; + + struct SessionMetaWriteTrackCbUserdata ud = { + .muxer = this, + .trackId = track.trackId, + }; + err = vmeta_session_recording_write( + &trackSessionMeta, &sessionMetaWriteTrackCb, &ud); + if (err < 0) + PDRAW_LOG_ERRNO("vmeta_session_recording_write", -err); + } + + if (fileSessionMeta.media_date == 0) { + uint64_t mediaDate = 0; + int32_t mediaDateGmtoff = 0; + err = time_local_get(&mediaDate, &mediaDateGmtoff); + if (err < 0) + PDRAW_LOG_ERRNO("time_local_get", -err); + fileSessionMeta.media_date = mediaDate; + fileSessionMeta.media_date_gmtoff = mediaDateGmtoff; + } + if (fileSessionMeta.title[0] == '\0') { + err = time_local_format(fileSessionMeta.media_date, + fileSessionMeta.media_date_gmtoff, + TIME_FMT_RFC1123, + fileSessionMeta.title, + sizeof(fileSessionMeta.title)); + if (err < 0) + PDRAW_LOG_ERRNO("time_local_format", -err); + } + + err = vmeta_session_recording_write( + &fileSessionMeta, &sessionMetaWriteFileCb, this); + if (err < 0) + PDRAW_LOG_ERRNO("vmeta_session_recording_write", -err); +} + + +int RecordMuxer::addTrackForMedia(CodedVideoMedia *media, uint64_t trackTime) +{ + int res, trackId; + const uint8_t *vps = nullptr, *sps = nullptr, *pps = nullptr; + size_t vpsSize = 0, spsSize = 0, ppsSize = 0; + struct mp4_video_decoder_config cfg = {}; + + PDRAW_LOG_ERRNO_RETURN_ERR_IF(mMux == nullptr, EAGAIN); + + if (trackTime == 0) + trackTime = time(nullptr); + + /* Add a new video track */ + struct mp4_mux_track_params params = { + .type = MP4_TRACK_TYPE_VIDEO, + .name = "DefaultVideo", /* TODO */ + .enabled = 1, /* TODO */ + .in_movie = 1, /* TODO */ + .in_preview = 1, /* TODO */ + .timescale = MP4_TIMESCALE, /* TODO */ + .creation_time = trackTime, + .modification_time = trackTime, + }; + res = mp4_mux_add_track(mMux, ¶ms); + if (res < 0) { + PDRAW_LOG_ERRNO("mp4_mux_add_track", -res); + return res; + } + trackId = res; + mTracks.insert( + {media, Track((uint32_t)trackId, 0, trackTime, INT64_MAX)}); + + switch (media->format.encoding) { + case VDEF_ENCODING_H264: + /* Set the H.264 parameter sets */ + res = media->getPs( + nullptr, nullptr, &sps, &spsSize, &pps, &ppsSize); + if (res < 0) { + PDRAW_LOG_ERRNO("CodedVideoMedia::getPs", -res); + return res; + } + cfg.codec = MP4_VIDEO_CODEC_AVC; + cfg.width = media->info.resolution.width; + cfg.height = media->info.resolution.height; + cfg.avc.c_sps = sps; + cfg.avc.sps_size = spsSize; + cfg.avc.c_pps = pps; + cfg.avc.pps_size = ppsSize; + break; + case VDEF_ENCODING_H265: + /* Set the H.265 parameter sets */ + res = media->getPs( + &vps, &vpsSize, &sps, &spsSize, &pps, &ppsSize); + if (res < 0) { + PDRAW_LOG_ERRNO("CodedVideoMedia::getPs", -res); + return res; + } + cfg.codec = MP4_VIDEO_CODEC_HEVC; + cfg.width = media->info.resolution.width; + cfg.height = media->info.resolution.height; + cfg.hevc.c_vps = vps; + cfg.hevc.vps_size = vpsSize; + cfg.hevc.c_sps = sps; + cfg.hevc.sps_size = spsSize; + cfg.hevc.c_pps = pps; + cfg.hevc.pps_size = ppsSize; + /* TODO: fill cfg.hevc.hvcc_info */ + break; + default: + break; + } + + res = mp4_mux_track_set_video_decoder_config(mMux, trackId, &cfg); + if (res < 0) { + PDRAW_LOG_ERRNO("mp4_mux_track_set_video_decoder_config", -res); + return res; + } + + return 0; +} + + +int RecordMuxer::addMetadataTrack(Track *ref, enum vmeta_frame_type metaType) +{ + int res; + const char *mimeType = nullptr; + const char *contentEncoding = nullptr; + + PDRAW_LOG_ERRNO_RETURN_ERR_IF(mMux == nullptr, EAGAIN); + PDRAW_LOG_ERRNO_RETURN_ERR_IF(mHasMetadataTrack, EALREADY); + + switch (metaType) { + case VMETA_FRAME_TYPE_V2: + mimeType = VMETA_FRAME_V2_MIME_TYPE; + contentEncoding = VMETA_FRAME_V2_CONTENT_ENCODING; + break; + case VMETA_FRAME_TYPE_V3: + mimeType = VMETA_FRAME_V3_MIME_TYPE; + contentEncoding = VMETA_FRAME_V3_CONTENT_ENCODING; + break; + case VMETA_FRAME_TYPE_PROTO: + mimeType = VMETA_FRAME_PROTO_MIME_TYPE; + contentEncoding = VMETA_FRAME_PROTO_CONTENT_ENCODING; + break; + default: + PDRAW_LOGE("%s: unsupported metadata type '%s'", + __func__, + vmeta_frame_type_str(metaType)); + return -ENOSYS; + } + + /* Add a new metadata track */ + struct mp4_mux_track_params params = { + .type = MP4_TRACK_TYPE_METADATA, + .name = "ParrotVideoMetadata", /* TODO */ + .enabled = 1, /* TODO */ + .in_movie = 1, /* TODO */ + .in_preview = 1, /* TODO */ + .timescale = MP4_TIMESCALE, /* TODO */ + .creation_time = ref->trackTime, + .modification_time = ref->trackTime, + }; + res = mp4_mux_add_track(mMux, ¶ms); + if (res < 0) { + PDRAW_LOG_ERRNO("mp4_mux_add_track", -res); + return res; + } + ref->metaTrackId = (uint32_t)res; + + /* Set the metadata mime type */ + res = mp4_mux_track_set_metadata_mime_type( + mMux, ref->metaTrackId, contentEncoding, mimeType); + if (res < 0) { + PDRAW_LOG_ERRNO("mp4_mux_track_set_metadata_mime_type", -res); + ref->metaTrackId = 0; + return res; + } + + /* Add track reference */ + res = mp4_mux_add_ref_to_track(mMux, ref->metaTrackId, ref->trackId); + if (res < 0) { + PDRAW_LOG_ERRNO("mp4_mux_add_ref_to_track", -res); + ref->metaTrackId = 0; + return res; + } + + mHasMetadataTrack = true; + + return 0; +} + + +int RecordMuxer::process(void) +{ + int res, inputMediaCount, i; + + if (mState != STARTED) + return 0; + + PDRAW_LOG_ERRNO_RETURN_ERR_IF(mMux == nullptr, EAGAIN); + + CodedSink::lock(); + + inputMediaCount = getInputMediaCount(); + + for (i = 0; i < inputMediaCount; i++) { + CodedVideoMedia *media = getInputMedia(i); + if (media == nullptr) { + res = -ENOENT; + PDRAW_LOG_ERRNO("getInputMedia", -res); + continue; + } + + /* Process each media */ + processMedia(media); + } + + CodedSink::unlock(); + + return 0; +} + + +int RecordMuxer::processMedia(CodedVideoMedia *media) +{ + int res, err; + struct mbuf_coded_video_frame *frame = nullptr; + Track &track = mTracks[media]; + + if (track.trackId == 0) { + res = -ENOENT; + PDRAW_LOG_ERRNO("track->trackId", -res); + return res; + } + CodedChannel *channel = getInputChannel(media); + if (channel == nullptr) { + res = -ENODEV; + PDRAW_LOG_ERRNO("Sink::getInputChannel", -res); + return res; + } + struct mbuf_coded_video_frame_queue *queue = channel->getQueue(); + if (queue == nullptr) { + res = -ENODEV; + PDRAW_LOG_ERRNO("Channel::getQueue", -res); + return res; + } + + /* TODO: This loops drops frames if the processFrame function fails. + * This is a problem for most coded streams, so the current behavior + * will result in a buggy output file. + * We should instead check what the error was, and decide to either: + * - Retry later, which can be achieved by using queue_peek() instead of + * queue_pop(), or + * - Discard the frame, flush the queue, and ask the upstream elements + * for a complete resync (or mabye even terminate the record and warn + * the app ?) + * The second choice is important to have, because if an error persists, + * then this whole element will be stuck on the buggy frame. */ + do { + res = mbuf_coded_video_frame_queue_pop(queue, &frame); + if (res < 0) { + if (res != -EAGAIN) + PDRAW_LOG_ERRNO( + "mbuf_coded_video_frame_queue_pop", + -res); + continue; + } + + /* Process each buffer */ + res = processFrame(media, &track, frame); + + err = mbuf_coded_video_frame_unref(frame); + if (err < 0) + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_unref", -err); + } while (res == 0); + + return 0; +} + + +int RecordMuxer::processFrame(CodedVideoMedia *media, + Track *track, + struct mbuf_coded_video_frame *frame) +{ + int res; + struct mbuf_ancillary_data *ancillaryData = nullptr; + struct vmeta_frame *metadata = nullptr; + CodedVideoMedia::Frame *meta; + struct mp4_mux_scattered_sample sample = {}; + const void *aData; + const void **frameNalus = nullptr; + size_t *frameNalusSize = nullptr; + + sample.nbuffers = mbuf_coded_video_frame_get_nalu_count(frame); + if (sample.nbuffers < 0) { + res = sample.nbuffers; + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_nalu_count", -res); + goto out; + } + frameNalus = + (const void **)calloc(sample.nbuffers, sizeof(*sample.buffers)); + frameNalusSize = (size_t *)calloc(sample.nbuffers, sizeof(*sample.len)); + if (!frameNalus || !frameNalusSize) { + res = -ENOMEM; + PDRAW_LOG_ERRNO("calloc", -res); + goto out; + } + + for (int i = 0; i < sample.nbuffers; i++) { + struct vdef_nalu nalu; + res = mbuf_coded_video_frame_get_nalu( + frame, i, &frameNalus[i], &nalu); + if (res < 0) { + PDRAW_LOG_ERRNO( + "mbuf_coded_video_frame_get_nalu(%d)", -res, i); + goto out; + } + frameNalusSize[i] = nalu.size; + } + + res = mbuf_coded_video_frame_get_ancillary_data( + frame, + PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME, + &ancillaryData); + if (res < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_ancillary_data", + -res); + goto out; + } + aData = mbuf_ancillary_data_get_buffer(ancillaryData, NULL); + meta = (CodedVideoMedia::Frame *)aData; + + /* Add a video sample to the MP4 muxer */ + sample.buffers = (const uint8_t *const *)frameNalus; + sample.len = (const size_t *)frameNalusSize; + sample.sync = meta->isSync; + sample.dts = + mp4_usec_to_sample_time(meta->ntpRawTimestamp, MP4_TIMESCALE); + + /* Decoding timestamps must be monotonic; avoid duplicate or + * rollback timestamps by faking the timestamp as the previous + * timestamp + 1 */ + if ((track->lastSampleTs != INT64_MAX) && + (sample.dts == track->lastSampleTs)) { + PDRAW_LOGW("duplicate timestamp (%" PRIu64 "), incrementing", + track->lastSampleTs); + sample.dts = track->lastSampleTs + 1; + } else if ((track->lastSampleTs != INT64_MAX) && + (sample.dts < track->lastSampleTs)) { + PDRAW_LOGW("timestamp rollback from %" PRIu64 " to %" PRIu64 + ", incrementing", + track->lastSampleTs, + sample.dts); + sample.dts = track->lastSampleTs + 1; + } + track->lastSampleTs = sample.dts; + + res = mp4_mux_track_add_scattered_sample(mMux, track->trackId, &sample); + if (res < 0) { + PDRAW_LOG_ERRNO("mp4_mux_track_add_scattered_sample", -res); + goto out; + } + + res = mbuf_coded_video_frame_get_metadata(frame, &metadata); + if (res == -ENOENT) { + /* No metadata, skip to the end */ + res = 0; + goto out; + } else if (res < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_metadata", -res); + goto out; + } + if (!mHasMetadataTrack) + addMetadataTrack(track, metadata->type); + if (track->metaTrackId) { + /* Add a metadata sample to the MP4 muxer */ + const uint8_t *metaContent = nullptr; + size_t metaLen = 0; + if (metadata->type != VMETA_FRAME_TYPE_PROTO) { + struct vmeta_buffer metaBuf; + vmeta_buffer_set_data( + &metaBuf, mMetaBuffer, VMETA_FRAME_MAX_SIZE, 0); + res = vmeta_frame_write(&metaBuf, metadata); + if (res < 0) { + PDRAW_LOG_ERRNO("vmeta_frame_write", -res); + goto out; + } + metaContent = metaBuf.data; + metaLen = metaBuf.pos; + } else { + res = vmeta_frame_proto_get_buffer( + metadata, &metaContent, &metaLen); + if (res < 0) { + PDRAW_LOG_ERRNO("vmeta_frame_proto_get_buffer", + -res); + } + } + if ((metaContent != nullptr) && (metaLen > 0)) { + struct mp4_mux_sample metaSample = { + .buffer = metaContent, + .len = metaLen, + .sync = 1, + .dts = sample.dts, + }; + res = mp4_mux_track_add_sample( + mMux, track->metaTrackId, &metaSample); + if (res < 0) { + PDRAW_LOG_ERRNO("mp4_mux_track_add_sample", + -res); + goto out; + } + } + if (metadata->type == VMETA_FRAME_TYPE_PROTO) { + vmeta_frame_proto_release_buffer(metadata, metaContent); + } + } +out: + if (metadata) + vmeta_frame_unref(metadata); + if (ancillaryData) + mbuf_ancillary_data_unref(ancillaryData); + if (frame && frameNalus) { + for (int i = 0; i < sample.nbuffers; i++) { + if (!frameNalus[i]) + continue; + int err = mbuf_coded_video_frame_release_nalu( + frame, i, frameNalus[i]); + if (err < 0) + PDRAW_LOG_ERRNO( + "mbuf_coded_video_frame_release_nalu", + -err); + } + } + free(frameNalus); + free(frameNalusSize); + + return res; +} + + +void RecordMuxer::sessionMetaWriteFileCb(enum vmeta_record_type type, + const char *key, + const char *value, + void *userdata) +{ + int res; + RecordMuxer *self = (RecordMuxer *)userdata; + + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + res = mp4_mux_add_file_metadata(self->mMux, key, value); + if (res < 0) + PDRAW_LOG_ERRNO("mp4_mux_add_file_metadata", -res); +} + + +void RecordMuxer::sessionMetaWriteTrackCb(enum vmeta_record_type type, + const char *key, + const char *value, + void *userdata) +{ + int res; + struct SessionMetaWriteTrackCbUserdata *ud = + (struct SessionMetaWriteTrackCbUserdata *)userdata; + + ULOG_ERRNO_RETURN_IF(ud == nullptr, EINVAL); + + RecordMuxer *self = ud->muxer; + uint32_t trackId = ud->trackId; + + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + res = mp4_mux_add_track_metadata(self->mMux, trackId, key, value); + if (res < 0) + PDRAW_LOG_ERRNO("mp4_mux_add_track_metadata", -res); +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_muxer_record.hpp b/libpdraw/src/pdraw_muxer_record.hpp index 36e5204..d9c92ef 100644 --- a/libpdraw/src/pdraw_muxer_record.hpp +++ b/libpdraw/src/pdraw_muxer_record.hpp @@ -1,118 +1,118 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Record muxer - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_MUXER_RECORD_HPP_ -#define _PDRAW_MUXER_RECORD_HPP_ - -#include "pdraw_muxer.hpp" - -#include -#include - -#include - -namespace Pdraw { - - -class RecordMuxer : public Muxer { -public: - RecordMuxer(Session *session, - Element::Listener *elementListener, - const std::string &fileName); - - ~RecordMuxer(void); - - int addInputMedia(CodedVideoMedia *media) override; - -private: - class Track { - public: - Track(uint32_t trackId, - uint32_t metaTrackId, - uint64_t trackTime, - int64_t lastSampleTs) : - trackId(trackId), - metaTrackId(metaTrackId), trackTime(trackTime), - lastSampleTs(lastSampleTs) - { - } - - Track(void) : Track(0, 0, 0, INT64_MAX) {} - - uint32_t trackId; - uint32_t metaTrackId; - uint64_t trackTime; - int64_t lastSampleTs; - }; - - struct SessionMetaWriteTrackCbUserdata { - RecordMuxer *muxer; - uint32_t trackId; - }; - - int internalStart(void) override; - - int internalStop(void) override; - - void mergeSessionMetadata(void); - - int addTrackForMedia(CodedVideoMedia *media, uint64_t trackTime); - - int addMetadataTrack(Track *ref, enum vmeta_frame_type metaType); - - int process(void) override; - - int processMedia(CodedVideoMedia *media); - - int processFrame(CodedVideoMedia *media, - Track *track, - struct mbuf_coded_video_frame *frame); - - static void sessionMetaWriteFileCb(enum vmeta_record_type type, - const char *key, - const char *value, - void *userdata); - - static void sessionMetaWriteTrackCb(enum vmeta_record_type type, - const char *key, - const char *value, - void *userdata); - - std::string mFileName; - struct mp4_mux *mMux; - time_t mMediaDate; - std::unordered_map mTracks; - bool mHasMetadataTrack; - uint8_t *mMetaBuffer; -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_MUXER_RECORD_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Record muxer + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_MUXER_RECORD_HPP_ +#define _PDRAW_MUXER_RECORD_HPP_ + +#include "pdraw_muxer.hpp" + +#include +#include + +#include + +namespace Pdraw { + + +class RecordMuxer : public Muxer { +public: + RecordMuxer(Session *session, + Element::Listener *elementListener, + const std::string &fileName); + + ~RecordMuxer(void); + + int addInputMedia(CodedVideoMedia *media) override; + +private: + class Track { + public: + Track(uint32_t trackId, + uint32_t metaTrackId, + uint64_t trackTime, + int64_t lastSampleTs) : + trackId(trackId), + metaTrackId(metaTrackId), trackTime(trackTime), + lastSampleTs(lastSampleTs) + { + } + + Track(void) : Track(0, 0, 0, INT64_MAX) {} + + uint32_t trackId; + uint32_t metaTrackId; + uint64_t trackTime; + int64_t lastSampleTs; + }; + + struct SessionMetaWriteTrackCbUserdata { + RecordMuxer *muxer; + uint32_t trackId; + }; + + int internalStart(void) override; + + int internalStop(void) override; + + void mergeSessionMetadata(void); + + int addTrackForMedia(CodedVideoMedia *media, uint64_t trackTime); + + int addMetadataTrack(Track *ref, enum vmeta_frame_type metaType); + + int process(void) override; + + int processMedia(CodedVideoMedia *media); + + int processFrame(CodedVideoMedia *media, + Track *track, + struct mbuf_coded_video_frame *frame); + + static void sessionMetaWriteFileCb(enum vmeta_record_type type, + const char *key, + const char *value, + void *userdata); + + static void sessionMetaWriteTrackCb(enum vmeta_record_type type, + const char *key, + const char *value, + void *userdata); + + std::string mFileName; + struct mp4_mux *mMux; + time_t mMediaDate; + std::unordered_map mTracks; + bool mHasMetadataTrack; + uint8_t *mMetaBuffer; +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_MUXER_RECORD_HPP_ */ diff --git a/libpdraw/src/pdraw_muxer_stream_rtmp.cpp b/libpdraw/src/pdraw_muxer_stream_rtmp.cpp index 3bdf72a..d1cc5f2 100644 --- a/libpdraw/src/pdraw_muxer_stream_rtmp.cpp +++ b/libpdraw/src/pdraw_muxer_stream_rtmp.cpp @@ -1,525 +1,525 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * RTMP stream muxer - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_rtmpmuxer -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_muxer_stream_rtmp.hpp" -#include "pdraw_session.hpp" - -#ifdef BUILD_LIBRTMP - -# include - -# include -# include - -namespace Pdraw { - - -# define NB_SUPPORTED_FORMATS 1 -static struct vdef_coded_format supportedFormats[NB_SUPPORTED_FORMATS]; -static pthread_once_t supportedFormatsIsInit = PTHREAD_ONCE_INIT; -static void initializeSupportedFormats(void) -{ - supportedFormats[0] = vdef_h264_avcc; -} - - -const struct rtmp_callbacks RtmpStreamMuxer::mRtmpCbs = { - .socket_cb = &RtmpStreamMuxer::onSocketCreated, - .connection_state = &RtmpStreamMuxer::connectionStateCb, - .peer_bw_changed = &RtmpStreamMuxer::peerBwChangedCb, - .data_unref = &RtmpStreamMuxer::dataUnrefCb, -}; - -const uint8_t RtmpStreamMuxer::mDummyAudioSpecificConfig[5] = { - 0x12, - 0x10, - 0x56, - 0xe5, - 0x00, -}; - -const uint8_t RtmpStreamMuxer::mDummyAudioSample[6] = { - 0x21, - 0x10, - 0x04, - 0x60, - 0x8c, - 0x1c, -}; - -const int RtmpStreamMuxer::mDummyAudioSampleRate = 44100; - -const int RtmpStreamMuxer::mDummyAudioSampleSize = 16; - - -RtmpStreamMuxer::RtmpStreamMuxer(Session *session, - Element::Listener *elementListener, - const std::string &url) : - Muxer(session, elementListener), - mUrl(url), mDummyAudioTimer(nullptr), mDummyAudioStarted(false), - mRtmpClient(nullptr), mRtmpConnectionState(RTMP_DISCONNECTED), - mConfigured(false), mSynchronized(false), mVideoMedia(nullptr), - mDuration(0.), mWidth(0), mHeight(0), mFramerate(0.), - mAudioSampleRate(mDummyAudioSampleRate), - mAudioSampleSize(mDummyAudioSampleSize), mDummyAudioTimestamp(0) -{ - (void)pthread_once(&supportedFormatsIsInit, initializeSupportedFormats); - - Element::setClassName(__func__); - setCodedVideoMediaFormatCaps(supportedFormats, NB_SUPPORTED_FORMATS); -} - - -RtmpStreamMuxer::~RtmpStreamMuxer(void) -{ - int err; - - err = internalStop(); -} - - -/* Must be called on the loop thread */ -int RtmpStreamMuxer::addInputMedia(CodedVideoMedia *media) -{ - int res; - const uint8_t *sps = nullptr, *pps = nullptr; - size_t spsSize = 0, ppsSize = 0; - unsigned int len = 0; - - /* Only accept the first video media */ - if (mVideoMedia != nullptr) { - PDRAW_LOGE("%s: only 1 video media supported, ignoring", - __func__); - return -EALREADY; - } - - res = Muxer::addInputMedia(media); - if (res < 0) - return res; - - mVideoMedia = media; - mDuration = 0.; /* TODO */ - mWidth = media->info.resolution.width; - mHeight = media->info.resolution.height; - mFramerate = ((media->info.framerate.num != 0) && - (media->info.framerate.den != 0)) - ? (double)media->info.framerate.num / - (double)media->info.framerate.den - : 30.; /* TODO */ - - res = media->getPs(nullptr, nullptr, &sps, &spsSize, &pps, &ppsSize); - if (res < 0) { - PDRAW_LOG_ERRNO("CodedVideoMedia::getPs", -res); - return res; - } - len = spsSize + ppsSize + - 11; /* TODO: the size shoud be retreived from libmp4 */ - mVideoAvcc.resize(len); - res = mp4_generate_avc_decoder_config( - sps, spsSize, pps, ppsSize, mVideoAvcc.data(), &len); - if (res < 0) { - PDRAW_LOG_ERRNO("mp4_generate_avc_decoder_config", -res); - return res; - } - - if (mRtmpConnectionState == RTMP_CONNECTED) { - res = configure(); - if (res < 0) - return res; - } - - return 0; -} - - -int RtmpStreamMuxer::internalStart(void) -{ - int res; - - mDummyAudioTimer = - pomp_timer_new(mSession->getLoop(), fakeAudioTimerCb, this); - if (mDummyAudioTimer == nullptr) { - res = -ENOMEM; - PDRAW_LOG_ERRNO("pomp_timer_new", -res); - return res; - } - - /* Create the RTMP client and connect */ - mRtmpClient = rtmp_client_new(mSession->getLoop(), &mRtmpCbs, this); - if (mRtmpClient == nullptr) { - res = -ENOMEM; - PDRAW_LOG_ERRNO("rtmp_client_new", -res); - return res; - } - res = rtmp_client_connect(mRtmpClient, mUrl.c_str()); - if (res < 0) { - PDRAW_LOG_ERRNO("rtmp_client_connect", -res); - return res; - } - - return 0; -} - - -int RtmpStreamMuxer::internalStop(void) -{ - int err; - - /* Free the dummy audio timer */ - if (mDummyAudioTimer != nullptr) { - err = pomp_timer_clear(mDummyAudioTimer); - if (err < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -err); - err = pomp_timer_destroy(mDummyAudioTimer); - if (err < 0) - PDRAW_LOG_ERRNO("pomp_timer_destroy", -err); - mDummyAudioTimer = nullptr; - } - - /* Free the RTMP client */ - if (mRtmpClient != nullptr) { - if (mRtmpConnectionState != RTMP_DISCONNECTED) { - err = rtmp_client_disconnect(mRtmpClient); - if (err < 0) - PDRAW_LOG_ERRNO("rtmp_client_disconnect", -err); - } - - rtmp_client_destroy(mRtmpClient); - mRtmpClient = nullptr; - } - - mConfigured = false; - - return 0; -} - - -int RtmpStreamMuxer::configure(void) -{ - int res; - - if (mConfigured) - return -EPROTO; - - if (mRtmpClient == nullptr) - return -EPROTO; - - res = rtmp_client_send_metadata(mRtmpClient, - mDuration, - mWidth, - mHeight, - mFramerate, - mAudioSampleRate, - mAudioSampleSize); - if (res < 0) { - PDRAW_LOG_ERRNO("rtmp_client_send_metadata", -res); - return res; - } - - res = rtmp_client_send_video_avcc( - mRtmpClient, mVideoAvcc.data(), mVideoAvcc.size(), nullptr); - if (res < 0) { - PDRAW_LOG_ERRNO("rtmp_client_send_video_avcc", -res); - return res; - } - - res = rtmp_client_send_audio_specific_config( - mRtmpClient, - mDummyAudioSpecificConfig, - sizeof(mDummyAudioSpecificConfig), - nullptr); - if (res < 0) { - PDRAW_LOG_ERRNO("rtmp_client_send_audio_specific_config", -res); - return res; - } - - mConfigured = true; - PDRAW_LOGI("RTMP client configured"); - return 0; -} - - -int RtmpStreamMuxer::process(void) -{ - int res, inputMediaCount, i; - - if (mState != STARTED) - return 0; - - CodedSink::lock(); - - inputMediaCount = getInputMediaCount(); - - for (i = 0; i < inputMediaCount; i++) { - CodedVideoMedia *media = getInputMedia(i); - if (media == nullptr) { - res = -ENOENT; - PDRAW_LOG_ERRNO("getInputMedia", -res); - continue; - } - - /* Process each media */ - processMedia(media); - } - - CodedSink::unlock(); - - return 0; -} - - -int RtmpStreamMuxer::processMedia(CodedVideoMedia *media) -{ - int res, err; - struct mbuf_coded_video_frame *frame; - - CodedChannel *channel = getInputChannel(media); - if (channel == nullptr) { - res = -ENODEV; - PDRAW_LOG_ERRNO("Sink::getInputChannel", -res); - return res; - } - struct mbuf_coded_video_frame_queue *queue = channel->getQueue(); - if (queue == nullptr) { - res = -ENODEV; - PDRAW_LOG_ERRNO("Channel::getQueue", -res); - return res; - } - - /* TODO: This loops drops frames if the processFrame function fails. - * This is a problem for most coded streams, so the current behavior - * will result in a buggy output stream. - * We should instead check what the error was, and decide to either: - * - Retry later, which can be achieved by using queue_peek() instead of - * queue_pop(), or - * - Discard the frame, flush the queue, and ask the upstream elements - * for a complete resync - * The second choice is important to have, because if an error persists, - * then this whole element will be stuck on the buggy frame. */ - do { - res = mbuf_coded_video_frame_queue_pop(queue, &frame); - if (res < 0) { - if (res != -EAGAIN) - PDRAW_LOG_ERRNO( - "mbuf_coded_video_frame_queue_pop", - -res); - continue; - } - - /* Process each buffer */ - if ((mRtmpConnectionState == RTMP_CONNECTED) && (mConfigured)) - res = processFrame(media, frame); - - err = mbuf_coded_video_frame_unref(frame); - if (err < 0) - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_unref", -err); - } while (res == 0); - - return 0; -} - - -int RtmpStreamMuxer::processFrame(CodedVideoMedia *media, - struct mbuf_coded_video_frame *in_frame) -{ - int res; - struct mbuf_coded_video_frame *frame = nullptr; - struct mbuf_ancillary_data *ancillaryData = nullptr; - CodedVideoMedia::Frame *meta; - const void *data = nullptr, *aData; - size_t len; - uint32_t ts; - - res = mbuf_coded_video_frame_get_ancillary_data( - in_frame, - PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME, - &ancillaryData); - if (res < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_ancillary_data", - -res); - goto out; - } - aData = mbuf_ancillary_data_get_buffer(ancillaryData, NULL); - meta = (CodedVideoMedia::Frame *)aData; - - if ((!mSynchronized) && (!meta->isSync)) - goto out; - mSynchronized = true; - - res = mbuf_coded_video_frame_get_packed_buffer(in_frame, &data, &len); - if (res == 0) { - /* Frame is already packed */ - frame = in_frame; - mbuf_coded_video_frame_ref(frame); - } else if (res == -EPROTO) { - /* Frame is not packed, copy & pack it */ - struct mbuf_mem *mem; - res = mbuf_mem_generic_new(len, &mem); - if (res < 0) { - PDRAW_LOG_ERRNO("mbuf_mem_generic_new", -res); - goto out; - } - res = mbuf_coded_video_frame_copy(in_frame, mem, &frame); - if (res < 0) { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_copy", -res); - goto out; - } - res = mbuf_coded_video_frame_get_packed_buffer( - frame, &data, &len); - if (res < 0) { - PDRAW_LOG_ERRNO( - "mbuf_coded_video_frame_get_packed_buffer", - -res); - goto out; - } - } else { - PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_packed_buffer", - -res); - goto out; - } - - ts = (meta->ntpRawTimestamp + 500) / 1000; - - res = rtmp_client_send_video_frame( - mRtmpClient, (const uint8_t *)data, len, ts, frame); - if (res < 0) { - PDRAW_LOG_ERRNO("rtmp_client_send_video_frame", -res); - goto out; - } - /* At this point, the frame will be released by the dataReleaseCb - * function, so we can forget about it */ - frame = nullptr; - - - if (!mDummyAudioStarted) { - mDummyAudioTimestamp = ts; - res = pomp_timer_set(mDummyAudioTimer, 1); - if (res < 0) - PDRAW_LOG_ERRNO("pomp_timer_set", -res); - else - mDummyAudioStarted = true; - } - -out: - if (ancillaryData) - mbuf_ancillary_data_unref(ancillaryData); - if (frame) { - if (data) - mbuf_coded_video_frame_release_packed_buffer(frame, - data); - mbuf_coded_video_frame_unref(frame); - } - return res; -} - - -void RtmpStreamMuxer::fakeAudioTimerCb(struct pomp_timer *timer, void *userdata) -{ - int res; - RtmpStreamMuxer *self = (RtmpStreamMuxer *)userdata; - - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - res = rtmp_client_send_audio_data(self->mRtmpClient, - mDummyAudioSample, - sizeof(mDummyAudioSample), - self->mDummyAudioTimestamp, - nullptr); - if (res < 0) - PDRAW_LOG_ERRNO("rtmp_client_send_audio_data", -res); - - self->mDummyAudioTimestamp += 23; - - res = pomp_timer_set(timer, 23); - if (res < 0) - PDRAW_LOG_ERRNO("pomp_timer_set", -res); -} - - -void RtmpStreamMuxer::onSocketCreated(int fd, void *userdata) -{ - RtmpStreamMuxer *self = reinterpret_cast(userdata); - - self->mSession->socketCreated(fd); -} - - -void RtmpStreamMuxer::connectionStateCb(enum rtmp_connection_state state, - void *userdata) -{ - RtmpStreamMuxer *self = (RtmpStreamMuxer *)userdata; - - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - PDRAW_LOGI("%s: state=%s", - __func__, - rtmp_connection_state_to_string(state)); - self->mRtmpConnectionState = state; - - if ((self->mRtmpConnectionState == RTMP_CONNECTED) && - (!self->mConfigured)) { - self->configure(); - } -} - - -void RtmpStreamMuxer::peerBwChangedCb(uint32_t bandwidth, void *userdata) -{ - RtmpStreamMuxer *self = (RtmpStreamMuxer *)userdata; - - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - - PDRAW_LOGI("%s: peer bandwidth changed to %" PRIu32 " bytes per second", - __func__, - bandwidth); -} - - -void RtmpStreamMuxer::dataUnrefCb(uint8_t *data, - void *buffer_userdata, - void *userdata) -{ - struct mbuf_coded_video_frame *frame = - (struct mbuf_coded_video_frame *)buffer_userdata; - - if (frame != nullptr) { - mbuf_coded_video_frame_release_packed_buffer( - frame, (const void *)data); - mbuf_coded_video_frame_unref(frame); - } -} - -} /* namespace Pdraw */ - -#endif /* BUILD_LIBRTMP */ +/** + * Parrot Drones Awesome Video Viewer Library + * RTMP stream muxer + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_rtmpmuxer +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_muxer_stream_rtmp.hpp" +#include "pdraw_session.hpp" + +#ifdef BUILD_LIBRTMP + +# include + +# include +# include + +namespace Pdraw { + + +# define NB_SUPPORTED_FORMATS 1 +static struct vdef_coded_format supportedFormats[NB_SUPPORTED_FORMATS]; +static pthread_once_t supportedFormatsIsInit = PTHREAD_ONCE_INIT; +static void initializeSupportedFormats(void) +{ + supportedFormats[0] = vdef_h264_avcc; +} + + +const struct rtmp_callbacks RtmpStreamMuxer::mRtmpCbs = { + .socket_cb = &RtmpStreamMuxer::onSocketCreated, + .connection_state = &RtmpStreamMuxer::connectionStateCb, + .peer_bw_changed = &RtmpStreamMuxer::peerBwChangedCb, + .data_unref = &RtmpStreamMuxer::dataUnrefCb, +}; + +const uint8_t RtmpStreamMuxer::mDummyAudioSpecificConfig[5] = { + 0x12, + 0x10, + 0x56, + 0xe5, + 0x00, +}; + +const uint8_t RtmpStreamMuxer::mDummyAudioSample[6] = { + 0x21, + 0x10, + 0x04, + 0x60, + 0x8c, + 0x1c, +}; + +const int RtmpStreamMuxer::mDummyAudioSampleRate = 44100; + +const int RtmpStreamMuxer::mDummyAudioSampleSize = 16; + + +RtmpStreamMuxer::RtmpStreamMuxer(Session *session, + Element::Listener *elementListener, + const std::string &url) : + Muxer(session, elementListener), + mUrl(url), mDummyAudioTimer(nullptr), mDummyAudioStarted(false), + mRtmpClient(nullptr), mRtmpConnectionState(RTMP_DISCONNECTED), + mConfigured(false), mSynchronized(false), mVideoMedia(nullptr), + mDuration(0.), mWidth(0), mHeight(0), mFramerate(0.), + mAudioSampleRate(mDummyAudioSampleRate), + mAudioSampleSize(mDummyAudioSampleSize), mDummyAudioTimestamp(0) +{ + (void)pthread_once(&supportedFormatsIsInit, initializeSupportedFormats); + + Element::setClassName(__func__); + setCodedVideoMediaFormatCaps(supportedFormats, NB_SUPPORTED_FORMATS); +} + + +RtmpStreamMuxer::~RtmpStreamMuxer(void) +{ + int err; + + err = internalStop(); +} + + +/* Must be called on the loop thread */ +int RtmpStreamMuxer::addInputMedia(CodedVideoMedia *media) +{ + int res; + const uint8_t *sps = nullptr, *pps = nullptr; + size_t spsSize = 0, ppsSize = 0; + unsigned int len = 0; + + /* Only accept the first video media */ + if (mVideoMedia != nullptr) { + PDRAW_LOGE("%s: only 1 video media supported, ignoring", + __func__); + return -EALREADY; + } + + res = Muxer::addInputMedia(media); + if (res < 0) + return res; + + mVideoMedia = media; + mDuration = 0.; /* TODO */ + mWidth = media->info.resolution.width; + mHeight = media->info.resolution.height; + mFramerate = ((media->info.framerate.num != 0) && + (media->info.framerate.den != 0)) + ? (double)media->info.framerate.num / + (double)media->info.framerate.den + : 30.; /* TODO */ + + res = media->getPs(nullptr, nullptr, &sps, &spsSize, &pps, &ppsSize); + if (res < 0) { + PDRAW_LOG_ERRNO("CodedVideoMedia::getPs", -res); + return res; + } + len = spsSize + ppsSize + + 11; /* TODO: the size shoud be retreived from libmp4 */ + mVideoAvcc.resize(len); + res = mp4_generate_avc_decoder_config( + sps, spsSize, pps, ppsSize, mVideoAvcc.data(), &len); + if (res < 0) { + PDRAW_LOG_ERRNO("mp4_generate_avc_decoder_config", -res); + return res; + } + + if (mRtmpConnectionState == RTMP_CONNECTED) { + res = configure(); + if (res < 0) + return res; + } + + return 0; +} + + +int RtmpStreamMuxer::internalStart(void) +{ + int res; + + mDummyAudioTimer = + pomp_timer_new(mSession->getLoop(), fakeAudioTimerCb, this); + if (mDummyAudioTimer == nullptr) { + res = -ENOMEM; + PDRAW_LOG_ERRNO("pomp_timer_new", -res); + return res; + } + + /* Create the RTMP client and connect */ + mRtmpClient = rtmp_client_new(mSession->getLoop(), &mRtmpCbs, this); + if (mRtmpClient == nullptr) { + res = -ENOMEM; + PDRAW_LOG_ERRNO("rtmp_client_new", -res); + return res; + } + res = rtmp_client_connect(mRtmpClient, mUrl.c_str()); + if (res < 0) { + PDRAW_LOG_ERRNO("rtmp_client_connect", -res); + return res; + } + + return 0; +} + + +int RtmpStreamMuxer::internalStop(void) +{ + int err; + + /* Free the dummy audio timer */ + if (mDummyAudioTimer != nullptr) { + err = pomp_timer_clear(mDummyAudioTimer); + if (err < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -err); + err = pomp_timer_destroy(mDummyAudioTimer); + if (err < 0) + PDRAW_LOG_ERRNO("pomp_timer_destroy", -err); + mDummyAudioTimer = nullptr; + } + + /* Free the RTMP client */ + if (mRtmpClient != nullptr) { + if (mRtmpConnectionState != RTMP_DISCONNECTED) { + err = rtmp_client_disconnect(mRtmpClient); + if (err < 0) + PDRAW_LOG_ERRNO("rtmp_client_disconnect", -err); + } + + rtmp_client_destroy(mRtmpClient); + mRtmpClient = nullptr; + } + + mConfigured = false; + + return 0; +} + + +int RtmpStreamMuxer::configure(void) +{ + int res; + + if (mConfigured) + return -EPROTO; + + if (mRtmpClient == nullptr) + return -EPROTO; + + res = rtmp_client_send_metadata(mRtmpClient, + mDuration, + mWidth, + mHeight, + mFramerate, + mAudioSampleRate, + mAudioSampleSize); + if (res < 0) { + PDRAW_LOG_ERRNO("rtmp_client_send_metadata", -res); + return res; + } + + res = rtmp_client_send_video_avcc( + mRtmpClient, mVideoAvcc.data(), mVideoAvcc.size(), nullptr); + if (res < 0) { + PDRAW_LOG_ERRNO("rtmp_client_send_video_avcc", -res); + return res; + } + + res = rtmp_client_send_audio_specific_config( + mRtmpClient, + mDummyAudioSpecificConfig, + sizeof(mDummyAudioSpecificConfig), + nullptr); + if (res < 0) { + PDRAW_LOG_ERRNO("rtmp_client_send_audio_specific_config", -res); + return res; + } + + mConfigured = true; + PDRAW_LOGI("RTMP client configured"); + return 0; +} + + +int RtmpStreamMuxer::process(void) +{ + int res, inputMediaCount, i; + + if (mState != STARTED) + return 0; + + CodedSink::lock(); + + inputMediaCount = getInputMediaCount(); + + for (i = 0; i < inputMediaCount; i++) { + CodedVideoMedia *media = getInputMedia(i); + if (media == nullptr) { + res = -ENOENT; + PDRAW_LOG_ERRNO("getInputMedia", -res); + continue; + } + + /* Process each media */ + processMedia(media); + } + + CodedSink::unlock(); + + return 0; +} + + +int RtmpStreamMuxer::processMedia(CodedVideoMedia *media) +{ + int res, err; + struct mbuf_coded_video_frame *frame; + + CodedChannel *channel = getInputChannel(media); + if (channel == nullptr) { + res = -ENODEV; + PDRAW_LOG_ERRNO("Sink::getInputChannel", -res); + return res; + } + struct mbuf_coded_video_frame_queue *queue = channel->getQueue(); + if (queue == nullptr) { + res = -ENODEV; + PDRAW_LOG_ERRNO("Channel::getQueue", -res); + return res; + } + + /* TODO: This loops drops frames if the processFrame function fails. + * This is a problem for most coded streams, so the current behavior + * will result in a buggy output stream. + * We should instead check what the error was, and decide to either: + * - Retry later, which can be achieved by using queue_peek() instead of + * queue_pop(), or + * - Discard the frame, flush the queue, and ask the upstream elements + * for a complete resync + * The second choice is important to have, because if an error persists, + * then this whole element will be stuck on the buggy frame. */ + do { + res = mbuf_coded_video_frame_queue_pop(queue, &frame); + if (res < 0) { + if (res != -EAGAIN) + PDRAW_LOG_ERRNO( + "mbuf_coded_video_frame_queue_pop", + -res); + continue; + } + + /* Process each buffer */ + if ((mRtmpConnectionState == RTMP_CONNECTED) && (mConfigured)) + res = processFrame(media, frame); + + err = mbuf_coded_video_frame_unref(frame); + if (err < 0) + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_unref", -err); + } while (res == 0); + + return 0; +} + + +int RtmpStreamMuxer::processFrame(CodedVideoMedia *media, + struct mbuf_coded_video_frame *in_frame) +{ + int res; + struct mbuf_coded_video_frame *frame = nullptr; + struct mbuf_ancillary_data *ancillaryData = nullptr; + CodedVideoMedia::Frame *meta; + const void *data = nullptr, *aData; + size_t len; + uint32_t ts; + + res = mbuf_coded_video_frame_get_ancillary_data( + in_frame, + PDRAW_ANCILLARY_DATA_KEY_CODEDVIDEOFRAME, + &ancillaryData); + if (res < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_ancillary_data", + -res); + goto out; + } + aData = mbuf_ancillary_data_get_buffer(ancillaryData, NULL); + meta = (CodedVideoMedia::Frame *)aData; + + if ((!mSynchronized) && (!meta->isSync)) + goto out; + mSynchronized = true; + + res = mbuf_coded_video_frame_get_packed_buffer(in_frame, &data, &len); + if (res == 0) { + /* Frame is already packed */ + frame = in_frame; + mbuf_coded_video_frame_ref(frame); + } else if (res == -EPROTO) { + /* Frame is not packed, copy & pack it */ + struct mbuf_mem *mem; + res = mbuf_mem_generic_new(len, &mem); + if (res < 0) { + PDRAW_LOG_ERRNO("mbuf_mem_generic_new", -res); + goto out; + } + res = mbuf_coded_video_frame_copy(in_frame, mem, &frame); + if (res < 0) { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_copy", -res); + goto out; + } + res = mbuf_coded_video_frame_get_packed_buffer( + frame, &data, &len); + if (res < 0) { + PDRAW_LOG_ERRNO( + "mbuf_coded_video_frame_get_packed_buffer", + -res); + goto out; + } + } else { + PDRAW_LOG_ERRNO("mbuf_coded_video_frame_get_packed_buffer", + -res); + goto out; + } + + ts = (meta->ntpRawTimestamp + 500) / 1000; + + res = rtmp_client_send_video_frame( + mRtmpClient, (const uint8_t *)data, len, ts, frame); + if (res < 0) { + PDRAW_LOG_ERRNO("rtmp_client_send_video_frame", -res); + goto out; + } + /* At this point, the frame will be released by the dataReleaseCb + * function, so we can forget about it */ + frame = nullptr; + + + if (!mDummyAudioStarted) { + mDummyAudioTimestamp = ts; + res = pomp_timer_set(mDummyAudioTimer, 1); + if (res < 0) + PDRAW_LOG_ERRNO("pomp_timer_set", -res); + else + mDummyAudioStarted = true; + } + +out: + if (ancillaryData) + mbuf_ancillary_data_unref(ancillaryData); + if (frame) { + if (data) + mbuf_coded_video_frame_release_packed_buffer(frame, + data); + mbuf_coded_video_frame_unref(frame); + } + return res; +} + + +void RtmpStreamMuxer::fakeAudioTimerCb(struct pomp_timer *timer, void *userdata) +{ + int res; + RtmpStreamMuxer *self = (RtmpStreamMuxer *)userdata; + + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + res = rtmp_client_send_audio_data(self->mRtmpClient, + mDummyAudioSample, + sizeof(mDummyAudioSample), + self->mDummyAudioTimestamp, + nullptr); + if (res < 0) + PDRAW_LOG_ERRNO("rtmp_client_send_audio_data", -res); + + self->mDummyAudioTimestamp += 23; + + res = pomp_timer_set(timer, 23); + if (res < 0) + PDRAW_LOG_ERRNO("pomp_timer_set", -res); +} + + +void RtmpStreamMuxer::onSocketCreated(int fd, void *userdata) +{ + RtmpStreamMuxer *self = reinterpret_cast(userdata); + + self->mSession->socketCreated(fd); +} + + +void RtmpStreamMuxer::connectionStateCb(enum rtmp_connection_state state, + void *userdata) +{ + RtmpStreamMuxer *self = (RtmpStreamMuxer *)userdata; + + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + PDRAW_LOGI("%s: state=%s", + __func__, + rtmp_connection_state_to_string(state)); + self->mRtmpConnectionState = state; + + if ((self->mRtmpConnectionState == RTMP_CONNECTED) && + (!self->mConfigured)) { + self->configure(); + } +} + + +void RtmpStreamMuxer::peerBwChangedCb(uint32_t bandwidth, void *userdata) +{ + RtmpStreamMuxer *self = (RtmpStreamMuxer *)userdata; + + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + + PDRAW_LOGI("%s: peer bandwidth changed to %" PRIu32 " bytes per second", + __func__, + bandwidth); +} + + +void RtmpStreamMuxer::dataUnrefCb(uint8_t *data, + void *buffer_userdata, + void *userdata) +{ + struct mbuf_coded_video_frame *frame = + (struct mbuf_coded_video_frame *)buffer_userdata; + + if (frame != nullptr) { + mbuf_coded_video_frame_release_packed_buffer( + frame, (const void *)data); + mbuf_coded_video_frame_unref(frame); + } +} + +} /* namespace Pdraw */ + +#endif /* BUILD_LIBRTMP */ diff --git a/libpdraw/src/pdraw_muxer_stream_rtmp.hpp b/libpdraw/src/pdraw_muxer_stream_rtmp.hpp index be79183..b7295f3 100644 --- a/libpdraw/src/pdraw_muxer_stream_rtmp.hpp +++ b/libpdraw/src/pdraw_muxer_stream_rtmp.hpp @@ -1,109 +1,109 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * RTMP stream muxer - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_MUXER_STREAM_RTMP_HPP_ -#define _PDRAW_MUXER_STREAM_RTMP_HPP_ - -#ifdef BUILD_LIBRTMP - -# include "pdraw_muxer.hpp" - -# include - -# include - -namespace Pdraw { - - -class RtmpStreamMuxer : public Muxer { -public: - RtmpStreamMuxer(Session *session, - Element::Listener *elementListener, - const std::string &url); - - ~RtmpStreamMuxer(void); - - int addInputMedia(CodedVideoMedia *media) override; - -private: - int internalStart(void) override; - - int internalStop(void) override; - - int configure(void); - - int process(void) override; - - int processMedia(CodedVideoMedia *media); - - int processFrame(CodedVideoMedia *media, - struct mbuf_coded_video_frame *frame); - - static void fakeAudioTimerCb(struct pomp_timer *timer, void *userdata); - - static void onSocketCreated(int fd, void *userdata); - - static void connectionStateCb(enum rtmp_connection_state state, - void *userdata); - - static void peerBwChangedCb(uint32_t bandwidth, void *userdata); - - static void - dataUnrefCb(uint8_t *data, void *buffer_userdata, void *userdata); - - std::string mUrl; - struct pomp_timer *mDummyAudioTimer; - bool mDummyAudioStarted; - struct rtmp_client *mRtmpClient; - enum rtmp_connection_state mRtmpConnectionState; - bool mConfigured; - bool mSynchronized; - CodedVideoMedia *mVideoMedia; - double mDuration; - int mWidth; - int mHeight; - double mFramerate; - int mAudioSampleRate; - int mAudioSampleSize; - uint32_t mDummyAudioTimestamp; - std::vector mVideoAvcc; - - static const struct rtmp_callbacks mRtmpCbs; - static const uint8_t mDummyAudioSpecificConfig[5]; - static const uint8_t mDummyAudioSample[6]; - static const int mDummyAudioSampleRate; - static const int mDummyAudioSampleSize; -}; - -} /* namespace Pdraw */ - -#endif /* BUILD_LIBRTMP */ - -#endif /* !_PDRAW_MUXER_STREAM_RTMP_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * RTMP stream muxer + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_MUXER_STREAM_RTMP_HPP_ +#define _PDRAW_MUXER_STREAM_RTMP_HPP_ + +#ifdef BUILD_LIBRTMP + +# include "pdraw_muxer.hpp" + +# include + +# include + +namespace Pdraw { + + +class RtmpStreamMuxer : public Muxer { +public: + RtmpStreamMuxer(Session *session, + Element::Listener *elementListener, + const std::string &url); + + ~RtmpStreamMuxer(void); + + int addInputMedia(CodedVideoMedia *media) override; + +private: + int internalStart(void) override; + + int internalStop(void) override; + + int configure(void); + + int process(void) override; + + int processMedia(CodedVideoMedia *media); + + int processFrame(CodedVideoMedia *media, + struct mbuf_coded_video_frame *frame); + + static void fakeAudioTimerCb(struct pomp_timer *timer, void *userdata); + + static void onSocketCreated(int fd, void *userdata); + + static void connectionStateCb(enum rtmp_connection_state state, + void *userdata); + + static void peerBwChangedCb(uint32_t bandwidth, void *userdata); + + static void + dataUnrefCb(uint8_t *data, void *buffer_userdata, void *userdata); + + std::string mUrl; + struct pomp_timer *mDummyAudioTimer; + bool mDummyAudioStarted; + struct rtmp_client *mRtmpClient; + enum rtmp_connection_state mRtmpConnectionState; + bool mConfigured; + bool mSynchronized; + CodedVideoMedia *mVideoMedia; + double mDuration; + int mWidth; + int mHeight; + double mFramerate; + int mAudioSampleRate; + int mAudioSampleSize; + uint32_t mDummyAudioTimestamp; + std::vector mVideoAvcc; + + static const struct rtmp_callbacks mRtmpCbs; + static const uint8_t mDummyAudioSpecificConfig[5]; + static const uint8_t mDummyAudioSample[6]; + static const int mDummyAudioSampleRate; + static const int mDummyAudioSampleSize; +}; + +} /* namespace Pdraw */ + +#endif /* BUILD_LIBRTMP */ + +#endif /* !_PDRAW_MUXER_STREAM_RTMP_HPP_ */ diff --git a/libpdraw/src/pdraw_renderer.cpp b/libpdraw/src/pdraw_renderer.cpp index 53f3d78..f87759f 100644 --- a/libpdraw/src/pdraw_renderer.cpp +++ b/libpdraw/src/pdraw_renderer.cpp @@ -1,156 +1,156 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Renderer interface - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_rndvid -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_renderer.hpp" -#include "pdraw_renderer_gles2.hpp" -#include "pdraw_renderer_videocoreegl.hpp" -#include "pdraw_session.hpp" - -#include - -namespace Pdraw { - -Renderer *Renderer::create(Session *session, - Element::Listener *listener, - IPdraw::IVideoRenderer *renderer, - IPdraw::IVideoRenderer::Listener *rndListener, - unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - struct egl_display *eglDisplay) -{ -#if defined(USE_VIDEOCOREEGL) - return new VideoCoreEglRenderer(session, - listener, - renderer, - rndListener, - mediaId, - renderPos, - params, - eglDisplay); -#elif defined(USE_GLES2) - return new Gles2Renderer(session, - listener, - renderer, - rndListener, - mediaId, - renderPos, - params, - eglDisplay); -#else - return nullptr; -#endif -} - - -Renderer::Renderer(Session *session, - Element::Listener *listener, - IPdraw::IVideoRenderer *renderer, - IPdraw::IVideoRenderer::Listener *rndListener, - uint32_t mediaTypeCaps, - const struct vdef_raw_format *rawVideoMediaFormatCaps, - int rawVideoMediaFormatCapsCount, - unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - struct egl_display *eglDisplay) : - RawSinkElement(session, - listener, - 1, - rawVideoMediaFormatCaps, - rawVideoMediaFormatCapsCount), - mRenderer(renderer), mRendererListener(rndListener) -{ - int res; - pthread_mutexattr_t attr; - - res = pthread_mutexattr_init(&attr); - if (res != 0) { - PDRAW_LOG_ERRNO("pthread_mutexattr_init", res); - return; - } - - res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - if (res != 0) { - PDRAW_LOG_ERRNO("pthread_mutexattr_settype", res); - goto exit; - } - - res = pthread_mutex_init(&mListenerMutex, &attr); - if (res != 0) { - PDRAW_LOG_ERRNO("pthread_mutex_init", res); - goto exit; - } - -exit: - pthread_mutexattr_destroy(&attr); - return; -} - - -Renderer::~Renderer(void) -{ - /* Remove any leftover idle callbacks */ - pomp_loop_idle_remove(mSession->getLoop(), idleCompleteStop, this); - - pthread_mutex_destroy(&mListenerMutex); -} - - -void Renderer::removeRendererListener(void) -{ - pthread_mutex_lock(&mListenerMutex); - mRendererListener = nullptr; - pthread_mutex_unlock(&mListenerMutex); -} - - -void Renderer::asyncCompleteStop(void) -{ - pomp_loop_idle_add(mSession->getLoop(), idleCompleteStop, this); -} - - -/** - * Renderer calls from idle functions on the loop thread - */ - -void Renderer::idleCompleteStop(void *userdata) -{ - Renderer *self = reinterpret_cast(userdata); - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - self->completeStop(); -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Renderer interface + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_rndvid +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_renderer.hpp" +#include "pdraw_renderer_gles2.hpp" +#include "pdraw_renderer_videocoreegl.hpp" +#include "pdraw_session.hpp" + +#include + +namespace Pdraw { + +Renderer *Renderer::create(Session *session, + Element::Listener *listener, + IPdraw::IVideoRenderer *renderer, + IPdraw::IVideoRenderer::Listener *rndListener, + unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + struct egl_display *eglDisplay) +{ +#if defined(USE_VIDEOCOREEGL) + return new VideoCoreEglRenderer(session, + listener, + renderer, + rndListener, + mediaId, + renderPos, + params, + eglDisplay); +#elif defined(USE_GLES2) + return new Gles2Renderer(session, + listener, + renderer, + rndListener, + mediaId, + renderPos, + params, + eglDisplay); +#else + return nullptr; +#endif +} + + +Renderer::Renderer(Session *session, + Element::Listener *listener, + IPdraw::IVideoRenderer *renderer, + IPdraw::IVideoRenderer::Listener *rndListener, + uint32_t mediaTypeCaps, + const struct vdef_raw_format *rawVideoMediaFormatCaps, + int rawVideoMediaFormatCapsCount, + unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + struct egl_display *eglDisplay) : + RawSinkElement(session, + listener, + 1, + rawVideoMediaFormatCaps, + rawVideoMediaFormatCapsCount), + mRenderer(renderer), mRendererListener(rndListener) +{ + int res; + pthread_mutexattr_t attr; + + res = pthread_mutexattr_init(&attr); + if (res != 0) { + PDRAW_LOG_ERRNO("pthread_mutexattr_init", res); + return; + } + + res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + if (res != 0) { + PDRAW_LOG_ERRNO("pthread_mutexattr_settype", res); + goto exit; + } + + res = pthread_mutex_init(&mListenerMutex, &attr); + if (res != 0) { + PDRAW_LOG_ERRNO("pthread_mutex_init", res); + goto exit; + } + +exit: + pthread_mutexattr_destroy(&attr); + return; +} + + +Renderer::~Renderer(void) +{ + /* Remove any leftover idle callbacks */ + pomp_loop_idle_remove(mSession->getLoop(), idleCompleteStop, this); + + pthread_mutex_destroy(&mListenerMutex); +} + + +void Renderer::removeRendererListener(void) +{ + pthread_mutex_lock(&mListenerMutex); + mRendererListener = nullptr; + pthread_mutex_unlock(&mListenerMutex); +} + + +void Renderer::asyncCompleteStop(void) +{ + pomp_loop_idle_add(mSession->getLoop(), idleCompleteStop, this); +} + + +/** + * Renderer calls from idle functions on the loop thread + */ + +void Renderer::idleCompleteStop(void *userdata) +{ + Renderer *self = reinterpret_cast(userdata); + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + self->completeStop(); +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_renderer.hpp b/libpdraw/src/pdraw_renderer.hpp index 7f36352..2f2aaa5 100644 --- a/libpdraw/src/pdraw_renderer.hpp +++ b/libpdraw/src/pdraw_renderer.hpp @@ -1,98 +1,98 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Renderer interface - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_RENDERER_HPP_ -#define _PDRAW_RENDERER_HPP_ - -#include "pdraw_element.hpp" - -#include - -namespace Pdraw { - -class Renderer : public RawSinkElement { -public: - virtual ~Renderer(void); - - virtual int render(struct pdraw_rect *contentPos, - const float *viewMat = nullptr, - const float *projMat = nullptr) = 0; - - virtual int resize(const struct pdraw_rect *renderPos) = 0; - - virtual int setMediaId(unsigned int mediaId) = 0; - - virtual unsigned int getMediaId(void) = 0; - - virtual int - setParams(const struct pdraw_video_renderer_params *params) = 0; - - virtual int getParams(struct pdraw_video_renderer_params *params) = 0; - - virtual void completeStop(void) = 0; - - static Renderer * - create(Session *session, - Element::Listener *listener, - IPdraw::IVideoRenderer *renderer, - IPdraw::IVideoRenderer::Listener *rndListener, - unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - struct egl_display *eglDisplay); - -protected: - Renderer(Session *session, - Element::Listener *listener, - IPdraw::IVideoRenderer *renderer, - IPdraw::IVideoRenderer::Listener *rndListener, - uint32_t mediaTypeCaps, - const struct vdef_raw_format *rawVideoMediaFormatCaps, - int rawVideoMediaFormatCapsCount, - unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - struct egl_display *eglDisplay); - - void removeRendererListener(void); - - void asyncCompleteStop(void); - - IPdraw::IVideoRenderer *mRenderer; - IPdraw::IVideoRenderer::Listener *mRendererListener; - pthread_mutex_t mListenerMutex; - -private: - static void idleCompleteStop(void *userdata); -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_RENDERER_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Renderer interface + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_RENDERER_HPP_ +#define _PDRAW_RENDERER_HPP_ + +#include "pdraw_element.hpp" + +#include + +namespace Pdraw { + +class Renderer : public RawSinkElement { +public: + virtual ~Renderer(void); + + virtual int render(struct pdraw_rect *contentPos, + const float *viewMat = nullptr, + const float *projMat = nullptr) = 0; + + virtual int resize(const struct pdraw_rect *renderPos) = 0; + + virtual int setMediaId(unsigned int mediaId) = 0; + + virtual unsigned int getMediaId(void) = 0; + + virtual int + setParams(const struct pdraw_video_renderer_params *params) = 0; + + virtual int getParams(struct pdraw_video_renderer_params *params) = 0; + + virtual void completeStop(void) = 0; + + static Renderer * + create(Session *session, + Element::Listener *listener, + IPdraw::IVideoRenderer *renderer, + IPdraw::IVideoRenderer::Listener *rndListener, + unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + struct egl_display *eglDisplay); + +protected: + Renderer(Session *session, + Element::Listener *listener, + IPdraw::IVideoRenderer *renderer, + IPdraw::IVideoRenderer::Listener *rndListener, + uint32_t mediaTypeCaps, + const struct vdef_raw_format *rawVideoMediaFormatCaps, + int rawVideoMediaFormatCapsCount, + unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + struct egl_display *eglDisplay); + + void removeRendererListener(void); + + void asyncCompleteStop(void); + + IPdraw::IVideoRenderer *mRenderer; + IPdraw::IVideoRenderer::Listener *mRendererListener; + pthread_mutex_t mListenerMutex; + +private: + static void idleCompleteStop(void *userdata); +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_RENDERER_HPP_ */ diff --git a/libpdraw/src/pdraw_renderer_gles2.cpp b/libpdraw/src/pdraw_renderer_gles2.cpp index f81a433..e4bdfda 100644 --- a/libpdraw/src/pdraw_renderer_gles2.cpp +++ b/libpdraw/src/pdraw_renderer_gles2.cpp @@ -1,2537 +1,2537 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * OpenGL ES 2.0 renderer - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_rndvidgl -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_renderer_gles2.hpp" -#include "pdraw_session.hpp" -#include "pdraw_settings.hpp" - -#ifdef USE_GLES2 - -# include -# include -# include - -# include - -namespace Pdraw { - -# define GLES2_RENDERER_ANCILLARY_DATA_KEY_INPUT_TIME \ - "pdraw.gles2_renderer.input_time" -# define GLES2_RENDERER_QUEUE_MAX_FRAMES 5 - -# define GLES2_RENDERER_DEFAULT_DELAY_MS 33 -# define GLES2_RENDERER_FADE_FROM_BLACK_DURATION 500000 -# define GLES2_RENDERER_FADE_TO_BLACK_DURATION 500000 -# define GLES2_RENDERER_FADE_TO_BLACK_AND_WHITE_DURATION 2000000 -# define GLES2_RENDERER_FADE_TO_BLACK_AND_WHITE_HOLD 50000 -# define GLES2_RENDERER_FADE_TO_BLUR_DURATION 2000000 -# define GLES2_RENDERER_FADE_TO_BLUR_HOLD 500000 -# define GLES2_RENDERER_FLASH_THEN_BLACK_AND_WHITE_DURATION 200000 -# define GLES2_RENDERER_FLASH_THEN_BLACK_AND_WHITE_HOLD 200000 -# define GLES2_RENDERER_WATCHDOG_TIME_S 2 -# define GLES2_RENDERER_VIDEO_PRES_STATS_TIME_MS 200 -# define GLES2_RENDERER_SCHED_ADAPTIVE_EPSILON_PERCENT 5 - -# define NB_SUPPORTED_FORMATS 4 -static struct vdef_raw_format supportedFormats[NB_SUPPORTED_FORMATS]; -static pthread_once_t supportedFormatsIsInit = PTHREAD_ONCE_INIT; -static void initializeSupportedFormats(void) -{ - supportedFormats[0] = vdef_i420; - supportedFormats[1] = vdef_nv12; - supportedFormats[2] = vdef_i420_10_16le; - supportedFormats[3] = vdef_nv12_10_16le_high; -} - - -/* Called on the rendering thread */ -Gles2Renderer::Gles2Renderer(Session *session, - Element::Listener *listener, - IPdraw::IVideoRenderer *renderer, - IPdraw::IVideoRenderer::Listener *rndListener, - unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - struct egl_display *eglDisplay) : - Renderer(session, - listener, - renderer, - rndListener, - Media::Type::RAW_VIDEO, - nullptr, - 0, - mediaId, - renderPos, - params, - eglDisplay) -{ - int ret; - - Element::setClassName(__func__); - mMediaId = mediaId; - mCurrentMediaId = 0; - mRunning = false; - mCurrentFrame = nullptr; - mCurrentFrameData = {}; - mCurrentFrameInfo = {}; - mCurrentFrameMetadata = nullptr; - mLastAddedMedia = nullptr; - mMediaInfo = {}; - mMediaInfoSessionMeta = {}; - mTimer = nullptr; - mGles2Video = nullptr; - mGles2HmdFirstTexUnit = 0; - mGles2VideoFirstTexUnit = - mGles2HmdFirstTexUnit + Gles2Hmd::getTexUnitCount(); - mGles2Hmd = nullptr; - mDefaultFbo = 0; - mHmdFboSize = 0; - mHmdFbo = 0; - mHmdFboTexture = 0; - mHmdFboRenderBuffer = 0; - mExtLoadFbo = 0; - mExtLoadFboTexture = 0; - mX = 0; - mY = 0; - mWidth = 0; - mHeight = 0; - mPendingTransition = Transition::NONE; - mCurrentTransition = Transition::NONE; - mTransitionStartTime = 0; - mTransitionHoldTime = 0; - memset(&mParams, 0, sizeof(mParams)); - mExtLoadVideoTexture = false; - mExtVideoTextureWidth = 0; - mExtVideoTextureHeight = 0; - mRenderVideoOverlay = false; - mFirstFrame = false; - mFrameLoaded = false; - mLastRenderTimestamp = UINT64_MAX; - mLastFrameTimestamp = UINT64_MAX; - mRenderReadyScheduled = false; - mVideoPresStatsTimer = nullptr; - mSchedLastInputTimestamp = UINT64_MAX; - mSchedLastOutputTimestamp = UINT64_MAX; - mFrameLoadedLogged = false; - mWatchdogTimer = nullptr; - mWatchdogTriggered = false; - - (void)pthread_once(&supportedFormatsIsInit, initializeSupportedFormats); - setRawVideoMediaFormatCaps(supportedFormats, NB_SUPPORTED_FORMATS); - - if (mRendererListener != nullptr) { - ret = mRendererListener->loadVideoTexture( - nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr, 0); - if (ret != -ENOSYS) - mExtLoadVideoTexture = true; - ret = mRendererListener->renderVideoOverlay(nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr); - if (ret != -ENOSYS) - mRenderVideoOverlay = true; - } - - if (renderPos != nullptr && params != nullptr) { - ret = setup(renderPos, params, eglDisplay); - if (ret != 0) - return; - } - - /* Post a message on the loop thread */ - setStateAsyncNotify(CREATED); - return; -} - - -/* Must be called on the loop thread */ -Gles2Renderer::~Gles2Renderer(void) -{ - int ret; - - if (mState == STARTED) - PDRAW_LOGW("renderer is still running"); - - /* Make sure listener function will no longer be called */ - removeRendererListener(); - mExtLoadVideoTexture = false; - mRenderVideoOverlay = false; - - /* Remove any leftover idle callbacks */ - ret = pomp_loop_idle_remove_by_cookie(mSession->getLoop(), this); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_loop_idle_remove_by_cookie", -ret); - - unsigned int count = getInputMediaCount(); - if (count > 0) { - PDRAW_LOGW("not all input media have been removed"); - ret = removeInputMedias(); - if (ret < 0) - PDRAW_LOG_ERRNO("removeInputMedias", -ret); - } - - if (mCurrentFrameMetadata != nullptr) { - vmeta_frame_unref(mCurrentFrameMetadata); - mCurrentFrameMetadata = nullptr; - } - if (mCurrentFrame != nullptr) { - int releaseRet = mbuf_raw_video_frame_unref(mCurrentFrame); - if (releaseRet < 0) - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_unref", - -releaseRet); - mCurrentFrame = nullptr; - } - - Media::cleanupMediaInfo(&mMediaInfo); - - if (mTimer != nullptr) { - ret = pomp_timer_clear(mTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); - ret = pomp_timer_destroy(mTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_destroy", -ret); - mTimer = nullptr; - } - if (mWatchdogTimer != nullptr) { - ret = pomp_timer_clear(mWatchdogTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); - ret = pomp_timer_destroy(mWatchdogTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_destroy", -ret); - mWatchdogTimer = nullptr; - } - if (mVideoPresStatsTimer != nullptr) { - ret = pomp_timer_clear(mVideoPresStatsTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); - ret = pomp_timer_destroy(mVideoPresStatsTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_destroy", -ret); - mVideoPresStatsTimer = nullptr; - } - - if (mGles2Video != nullptr) { - delete mGles2Video; - mGles2Video = nullptr; - } - if (mGles2Hmd != nullptr) { - delete mGles2Hmd; - mGles2Hmd = nullptr; - } -} - - -/* Called on the rendering thread */ -int Gles2Renderer::setup(const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - struct egl_display *eglDisplay) -{ - int ret = 0; - - if (params == nullptr) - return -EINVAL; - if (renderPos == nullptr) - return -EINVAL; - - if ((mState != INVALID) && (mState != CREATED)) { - PDRAW_LOGE("invalid state"); - return -EPROTO; - } - - GLCHK(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mDefaultFbo)); - - ret = resize(renderPos); - if (ret < 0) { - PDRAW_LOG_ERRNO("resize", -ret); - goto out; - } - - ret = setParams(params); - if (ret < 0) { - PDRAW_LOG_ERRNO("setParams", -ret); - goto out; - } - - mGles2Video = new Gles2Video(mSession, - (mParams.enable_hmd_distortion_correction) - ? mHmdFbo - : mDefaultFbo, - mGles2VideoFirstTexUnit); - if (mGles2Video == nullptr) { - ret = -ENOMEM; - PDRAW_LOG_ERRNO("failed to create Gles2Video", -ret); - goto out; - } - mGles2Video->setExtTexture(mExtLoadFboTexture); - -out: - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); - return ret; -} - - -/* Called on the rendering thread */ -int Gles2Renderer::start(void) -{ - if ((mState == STARTED) || (mState == STARTING)) { - return 0; - } - if (mState != CREATED) { - PDRAW_LOGE("renderer is not created"); - return -EPROTO; - } - /* Post a message on the loop thread */ - setStateAsyncNotify(STARTING); - - mRunning = true; - - int ret = pomp_loop_idle_add_with_cookie( - mSession->getLoop(), idleStart, this, this); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_loop_idle_add_with_cookie", -ret); - - return ret; -} - -/* Called on the loop thread by start() */ -void Gles2Renderer::idleStart(void *renderer) -{ - Gles2Renderer *self = reinterpret_cast(renderer); - ULOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - int ret; - - if (self->mState != STARTING) { - PDRAW_LOGE("renderer is not starting"); - return; - } - - if (self->mTimer == nullptr) { - self->mTimer = pomp_timer_new( - self->mSession->getLoop(), timerCb, self); - if (self->mTimer == nullptr) { - PDRAW_LOGE("pomp_timer_new failed"); - goto err; - } - } - if (self->mWatchdogTimer == nullptr) { - self->mWatchdogTimer = pomp_timer_new( - self->mSession->getLoop(), watchdogTimerCb, self); - if (self->mWatchdogTimer == nullptr) { - PDRAW_LOGE("pomp_timer_new failed"); - goto err; - } - } - if (self->mVideoPresStatsTimer == nullptr) { - self->mVideoPresStatsTimer = pomp_timer_new( - self->mSession->getLoop(), videoPresStatsTimerCb, self); - if (self->mVideoPresStatsTimer == nullptr) { - PDRAW_LOGE("pomp_timer_new failed"); - goto err; - } - } - - pthread_mutex_lock(&self->mListenerMutex); - if (self->mRendererListener != nullptr) { - ret = pomp_timer_set(self->mTimer, - GLES2_RENDERER_DEFAULT_DELAY_MS); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_set", -ret); - } - pthread_mutex_unlock(&self->mListenerMutex); - - /* Post a message on the loop thread */ - self->setState(STARTED); - return; - -err: - if (self->mTimer != nullptr) { - ret = pomp_timer_clear(self->mTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); - ret = pomp_timer_destroy(self->mTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_destroy", -ret); - self->mTimer = nullptr; - } - if (self->mWatchdogTimer != nullptr) { - ret = pomp_timer_clear(self->mWatchdogTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); - ret = pomp_timer_destroy(self->mWatchdogTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_destroy", -ret); - self->mWatchdogTimer = nullptr; - } - if (self->mVideoPresStatsTimer != nullptr) { - ret = pomp_timer_clear(self->mVideoPresStatsTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); - ret = pomp_timer_destroy(self->mVideoPresStatsTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_destroy", -ret); - self->mVideoPresStatsTimer = nullptr; - } -} - - -/* Called on the rendering thread */ -int Gles2Renderer::stop(void) -{ - int ret; - - if ((mState == STOPPED) || (mState == STOPPING)) - return 0; - - /* Post a message on the loop thread */ - setStateAsyncNotify(STOPPING); - - mRunning = false; - - removeRendererListener(); - mExtLoadVideoTexture = false; - mRenderVideoOverlay = false; - - if (mGles2Video != nullptr) { - delete mGles2Video; - mGles2Video = nullptr; - } - - ret = stopHmd(); - if (ret < 0) - PDRAW_LOG_ERRNO("stopHmd", -ret); - ret = stopExtLoad(); - if (ret < 0) - PDRAW_LOG_ERRNO("stopExtLoad", -ret); - - /* Remove any leftover idle callbacks */ - ret = pomp_loop_idle_remove_by_cookie(mSession->getLoop(), this); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_loop_idle_remove_by_cookie", -ret); - - /* Post a message on the loop thread */ - asyncCompleteStop(); - - return 0; -} - - -/* Called on the loop thread */ -void Gles2Renderer::completeStop(void) -{ - int ret; - - if (mState == STOPPED) - return; - - if (mTimer != nullptr) { - ret = pomp_timer_clear(mTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); - } - if (mWatchdogTimer != nullptr) { - ret = pomp_timer_clear(mWatchdogTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); - } - if (mVideoPresStatsTimer != nullptr) { - ret = pomp_timer_clear(mVideoPresStatsTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); - } - - ret = removeInputMedias(); - if (ret < 0) - PDRAW_LOG_ERRNO("removeInputMedias", -ret); - - setState(STOPPED); -} - - -/* Called on the loop thread */ -void Gles2Renderer::queueEventCb(struct pomp_evt *evt, void *userdata) -{ - Gles2Renderer *self = (Gles2Renderer *)userdata; - int res = 0; - uint32_t delayMs; - uint64_t delayUs = 0; - - if (self == nullptr) { - PDRAW_LOGE("invalid renderer pointer"); - return; - } - - if ((!self->mRunning) || (self->mState != STARTED)) - return; - - self->RawSink::lock(); - - if (self->mLastAddedMedia == nullptr) { - /* No current media */ - self->RawSink::unlock(); - return; - } - - struct mbuf_raw_video_frame_queue *queue = - self->getLastAddedMediaQueue(); - if (queue == nullptr) { - self->RawSink::unlock(); - return; - } - - uint64_t curTime = 0; - struct timespec ts = {0, 0}; - res = time_get_monotonic(&ts); - if (res < 0) - PDRAW_LOG_ERRNO("time_get_monotonic", -res); - res = time_timespec_to_us(&ts, &curTime); - if (res < 0) - PDRAW_LOG_ERRNO("time_timespec_to_us", -res); - - res = self->getNextFrameDelay(queue, - curTime, - false, - nullptr, - &delayUs, - nullptr, - nullptr, - nullptr, - 0); - if (res < 0) { - PDRAW_LOG_ERRNO("getNextFrameDelay", -res); - self->RawSink::unlock(); - return; - } - - self->RawSink::unlock(); - - pthread_mutex_lock(&self->mListenerMutex); - if (self->mRendererListener && !self->mRenderReadyScheduled) { - delayMs = (delayUs + 500) / 1000; - if (delayMs == 0) { - /* Signal "render ready" now and schedule a renderer - * keepalive timer */ - self->mRendererListener->onVideoRenderReady( - self->mSession, self->mRenderer); - delayMs = GLES2_RENDERER_DEFAULT_DELAY_MS; - } else { - /* Only true when the timer is set by an incoming - * delayed frame and not the renderer keepalive timer */ - self->mRenderReadyScheduled = true; - } - res = pomp_timer_set(self->mTimer, delayMs); - if (res < 0) { - PDRAW_LOG_ERRNO("pomp_timer_set", -res); - self->mRenderReadyScheduled = false; - } - } - pthread_mutex_unlock(&self->mListenerMutex); -} - - -/* Called on the loop thread */ -void Gles2Renderer::timerCb(struct pomp_timer *timer, void *userdata) -{ - Gles2Renderer *self = (Gles2Renderer *)userdata; - int res; - uint32_t delayMs; - uint64_t delayUs = 0; - - if (self == nullptr) { - PDRAW_LOGE("invalid renderer pointer"); - return; - } - - if ((!self->mRunning) || (self->mState != STARTED)) - return; - - self->RawSink::lock(); - - if (self->mLastAddedMedia == nullptr) { - /* No current media */ - self->RawSink::unlock(); - return; - } - - struct mbuf_raw_video_frame_queue *queue = - self->getLastAddedMediaQueue(); - if (queue == nullptr) { - self->RawSink::unlock(); - return; - } - - uint64_t curTime = 0; - struct timespec ts = {0, 0}; - res = time_get_monotonic(&ts); - if (res < 0) - PDRAW_LOG_ERRNO("time_get_monotonic", -res); - res = time_timespec_to_us(&ts, &curTime); - if (res < 0) - PDRAW_LOG_ERRNO("time_timespec_to_us", -res); - - res = self->getNextFrameDelay(queue, - curTime, - false, - nullptr, - &delayUs, - nullptr, - nullptr, - nullptr, - 0); - if ((res < 0) && (res != -EAGAIN)) { - PDRAW_LOG_ERRNO("getNextFrameDelay", -res); - self->RawSink::unlock(); - return; - } - - self->RawSink::unlock(); - - pthread_mutex_lock(&self->mListenerMutex); - if (self->mRendererListener) { - delayMs = (delayUs + 500) / 1000; - self->mRendererListener->onVideoRenderReady(self->mSession, - self->mRenderer); - if ((res == -EAGAIN) || (delayMs == 0)) - delayMs = GLES2_RENDERER_DEFAULT_DELAY_MS; - res = pomp_timer_set(self->mTimer, delayMs); - if (res < 0) - PDRAW_LOG_ERRNO("pomp_timer_set", -res); - } - pthread_mutex_unlock(&self->mListenerMutex); -} - - -/* Called on the loop thread */ -void Gles2Renderer::watchdogTimerCb(struct pomp_timer *timer, void *userdata) -{ - Gles2Renderer *self = (Gles2Renderer *)userdata; - - if ((!self->mRunning) || (self->mState != STARTED)) - return; - - bool expected = false; - if (self->mWatchdogTriggered.compare_exchange_strong(expected, true)) { - PDRAW_LOGW("no new frame for %ds", - GLES2_RENDERER_WATCHDOG_TIME_S); - } -} - - -/* Called on the loop thread */ -void Gles2Renderer::videoPresStatsTimerCb(struct pomp_timer *timer, - void *userdata) -{ - Gles2Renderer *self = (Gles2Renderer *)userdata; - - if ((!self->mRunning) || (self->mState != STARTED)) - return; - - self->RawSink::lock(); - if (self->mLastAddedMedia == nullptr) { - /* No current media */ - self->RawSink::unlock(); - return; - } - - RawChannel *channel = self->getInputChannel(self->mLastAddedMedia); - if (channel == nullptr) { - self->RawSink::unlock(); - PDRAW_LOG_ERRNO("failed to get input port", EPROTO); - return; - } - - int err = channel->sendVideoPresStats(&self->mVideoPresStats); - if (err < 0) - PDRAW_LOG_ERRNO("channel->sendVideoPresStats", -err); - - self->RawSink::unlock(); -} - - -/* Must be called on the loop thread */ -void Gles2Renderer::onChannelFlush(RawChannel *channel) -{ - int ret; - - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - PDRAW_LOGD("flushing input channel"); - - RawSink::lock(); - - struct mbuf_raw_video_frame_queue *queue = channel->getQueue(); - if (queue != nullptr) { - ret = mbuf_raw_video_frame_queue_flush(queue); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_flush", - -ret); - } - if (mCurrentFrame != nullptr) { - int releaseRet = mbuf_raw_video_frame_unref(mCurrentFrame); - if (releaseRet < 0) - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_unref", - -releaseRet); - mCurrentFrame = nullptr; - } - - RawSink::unlock(); - - ret = channel->flushDone(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->flushDone", -ret); -} - - -/* Must be called on the loop thread */ -void Gles2Renderer::onChannelSos(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSink::lock(); - - RawSink::onChannelSos(channel); - if (mParams.enable_transition_flags & - PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_SOS) - mPendingTransition = Transition::FADE_FROM_BLACK; - - RawSink::unlock(); -} - - -/* Must be called on the loop thread */ -void Gles2Renderer::onChannelEos(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSink::lock(); - - RawSink::onChannelEos(channel); - int ret = pomp_timer_clear(mWatchdogTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); - ret = pomp_timer_clear(mVideoPresStatsTimer); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); - if (mParams.enable_transition_flags & - PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_EOS) - mPendingTransition = Transition::FADE_TO_BLACK; - - RawSink::unlock(); -} - - -/* Must be called on the loop thread */ -void Gles2Renderer::onChannelReconfigure(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSink::lock(); - - RawSink::onChannelReconfigure(channel); - if (mParams.enable_transition_flags & - PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_RECONFIGURE) - mPendingTransition = Transition::FADE_TO_BLUR; - - RawSink::unlock(); -} - - -/* Must be called on the loop thread */ -void Gles2Renderer::onChannelTimeout(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSink::lock(); - - RawSink::onChannelTimeout(channel); - if (mParams.enable_transition_flags & - PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_TIMEOUT) - mPendingTransition = Transition::FADE_TO_BLACK_AND_WHITE; - - RawSink::unlock(); -} - - -/* Must be called on the loop thread */ -void Gles2Renderer::onChannelPhotoTrigger(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSink::lock(); - - RawSink::onChannelPhotoTrigger(channel); - if (mParams.enable_transition_flags & - PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_PHOTO_TRIGGER) - mPendingTransition = Transition::FLASH_THEN_BLACK_AND_WHITE; - - RawSink::unlock(); -} - - -bool Gles2Renderer::queueFilter(struct mbuf_raw_video_frame *frame, - void *userdata) -{ - int err; - uint64_t ts_us; - struct timespec cur_ts = {0, 0}; - - /* Set the input time ancillary data to the frame */ - time_get_monotonic(&cur_ts); - time_timespec_to_us(&cur_ts, &ts_us); - err = mbuf_raw_video_frame_add_ancillary_buffer( - frame, - GLES2_RENDERER_ANCILLARY_DATA_KEY_INPUT_TIME, - &ts_us, - sizeof(ts_us)); - if (err < 0) - ULOG_ERRNO("mbuf_raw_video_frame_add_ancillary_buffer", -err); - - return true; -} - - -uint64_t Gles2Renderer::getFrameU64(struct mbuf_raw_video_frame *frame, - const char *key) -{ - int res; - struct mbuf_ancillary_data *data; - uint64_t val = 0; - const void *raw_data; - size_t len; - - res = mbuf_raw_video_frame_get_ancillary_data(frame, key, &data); - if (res < 0) - return 0; - - raw_data = mbuf_ancillary_data_get_buffer(data, &len); - if (!raw_data || len != sizeof(val)) - goto out; - memcpy(&val, raw_data, sizeof(val)); - -out: - mbuf_ancillary_data_unref(data); - return val; -} - - -struct mbuf_raw_video_frame_queue *Gles2Renderer::getLastAddedMediaQueue(void) -{ - RawSink::lock(); - RawChannel *channel = getInputChannel(mLastAddedMedia); - if (channel == nullptr) { - PDRAW_LOGE("failed to get input channel"); - RawSink::unlock(); - return nullptr; - } - struct mbuf_raw_video_frame_queue *queue = channel->getQueue(); - if (queue == nullptr) { - PDRAW_LOGE("failed to get input queue"); - RawSink::unlock(); - return nullptr; - } - RawSink::unlock(); - return queue; -} - - -/* Must be called on the loop thread */ -int Gles2Renderer::addInputMedia(RawVideoMedia *media) -{ - struct pomp_loop *loop = nullptr; - struct pomp_evt *evt = nullptr; - int res = 0; - - if ((mMediaId != 0) && (mMediaId != media->id)) - return -EPERM; - if (mLastAddedMedia != nullptr) - return -EBUSY; - if ((!mRunning) || (mState != STARTED)) - return -EAGAIN; - - RawSink::lock(); - - mFirstFrame = true; - - /* Reset the scheduling */ - mSchedLastInputTimestamp = UINT64_MAX; - mSchedLastOutputTimestamp = UINT64_MAX; - PDRAW_LOGD("RESET SCHEDULING"); - - res = RawSink::addInputMedia(media); - if (res == -EEXIST) { - RawSink::unlock(); - return res; - } else if (res < 0) { - RawSink::unlock(); - PDRAW_LOG_ERRNO("RawSink::addInputMedia", -res); - return res; - } - - RawChannel *channel = getInputChannel(media); - if (channel == nullptr) { - RawSink::unlock(); - PDRAW_LOGE("failed to get channel"); - return -EPROTO; - } - - channel->setKey(this); - struct mbuf_raw_video_frame_queue_args args = {}; - args.filter = &queueFilter; - args.filter_userdata = this; - args.max_frames = GLES2_RENDERER_QUEUE_MAX_FRAMES; - struct mbuf_raw_video_frame_queue *queue; - res = mbuf_raw_video_frame_queue_new_with_args(&args, &queue); - if (res < 0) { - RawSink::unlock(); - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_new_with_args", - -res); - return res; - } - channel->setQueue(queue); - - res = mbuf_raw_video_frame_queue_get_event(queue, &evt); - if (res < 0) { - RawSink::unlock(); - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_get_event", -res); - goto error; - } - - loop = mSession->getLoop(); - if (loop == nullptr) { - RawSink::unlock(); - PDRAW_LOGE("loop not found"); - res = -ENODEV; - goto error; - } - - res = pomp_evt_attach_to_loop(evt, loop, &queueEventCb, this); - if (res < 0) { - RawSink::unlock(); - PDRAW_LOG_ERRNO("pomp_evt_attach_to_loop", -res); - goto error; - } - - mLastAddedMedia = media; - mCurrentMediaId = mMediaId; - - media->fillMediaInfo(&mMediaInfo); - /* Deep copy: copy the session metadata */ - mMediaInfoSessionMeta = *mMediaInfo.session_meta; - mMediaInfo.session_meta = &mMediaInfoSessionMeta; - - RawSink::unlock(); - - pthread_mutex_lock(&mListenerMutex); - if (mRendererListener) { - mRendererListener->onVideoRendererMediaAdded( - mSession, mRenderer, &mMediaInfo); - } - pthread_mutex_unlock(&mListenerMutex); - - return 0; - -error: - removeQueueFdFromPomp(queue); - return res; -} - - -/* Must be called on the loop thread */ -int Gles2Renderer::removeQueueFdFromPomp( - struct mbuf_raw_video_frame_queue *queue) -{ - int ret; - struct pomp_loop *loop = nullptr; - struct pomp_evt *evt = nullptr; - - loop = mSession->getLoop(); - if (loop == nullptr) { - PDRAW_LOGE("loop not found"); - return -ENODEV; - } - - ret = mbuf_raw_video_frame_queue_get_event(queue, &evt); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_get_event", -ret); - return ret; - } - - ret = pomp_evt_detach_from_loop(evt, loop); - if (ret < 0) { - PDRAW_LOG_ERRNO("pomp_evt_detach_from_loop", -ret); - return ret; - } - - return 0; -} - - -/* Must be called on the loop thread */ -int Gles2Renderer::removeInputMedia(RawVideoMedia *media) -{ - int ret; - - RawSink::lock(); - - if (mLastAddedMedia == media) { - mLastAddedMedia = nullptr; - mCurrentMediaId = 0; - pthread_mutex_lock(&mListenerMutex); - if (mRendererListener) { - mRendererListener->onVideoRendererMediaRemoved( - mSession, mRenderer, &mMediaInfo); - } - pthread_mutex_unlock(&mListenerMutex); - - if (mCurrentFrame != nullptr) { - int releaseRet = - mbuf_raw_video_frame_unref(mCurrentFrame); - if (releaseRet < 0) - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_unref", - -releaseRet); - mCurrentFrame = nullptr; - } - - Media::cleanupMediaInfo(&mMediaInfo); - } - - - RawChannel *channel = getInputChannel(media); - if (channel == nullptr) { - RawSink::unlock(); - PDRAW_LOGE("failed to get channel"); - return -EPROTO; - } - /* Keep a reference on the queue to destroy it after removing the - * input media (avoids deadlocks when trying to push new frames out - * of the VideoDecoder whereas the queue is already destroyed) */ - struct mbuf_raw_video_frame_queue *queue = channel->getQueue(); - - ret = RawSink::removeInputMedia(media); - if (ret < 0) { - RawSink::unlock(); - PDRAW_LOG_ERRNO("RawSink::removeInputMedia", -ret); - return ret; - } - - RawSink::unlock(); - - if (queue != nullptr) { - ret = removeQueueFdFromPomp(queue); - if (ret < 0) - PDRAW_LOG_ERRNO("removeQueueFdFromPomp", -ret); - ret = mbuf_raw_video_frame_queue_flush(queue); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_flush", - -ret); - ret = mbuf_raw_video_frame_queue_destroy(queue); - if (ret < 0) - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_destroy", - -ret); - } - - return 0; -} - - -/* Must be called on the loop thread */ -int Gles2Renderer::removeInputMedias(void) -{ - int ret, inputMediaCount, i; - - RawSink::lock(); - - inputMediaCount = getInputMediaCount(); - - /* Note: loop downwards because calling removeInputMedia removes - * input ports and decreases the media count */ - for (i = inputMediaCount - 1; i >= 0; i--) { - RawVideoMedia *media = getInputMedia(i); - if (media == nullptr) { - PDRAW_LOG_ERRNO("getInputMedia", ENOENT); - continue; - } - ret = removeInputMedia(media); - if (ret < 0) { - PDRAW_LOG_ERRNO("removeInputMedia", -ret); - continue; - } - } - - mLastAddedMedia = nullptr; - mCurrentMediaId = 0; - RawSink::unlock(); - - return 0; -} - - -void Gles2Renderer::idleRenewMedia(void *userdata) -{ - Gles2Renderer *self = reinterpret_cast(userdata); - PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); - if (self->mLastAddedMedia != nullptr) - self->removeInputMedia(self->mLastAddedMedia); - self->mSession->addMediaToRenderer(self->mMediaId, self); -} - - -/* Called on the rendering thread */ -void Gles2Renderer::abortTransition(void) -{ - RawSink::lock(); - mCurrentTransition = Transition::NONE; - mTransitionStartTime = 0; - mTransitionHoldTime = 0; - RawSink::unlock(); - - if (mGles2Video) { - mGles2Video->setSatCoef(1.0f); - mGles2Video->setLightCoef(1.0f); - mGles2Video->setDarkCoef(1.0f); - mGles2Video->abortTransition(); - } -} - - -/* Called on the rendering thread */ -int Gles2Renderer::doTransition(uint64_t timestamp, - bool frameReady, - bool *loadFrame) -{ - if (timestamp == 0) - return -EINVAL; - if (loadFrame == nullptr) - return -EINVAL; - - RawSink::lock(); - - if (mPendingTransition == Transition::NONE) - goto out; - - if (mCurrentTransition != Transition::NONE) { - /* Abort previous transition */ - abortTransition(); - } - - mCurrentTransition = mPendingTransition; - mPendingTransition = Transition::NONE; - mTransitionStartTime = timestamp; - - switch (mCurrentTransition) { - case Transition::FADE_FROM_BLACK: - mTransitionHoldTime = GLES2_RENDERER_FADE_FROM_BLACK_DURATION; - if (mGles2Video) { - mGles2Video->startTransition( - GLES2_VIDEO_TRANSITION_FADE_FROM_BLACK, - GLES2_RENDERER_FADE_FROM_BLACK_DURATION, - false); - } - break; - case Transition::FADE_TO_BLACK: - mTransitionHoldTime = GLES2_RENDERER_FADE_TO_BLACK_DURATION; - if (mGles2Video) { - mGles2Video->startTransition( - GLES2_VIDEO_TRANSITION_FADE_TO_BLACK, - GLES2_RENDERER_FADE_TO_BLACK_DURATION, - true); - } - break; - case Transition::FADE_TO_BLACK_AND_WHITE: - mTransitionHoldTime = - GLES2_RENDERER_FADE_TO_BLACK_AND_WHITE_HOLD; - if (mGles2Video) { - mGles2Video->startTransition( - GLES2_VIDEO_TRANSITION_FADE_TO_BLACK_AND_WHITE, - GLES2_RENDERER_FADE_TO_BLACK_AND_WHITE_DURATION, - true); - } - break; - case Transition::FADE_TO_BLUR: - mTransitionHoldTime = GLES2_RENDERER_FADE_TO_BLUR_HOLD; - if (mGles2Video) { - mGles2Video->startTransition( - GLES2_VIDEO_TRANSITION_FADE_TO_BLUR, - GLES2_RENDERER_FADE_TO_BLUR_DURATION, - true); - } - break; - case Transition::FLASH_THEN_BLACK_AND_WHITE: - mTransitionHoldTime = - GLES2_RENDERER_FLASH_THEN_BLACK_AND_WHITE_HOLD; - if (mGles2Video) { - mGles2Video->startTransition( - GLES2_VIDEO_TRANSITION_FLASH, - GLES2_RENDERER_FLASH_THEN_BLACK_AND_WHITE_DURATION, - true); - } - break; - default: - abortTransition(); - break; - } - -out: - if ((frameReady) && (mCurrentTransition != Transition::NONE) && - (timestamp >= mTransitionStartTime + mTransitionHoldTime)) { - /* End the transition */ - abortTransition(); - *loadFrame = true; - } else if ((frameReady) && - ((mCurrentTransition == Transition::NONE) || - (mCurrentTransition == Transition::FADE_FROM_BLACK) || - (mCurrentTransition == - Transition::FLASH_THEN_BLACK_AND_WHITE))) { - *loadFrame = true; - } else { - *loadFrame = false; - } - RawSink::unlock(); - return 0; -} - - -/* Called on the rendering thread */ -int Gles2Renderer::loadVideoFrame(struct mbuf_raw_video_frame *mbufFrame, - RawVideoMedia::Frame *frame) -{ - int ret; - unsigned int planeCount = 0; - const void *planes[VDEF_RAW_MAX_PLANE_COUNT] = {}; - - if (vdef_dim_is_null(&mCurrentFrameInfo.info.resolution) || - vdef_dim_is_null(&mCurrentFrameInfo.info.sar)) { - PDRAW_LOGE("invalid frame dimensions"); - ret = -EINVAL; - goto out; - } - - planeCount = vdef_get_raw_frame_plane_count(&mCurrentFrameInfo.format); - for (unsigned int i = 0; i < planeCount; i++) { - size_t dummyPlaneLen; - ret = mbuf_raw_video_frame_get_plane( - mbufFrame, i, &planes[i], &dummyPlaneLen); - if (ret < 0) { - PDRAW_LOG_ERRNO( - "mbuf_raw_video_frame_get_plane(%u)", -ret, i); - goto out; - } - } - - ret = mGles2Video->loadFrame((const uint8_t **)planes, - mCurrentFrameInfo.plane_stride, - &mCurrentFrameInfo.format, - &mCurrentFrameInfo.info); - if (ret < 0) - PDRAW_LOG_ERRNO("gles2Video->loadFrame", -ret); - -out: - for (unsigned int i = 0; i < planeCount; i++) { - if (planes[i] == nullptr) - continue; - int err = mbuf_raw_video_frame_release_plane( - mbufFrame, i, planes[i]); - if (err < 0) - PDRAW_LOG_ERRNO( - "mbuf_raw_video_frame_release_plane(%u)", - -err, - i); - } - - mFrameLoaded = (ret == 0); - return ret; -} - - -/* Called on the rendering thread */ -int Gles2Renderer::loadExternalVideoFrame( - struct mbuf_raw_video_frame *mbufFrame, - RawVideoMedia::Frame *frame, - const struct pdraw_media_info *mediaInfo) -{ - int ret; - struct mbuf_ancillary_data *userData = nullptr; - size_t frameUserdataLen; - const void *frameUserdata = nullptr; - - ret = mbuf_raw_video_frame_get_ancillary_data( - mbufFrame, MBUF_ANCILLARY_KEY_USERDATA_SEI, &userData); - if (ret == -ENOENT) { - /* No userdata */ - frameUserdata = nullptr; - frameUserdataLen = 0; - } else if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_get_ancillary_data", - -ret); - goto out; - } else { - frameUserdata = mbuf_ancillary_data_get_buffer( - userData, &frameUserdataLen); - } - - if (vdef_dim_is_null(&mCurrentFrameInfo.info.resolution) || - vdef_dim_is_null(&mCurrentFrameInfo.info.sar)) { - PDRAW_LOGE("invalid frame dimensions"); - ret = -EINVAL; - goto out; - } - - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mExtLoadFbo)); - GLCHK(glViewport(0, 0, mExtVideoTextureWidth, mExtVideoTextureHeight)); - - /* Texture loading callback function */ - pthread_mutex_lock(&mListenerMutex); - if (mRendererListener != nullptr) { - ret = mRendererListener->loadVideoTexture( - mSession, - mRenderer, - mExtVideoTextureWidth, - mExtVideoTextureHeight, - mediaInfo, - mbufFrame, - frameUserdata, - (size_t)frameUserdataLen); - } - pthread_mutex_unlock(&mListenerMutex); - - if (mParams.enable_hmd_distortion_correction) { - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mHmdFbo)); - GLCHK(glViewport(0, 0, mHmdFboSize, mHmdFboSize)); - } else { - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); - GLCHK(glViewport(mX, mY, mWidth, mHeight)); - } - -out: - if (userData) - mbuf_ancillary_data_unref(userData); - mFrameLoaded = (ret == 0); - return ret; -} - - -/* Called on the rendering thread */ -int Gles2Renderer::renderVideoFrame(const struct pdraw_rect *renderPos, - struct pdraw_rect *contentPos, - Eigen::Matrix4f &viewProjMat) -{ - struct vdef_rect crop = { - .left = 0, - .top = 0, - .width = mCurrentFrameInfo.info.resolution.width, - .height = mCurrentFrameInfo.info.resolution.height, - }; - - return mGles2Video->renderFrame(renderPos, - contentPos, - viewProjMat, - mCurrentFrameInfo.plane_stride, - &mCurrentFrameInfo.format, - &mCurrentFrameInfo.info, - &crop, - mCurrentFrameMetadata, - &mParams); -} - - -/* Called on the rendering thread */ -int Gles2Renderer::renderExternalVideoFrame(const struct pdraw_rect *renderPos, - struct pdraw_rect *contentPos, - Eigen::Matrix4f &viewProjMat) -{ - struct vdef_frame_info info = mCurrentFrameInfo.info; - struct vdef_rect crop = { - .left = 0, - .top = 0, - .width = mExtVideoTextureWidth, - .height = mExtVideoTextureHeight, - }; - size_t frameStride[3] = {mExtVideoTextureWidth, 0, 0}; - - info.sar.width = mCurrentFrameInfo.info.resolution.width * - mExtVideoTextureHeight; - info.sar.height = mCurrentFrameInfo.info.resolution.height * - mExtVideoTextureWidth; - - return mGles2Video->renderFrame(renderPos, - contentPos, - viewProjMat, - frameStride, - &vdef_rgb, - &info, - &crop, - mCurrentFrameMetadata, - &mParams); -} - - -int Gles2Renderer::setupExtTexture(const struct vdef_raw_frame *frameInfo, - const RawVideoMedia::Frame *frame) -{ - int ret = 0; - - PDRAW_LOGI( - "external video texture: source=%ux%u (SAR=%u:%u) " - "DAR=%u:%u textureWidth=%u", - frameInfo->info.resolution.width, - frameInfo->info.resolution.height, - frameInfo->info.sar.width, - frameInfo->info.sar.height, - mParams.video_texture_dar_width, - mParams.video_texture_dar_height, - mParams.video_texture_width); - - /* Compute the external texture size */ - if (mParams.video_texture_dar_width != 0 && - mParams.video_texture_dar_height != 0) { - /* Custom DAR is provided */ - if (mParams.video_texture_width > 0) { - /* Custom DAR with custom texture width */ - mExtVideoTextureWidth = mParams.video_texture_width; - mExtVideoTextureHeight = - (mParams.video_texture_width * - mParams.video_texture_dar_height + - mParams.video_texture_dar_width / 2) / - mParams.video_texture_dar_width; - } else { - /* Custom DAR without custom texture width */ - float dar, ar; - dar = (float)mParams.video_texture_dar_width / - (float)mParams.video_texture_dar_height; - ar = (float)frameInfo->info.resolution.width / - (float)frameInfo->info.resolution.height; - /* Take the DAR into account, never decreasing - * the dimensions */ - if (dar > ar) { - mExtVideoTextureWidth = - (frameInfo->info.resolution.height * - mParams.video_texture_dar_width + - mParams.video_texture_dar_height / 2) / - mParams.video_texture_dar_height; - mExtVideoTextureHeight = - frameInfo->info.resolution.height; - } else { - mExtVideoTextureWidth = - frameInfo->info.resolution.width; - mExtVideoTextureHeight = - (frameInfo->info.resolution.width * - mParams.video_texture_dar_height + - mParams.video_texture_dar_width / 2) / - mParams.video_texture_dar_width; - } - } - } else if (mParams.video_texture_width > 0) { - /* Custom texture width without custom DAR */ - mExtVideoTextureWidth = mParams.video_texture_width; - mExtVideoTextureHeight = - (mParams.video_texture_width * - frameInfo->info.resolution.height + - frameInfo->info.resolution.width / 2) / - frameInfo->info.resolution.width; - - /* Take the SAR into account */ - mExtVideoTextureHeight = - (mExtVideoTextureHeight * frameInfo->info.sar.height + - frameInfo->info.sar.width / 2) / - frameInfo->info.sar.width; - } else { - /* No custom texture width and no custom DAR */ - float sar = (float)frameInfo->info.sar.width / - (float)frameInfo->info.sar.height; - /* Take the SAR into account, never decreasing the dimensions */ - if (sar < 1.f) { - mExtVideoTextureWidth = - frameInfo->info.resolution.width; - mExtVideoTextureHeight = - (frameInfo->info.resolution.height * - frameInfo->info.sar.height + - frameInfo->info.sar.width / 2) / - frameInfo->info.sar.width; - } else { - mExtVideoTextureWidth = - (frameInfo->info.resolution.width * - frameInfo->info.sar.width + - frameInfo->info.sar.height / 2) / - frameInfo->info.sar.height; - mExtVideoTextureHeight = - frameInfo->info.resolution.height; - } - } - - /* Round up to nearest multiple of 2 */ - mExtVideoTextureWidth = (mExtVideoTextureWidth + 1) & ~1; - mExtVideoTextureHeight = (mExtVideoTextureHeight + 1) & ~1; - - /* start external Texture */ - if (mExtVideoTextureWidth != 0 && mExtVideoTextureHeight != 0) { - ret = startExtLoad(); - if (ret < 0) { - PDRAW_LOG_ERRNO("startExtLoad", -ret); - mExtLoadVideoTexture = false; - mExtVideoTextureWidth = 0; - mExtVideoTextureHeight = 0; - mParams.video_texture_width = 0; - mParams.video_texture_dar_width = 0; - mParams.video_texture_dar_height = 0; - } - } else { - mExtLoadVideoTexture = false; - mExtVideoTextureWidth = 0; - mExtVideoTextureHeight = 0; - mParams.video_texture_width = 0; - mParams.video_texture_dar_width = 0; - mParams.video_texture_dar_height = 0; - ret = stopExtLoad(); - if (ret < 0) - PDRAW_LOG_ERRNO("stopExtLoad", -ret); - } - - PDRAW_LOGI("external video texture: size=%ux%u", - mExtVideoTextureWidth, - mExtVideoTextureHeight); - - return 0; -} - - -void Gles2Renderer::createProjMatrix(Eigen::Matrix4f &projMat, - float aspectRatio, - float near, - float far) -{ - float w = 1.f; - float h = aspectRatio; - float a = (far + near) / (far - near); - float b = -((2 * far * near) / (far - near)); - - projMat << w, 0, 0, 0, 0, h, 0, 0, 0, 0, a, b, 0, 0, 1, 0; -} - - -int Gles2Renderer::getNextFrameDelay(mbuf_raw_video_frame_queue *queue, - uint64_t curTime, - bool allowDrop, - bool *shouldBreak, - uint64_t *delayUs, - int64_t *compensationUs, - int64_t *timingErrorUs, - uint64_t *frameTsUs, - int logLevel) -{ - int ret; - uint32_t delay; - struct mbuf_raw_video_frame *frame = nullptr; - struct vdef_raw_frame frameInfo = {}; - uint64_t frameTs = 0; - - int count = mbuf_raw_video_frame_queue_get_count(queue); - if ((mParams.scheduling_mode == - PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ADAPTIVE) && - allowDrop && (count >= GLES2_RENDERER_QUEUE_MAX_FRAMES)) { - /* The queue is full, discard the next frame to catch up */ - ret = mbuf_raw_video_frame_queue_pop(queue, &frame); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_pop", -ret); - } else { - if (logLevel) { - (void)mbuf_raw_video_frame_get_frame_info( - frame, &frameInfo); - _PDRAW_LOG_INT(logLevel, - "QUEUE FULL (queue_count=%u) " - "frame #%u is discarded", - count, - frameInfo.info.index); - } - (void)mbuf_raw_video_frame_unref(frame); - frame = nullptr; - count--; - } - } - - ret = mbuf_raw_video_frame_queue_peek(queue, &frame); - if (ret < 0) { - if (ret != -EAGAIN) { - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_peek", - -ret); - } - return ret; - } - - ret = mbuf_raw_video_frame_get_frame_info(frame, &frameInfo); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_get_frame_info", -ret); - mbuf_raw_video_frame_unref(frame); - return ret; - } - frameTs = frameInfo.info.timescale - ? (frameInfo.info.timestamp * 1000000 + - frameInfo.info.timescale / 2) / - frameInfo.info.timescale - : 0; - - uint64_t inputTime = UINT64_MAX; - inputTime = getFrameU64(frame, - GLES2_RENDERER_ANCILLARY_DATA_KEY_INPUT_TIME); - - mbuf_raw_video_frame_unref(frame); - frame = nullptr; - - uint32_t outputDelay = - (inputTime != UINT64_MAX) ? curTime - inputTime : 0; - int64_t timingError = 0, compensation, epsilon = 0; - if (mSchedLastInputTimestamp != UINT64_MAX) { - epsilon = (GLES2_RENDERER_SCHED_ADAPTIVE_EPSILON_PERCENT * - (frameTs - mSchedLastInputTimestamp) + - 50) / - 100; - } - if (mSchedLastInputTimestamp != UINT64_MAX && - mSchedLastOutputTimestamp != UINT64_MAX) { - timingError = - (int64_t)frameTs - (int64_t)mSchedLastInputTimestamp - - (int64_t)curTime + (int64_t)mSchedLastOutputTimestamp; - } - - bool _shouldBreak = false; - switch (mParams.scheduling_mode) { - default: - case PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ASAP: - /* Render the frame ASAP */ - delay = 0; - compensation = 0; - break; - case PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ADAPTIVE: - if ((mSchedLastInputTimestamp == UINT64_MAX) && - (count <= GLES2_RENDERER_QUEUE_MAX_FRAMES / 2)) { - /* Initial buffering: half the queue must be filled - * (rounded down); break */ - if (logLevel) { - _PDRAW_LOG_INT( - logLevel, - "INITIAL BUFFERING (queue_count=%u)", - count); - } - /* TODO: reset the mSchedLast*putTimestamp variables - * somewhere to reset the initial buffering when - * needed (i.e. when we will have that information - * in downstream pipeline events) */ - delay = 0; - compensation = 0; - _shouldBreak = true; - } else if (timingError > epsilon) { - /* The frame is early */ - if (count < GLES2_RENDERER_QUEUE_MAX_FRAMES - 1) { - /* Process the frame later; break */ - delay = timingError; - compensation = 0; - _shouldBreak = true; - if (logLevel) { - _PDRAW_LOG_INT( - logLevel, - "[EARLY] frame #%u is %.2fms " - "early (delay=%.2fms, " - "queue_count=%u)", - frameInfo.info.index, - (float)timingError / 1000., - (float)outputDelay / 1000., - count); - } - } else { - /* Process the frame now anyway; do not take the - * timing error into account as compensation in - * mSchedLastOutputTimestamp */ - if (logLevel) { - _PDRAW_LOG_INT( - logLevel, - "[EARLY] frame #%u is %.2fms " - "early (delay=%.2fms, " - "queue_count=%u) " - "=> PROCESS ANYWAY", - frameInfo.info.index, - (float)timingError / 1000., - (float)outputDelay / 1000., - count); - } - compensation = 0; - delay = 0; - } - } else if (timingError < -epsilon) { - /* The frame is late */ - if (logLevel) { - _PDRAW_LOG_INT(logLevel, - "[LATE] frame #%u is %.2fms " - "late (delay=%.2fms, " - "queue_count=%u)", - frameInfo.info.index, - (float)timingError / -1000., - (float)outputDelay / 1000., - count); - } - delay = 0; - compensation = timingError; - } else { - /* The frame is on time */ - if (logLevel) { - _PDRAW_LOG_INT(logLevel, - "[ONTIME] frame #%u is on time " - "(delay=%.2fms, queue_count=%u)", - frameInfo.info.index, - (float)outputDelay / 1000., - count); - } - delay = 0; - compensation = timingError; - } - break; - } - - if (shouldBreak != nullptr) - *shouldBreak = _shouldBreak; - if (delayUs != nullptr) - *delayUs = delay; - if (compensationUs != nullptr) - *compensationUs = compensation; - if (timingErrorUs != nullptr) - *timingErrorUs = timingError; - if (frameTsUs != nullptr) - *frameTsUs = frameTs; - - return 0; -} - - -int Gles2Renderer::scheduleFrame(uint64_t curTime, - bool *load, - int64_t *compensationUs) -{ - int ret = 0, err; - bool _load = false; - struct mbuf_raw_video_frame *frame = nullptr; - int count; - bool shouldBreak; - uint64_t frameTs; - int64_t compensation; - - struct mbuf_raw_video_frame_queue *queue = getLastAddedMediaQueue(); - if (queue == nullptr) { - ret = -EPROTO; - goto out; - } - - count = mbuf_raw_video_frame_queue_get_count(queue); - if (count == 0) { - /* Empty queue, reset the scheduling (the shortage of frames - * can be because of a seek or pause in the playback) */ - mSchedLastInputTimestamp = UINT64_MAX; - mSchedLastOutputTimestamp = UINT64_MAX; - PDRAW_LOGD("RESET SCHEDULING"); - goto out; - } - - /* Get a new frame to render */ - do { - shouldBreak = false; - frameTs = 0; - compensation = 0; - - err = getNextFrameDelay(queue, - curTime, - true, - &shouldBreak, - nullptr, - &compensation, - nullptr, - &frameTs, - 0); - if (err < 0) { - if (err != -EAGAIN) - PDRAW_LOG_ERRNO("getNextFrameDelay", -err); - break; - } - - if (shouldBreak) - break; - - err = mbuf_raw_video_frame_queue_pop(queue, &frame); - if (err < 0) { - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_pop", -err); - break; - } - - if (mCurrentFrame != nullptr) - (void)mbuf_raw_video_frame_unref(mCurrentFrame); - mCurrentFrame = frame; - _load = true; - frame = nullptr; - mSchedLastOutputTimestamp = curTime + compensation; - mSchedLastInputTimestamp = frameTs; - if (compensationUs != nullptr) - *compensationUs = compensation; - } while (true); - -out: - if ((ret == 0) && (load != nullptr)) - *load = _load; - return ret; -} - - -/* Called on the rendering thread */ -int Gles2Renderer::render(struct pdraw_rect *contentPos, - const float *viewMat, - const float *projMat) -{ - int err; - struct pdraw_rect renderPos; - bool load = false, render = false; - uint64_t curTime = 0, inputTime, delay = 0; - int64_t compensation = 0; - struct timespec ts = {0, 0}; - struct pdraw_media_info *mediaInfoPtr = nullptr; - Eigen::Matrix4f vpMat, vMat, pMat; - struct pdraw_rect content; - struct mbuf_ancillary_data *ancillaryData = nullptr; - RawVideoMedia::Frame *data; - - memset(&content, 0, sizeof(content)); - - if (contentPos != nullptr) - *contentPos = content; - - if ((!mRunning) || (mState != STARTED)) - return 0; - - if ((mWidth == 0) || (mHeight == 0)) - return 0; - - err = time_get_monotonic(&ts); - if (err < 0) - PDRAW_LOG_ERRNO("time_get_monotonic", -err); - err = time_timespec_to_us(&ts, &curTime); - if (err < 0) - PDRAW_LOG_ERRNO("time_timespec_to_us", -err); - - GLCHK(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mDefaultFbo)); - - if (mParams.enable_hmd_distortion_correction) { - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mHmdFbo)); - GLCHK(glViewport(0, 0, mHmdFboSize, mHmdFboSize)); - GLCHK(glDisable(GL_DITHER)); - renderPos.x = 0; - renderPos.y = 0; - renderPos.width = mHmdFboSize; - renderPos.height = mHmdFboSize; - } else { - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); - GLCHK(glViewport(mX, mY, mWidth, mHeight)); - GLCHK(glDisable(GL_DITHER)); - renderPos.x = mX; - renderPos.y = mY; - renderPos.width = mWidth; - renderPos.height = mHeight; - } - - if (viewMat != nullptr) { - unsigned int i, j; - for (i = 0; i < 4; i++) { - for (j = 0; j < 4; j++) - vMat(i, j) = viewMat[j * 4 + i]; - } - } else { - vMat = Eigen::Matrix4f::Identity(); - } - if (projMat != nullptr) { - unsigned int i, j; - for (i = 0; i < 4; i++) { - for (j = 0; j < 4; j++) - pMat(i, j) = projMat[j * 4 + i]; - } - } else { - createProjMatrix(pMat, - (float)renderPos.width / - (float)renderPos.height, - 0.1f, - 100.f); - } - vpMat = pMat * vMat; - - RawSink::lock(); - if (mLastAddedMedia == nullptr) { - /* No current media */ - goto skip_dequeue; - } - - Media::cleanupMediaInfo(&mMediaInfo); - mLastAddedMedia->fillMediaInfo(&mMediaInfo); - /* Deep copy: copy the session metadata */ - mMediaInfoSessionMeta = *mMediaInfo.session_meta; - mMediaInfo.session_meta = &mMediaInfoSessionMeta; - mediaInfoPtr = &mMediaInfo; - - err = scheduleFrame(curTime, &load, &compensation); - if (err < 0) { - RawSink::unlock(); - goto skip_dequeue; - } - - if (load) { - /* We have a new frame */ - bool expected = true; - if (mWatchdogTriggered.compare_exchange_strong(expected, - false)) { - PDRAW_LOGI("new frame to render"); - } - int err = pomp_timer_set(mWatchdogTimer, - 1000 * GLES2_RENDERER_WATCHDOG_TIME_S); - if (err != 0) { - PDRAW_LOG_ERRNO("pomp_timer_set", -err); - } - } - - if (mCurrentFrame == nullptr) { - /* No new frame to load */ - goto skip_dequeue; - } - - if (mCurrentFrameMetadata) { - vmeta_frame_unref(mCurrentFrameMetadata); - mCurrentFrameMetadata = nullptr; - } - - err = mbuf_raw_video_frame_get_ancillary_data( - mCurrentFrame, - PDRAW_ANCILLARY_DATA_KEY_RAWVIDEOFRAME, - &ancillaryData); - if (err < 0) { - RawSink::unlock(); - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_get_ancillary_data", - -err); - goto skip_render; - } - data = (RawVideoMedia::Frame *)mbuf_ancillary_data_get_buffer( - ancillaryData, NULL); - if (data == nullptr) { - RawSink::unlock(); - PDRAW_LOGE("invalid ancillary data pointer"); - goto skip_render; - } - mCurrentFrameData = *data; - mbuf_ancillary_data_unref(ancillaryData); - - err = mbuf_raw_video_frame_get_frame_info(mCurrentFrame, - &mCurrentFrameInfo); - if (err < 0) { - RawSink::unlock(); - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_get_frame_info", -err); - goto skip_render; - } - - err = mbuf_raw_video_frame_get_metadata(mCurrentFrame, - &mCurrentFrameMetadata); - if (err < 0 && err != -ENOENT) { - RawSink::unlock(); - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_get_metadata", -err); - goto skip_render; - } - - inputTime = getFrameU64(mCurrentFrame, - GLES2_RENDERER_ANCILLARY_DATA_KEY_INPUT_TIME); - delay = (inputTime != 0) ? curTime - inputTime : 0; - - if (mFirstFrame) { - mFirstFrame = false; - int err = pomp_timer_set_periodic( - mVideoPresStatsTimer, - GLES2_RENDERER_VIDEO_PRES_STATS_TIME_MS, - GLES2_RENDERER_VIDEO_PRES_STATS_TIME_MS); - if (err != 0) - PDRAW_LOG_ERRNO("pomp_timer_set", -err); - if (mExtLoadVideoTexture) { - err = setupExtTexture(&mCurrentFrameInfo, - &mCurrentFrameData); - if (err < 0) - PDRAW_LOG_ERRNO("setupExtTexture", -err); - } - } - -skip_dequeue: - if (mGles2Video == nullptr) { - RawSink::unlock(); - goto skip_render; - } - - err = doTransition(curTime, load, &load); - if (err < 0) { - PDRAW_LOG_ERRNO("doTransition", -err); - goto skip_render; - } - - if (mCurrentFrame != nullptr) { - if (mExtLoadVideoTexture) { - err = loadExternalVideoFrame(mCurrentFrame, - &mCurrentFrameData, - mediaInfoPtr); - if (err < 0) - goto skip_render; - } else if (load) { - err = loadVideoFrame(mCurrentFrame, &mCurrentFrameData); - if (err < 0) - goto skip_render; - } - } - - if (mCurrentFrame != nullptr && load) { - /* First rendering of the current frame */ - - int queueCount = 0; - struct mbuf_raw_video_frame_queue *queue = - getLastAddedMediaQueue(); - if (queue != nullptr) { - queueCount = - mbuf_raw_video_frame_queue_get_count(queue); - } - - mCurrentFrameData.renderTimestamp = curTime; - uint64_t timestampDelta = 0; - if ((mCurrentFrameData.ntpRawTimestamp != 0) && - (mLastFrameTimestamp != UINT64_MAX)) { - timestampDelta = mCurrentFrameData.ntpRawTimestamp - - mLastFrameTimestamp; - } - uint64_t renderDelta = 0; - if ((mCurrentFrameData.renderTimestamp != 0) && - (mLastRenderTimestamp != UINT64_MAX)) { - renderDelta = mCurrentFrameData.renderTimestamp - - mLastRenderTimestamp; - } - int64_t timingError = 0; - if ((timestampDelta != 0) && (renderDelta != 0)) { - timingError = - (int64_t)timestampDelta - (int64_t)renderDelta; - } - uint64_t timingErrorAbs = - (timingError < 0) ? -timingError : timingError; - uint64_t estLatency = 0; - if ((mCurrentFrameData.renderTimestamp != 0) && - (mCurrentFrameData.localTimestamp != 0) && - (mCurrentFrameData.renderTimestamp > - mCurrentFrameData.localTimestamp)) { - estLatency = mCurrentFrameData.renderTimestamp - - mCurrentFrameData.localTimestamp; - } - uint64_t playerLatency = 0; - if ((mCurrentFrameData.renderTimestamp != 0) && - (mCurrentFrameData.recvStartTimestamp != 0) && - (mCurrentFrameData.renderTimestamp > - mCurrentFrameData.recvStartTimestamp)) { - playerLatency = mCurrentFrameData.renderTimestamp - - mCurrentFrameData.recvStartTimestamp; - } - PDRAW_LOGD( - "frame #%u est_total_latency=%.2fms " - "player_latency=%.2fms render_interval=%.2fms " - "render_delay=%.2fms timing_error=%.2fms " - "queue_count=%u", - mCurrentFrameInfo.info.index, - (float)estLatency / 1000., - (float)playerLatency / 1000., - (float)renderDelta / 1000., - (float)delay / 1000., - (float)timingError / 1000., - queueCount); - mVideoPresStats.timestamp = mCurrentFrameData.captureTimestamp; - mVideoPresStats.presentationFrameCount++; - mVideoPresStats.presentationTimestampDeltaIntegral += - timestampDelta; - mVideoPresStats.presentationTimestampDeltaIntegralSq += - timestampDelta * timestampDelta; - mVideoPresStats.presentationTimingErrorIntegral += - timingErrorAbs; - mVideoPresStats.presentationTimingErrorIntegralSq += - timingErrorAbs * timingErrorAbs; - mVideoPresStats.presentationEstimatedLatencyIntegral += - estLatency; - mVideoPresStats.presentationEstimatedLatencyIntegralSq += - estLatency * estLatency; - mVideoPresStats.playerLatencyIntegral += playerLatency; - mVideoPresStats.playerLatencyIntegralSq += - playerLatency * playerLatency; - mVideoPresStats.estimatedLatencyPrecisionIntegral += - mCurrentFrameData.localTimestampPrecision; - mLastRenderTimestamp = - mCurrentFrameData.renderTimestamp + compensation; - mLastFrameTimestamp = mCurrentFrameData.ntpRawTimestamp; - } - - RawSink::unlock(); - - /* Actual rendering */ - if (mFrameLoaded) { - if (mExtLoadVideoTexture) { - err = renderExternalVideoFrame( - &renderPos, &content, vpMat); - if (err < 0) { - PDRAW_LOG_ERRNO( - "gles2Video->" - "renderExternalVideoFrame", - -err); - } else { - render = true; - } - } else { - err = renderVideoFrame(&renderPos, &content, vpMat); - if (err < 0) { - PDRAW_LOG_ERRNO("gles2Video->renderVideoFrame", - -err); - } else { - render = true; - } - } - } else { - err = mGles2Video->clear(vpMat); - if (err < 0) - PDRAW_LOG_ERRNO("gles2Video->clear", -err); - } - if (mFrameLoaded != mFrameLoadedLogged) { - mFrameLoadedLogged = mFrameLoaded; - PDRAW_LOGI("render state: %s", - mFrameLoaded ? "video" : "black"); - } - -skip_render: - /* Overlay rendering callback function */ - if (mRenderVideoOverlay) { - struct pdraw_video_frame_extra frameExtra = {}; - struct vmeta_frame *frameMetaPtr = nullptr; - const struct pdraw_video_frame_extra *frameExtraPtr = nullptr; - if (render) { - frameExtra.play_timestamp = - mCurrentFrameData.playTimestamp; - if (mGles2Video) { - mGles2Video->getHistograms( - frameExtra.histogram, - frameExtra.histogram_len); - } - frameMetaPtr = mCurrentFrameMetadata; - frameExtraPtr = &frameExtra; - } - pthread_mutex_lock(&mListenerMutex); - if (mRendererListener != nullptr) { - mRendererListener->renderVideoOverlay(mSession, - mRenderer, - &renderPos, - &content, - vMat.data(), - pMat.data(), - mediaInfoPtr, - frameMetaPtr, - frameExtraPtr); - } - pthread_mutex_unlock(&mListenerMutex); - } - - /* HMD rendering */ - if (mParams.enable_hmd_distortion_correction) { - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); - GLCHK(glViewport(mX, mY, mWidth, mHeight)); - - if (mGles2Hmd) { - err = mGles2Hmd->renderHmd( - mHmdFboTexture, mHmdFboSize, mHmdFboSize); - if (err < 0) - PDRAW_LOG_ERRNO("gles2Hmd->renderHmd", -err); - } - } - - if (contentPos != nullptr) - *contentPos = content; - - return 0; -} - - -/* Called on the rendering thread */ -int Gles2Renderer::startHmd(void) -{ - int ret = 0; - GLenum gle; - - int err = stopHmd(); - if (err < 0) - PDRAW_LOG_ERRNO("stopHmd", -err); - - mHmdFboSize = (mWidth / 2 > mHeight) ? mWidth / 2 : mHeight; - GLCHK(); - - GLCHK(glGenFramebuffers(1, &mHmdFbo)); - if (mHmdFbo <= 0) { - PDRAW_LOGE("failed to create framebuffer"); - ret = -EPROTO; - goto out; - } - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mHmdFbo)); - - GLCHK(glGenTextures(1, &mHmdFboTexture)); - if (mHmdFboTexture <= 0) { - PDRAW_LOGE("failed to create texture"); - ret = -EPROTO; - goto out; - } - GLCHK(glActiveTexture(GL_TEXTURE0 + mGles2HmdFirstTexUnit)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mHmdFboTexture)); - GLCHK(glTexImage2D(GL_TEXTURE_2D, - 0, - GL_RGBA, - mHmdFboSize, - mHmdFboSize, - 0, - GL_RGBA, - GL_UNSIGNED_BYTE, - nullptr)); - - GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GLCHK(glTexParameterf( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GLCHK(glTexParameterf( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - - GLCHK(glGenRenderbuffers(1, &mHmdFboRenderBuffer)); - if (mHmdFboRenderBuffer <= 0) { - PDRAW_LOGE("failed to create render buffer"); - ret = -EPROTO; - goto out; - } - GLCHK(glBindRenderbuffer(GL_RENDERBUFFER, mHmdFboRenderBuffer)); - GLCHK(glRenderbufferStorage(GL_RENDERBUFFER, - GL_DEPTH_COMPONENT16, - mHmdFboSize, - mHmdFboSize)); - - GLCHK(glFramebufferTexture2D(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - mHmdFboTexture, - 0)); - GLCHK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, - GL_DEPTH_ATTACHMENT, - GL_RENDERBUFFER, - mHmdFboRenderBuffer)); - - gle = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (gle != GL_FRAMEBUFFER_COMPLETE) { - PDRAW_LOGE("invalid framebuffer status"); - ret = -EPROTO; - goto out; - } - - GLCHK(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); - GLCHK(glClear(GL_COLOR_BUFFER_BIT)); - GLCHK(glBindTexture(GL_TEXTURE_2D, 0)); - GLCHK(glBindRenderbuffer(GL_RENDERBUFFER, 0)); - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); - - mGles2Hmd = new Gles2Hmd(mSession, - mGles2HmdFirstTexUnit, - mWidth, - mHeight, - mParams.hmd_ipd_offset, - mParams.hmd_x_offset, - mParams.hmd_y_offset); - if (mGles2Hmd == nullptr) { - PDRAW_LOGE("failed to create Gles2Hmd context"); - ret = -ENOMEM; - goto out; - } - - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); - if (mGles2Video) - mGles2Video->setDefaultFbo(mHmdFbo); - - return 0; - -out: - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); - if (mGles2Video) - mGles2Video->setDefaultFbo(mDefaultFbo); - - return ret; -} - - -/* Called on the rendering thread */ -int Gles2Renderer::stopHmd(void) -{ - GLCHK(); - - if (mGles2Hmd != nullptr) { - delete mGles2Hmd; - mGles2Hmd = nullptr; - } - if (mHmdFboRenderBuffer > 0) { - GLCHK(glDeleteRenderbuffers(1, &mHmdFboRenderBuffer)); - mHmdFboRenderBuffer = 0; - } - if (mHmdFboTexture > 0) { - GLCHK(glDeleteTextures(1, &mHmdFboTexture)); - mHmdFboTexture = 0; - } - if (mHmdFbo > 0) { - GLCHK(glDeleteFramebuffers(1, &mHmdFbo)); - mHmdFbo = 0; - } - if (mGles2Video) - mGles2Video->setDefaultFbo(mDefaultFbo); - mHmdFboSize = 0; - - return 0; -} - - -/* Called on the rendering thread */ -int Gles2Renderer::startExtLoad(void) -{ - int ret = 0, err; - GLenum gle; - - err = stopExtLoad(); - if (err < 0) - PDRAW_LOG_ERRNO("stopExtLoad", -err); - - GLCHK(); - - GLCHK(glGenFramebuffers(1, &mExtLoadFbo)); - if (mExtLoadFbo <= 0) { - PDRAW_LOGE("failed to create framebuffer"); - ret = -EPROTO; - goto out; - } - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mExtLoadFbo)); - - GLCHK(glGenTextures(1, &mExtLoadFboTexture)); - if (mExtLoadFboTexture <= 0) { - PDRAW_LOGE("failed to create texture"); - ret = -EPROTO; - goto out; - } - GLCHK(glActiveTexture(GL_TEXTURE0)); - GLCHK(glBindTexture(GL_TEXTURE_2D, mExtLoadFboTexture)); - GLCHK(glTexImage2D(GL_TEXTURE_2D, - 0, - GL_RGBA, - mExtVideoTextureWidth, - mExtVideoTextureHeight, - 0, - GL_RGBA, - GL_UNSIGNED_BYTE, - nullptr)); - - GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GLCHK(glTexParameterf( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GLCHK(glTexParameterf( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - - GLCHK(glFramebufferTexture2D(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - mExtLoadFboTexture, - 0)); - - gle = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (gle != GL_FRAMEBUFFER_COMPLETE) { - PDRAW_LOGE("invalid framebuffer status"); - ret = -EPROTO; - goto out; - } - - GLCHK(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); - GLCHK(glClear(GL_COLOR_BUFFER_BIT)); - GLCHK(glBindTexture(GL_TEXTURE_2D, 0)); - - if (mGles2Video) - mGles2Video->setExtTexture(mExtLoadFboTexture); - -out: - GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); - return ret; -} - - -/* Called on the rendering thread */ -int Gles2Renderer::stopExtLoad(void) -{ - GLCHK(); - - if (mExtLoadFboTexture > 0) { - GLCHK(glDeleteTextures(1, &mExtLoadFboTexture)); - mExtLoadFboTexture = 0; - } - if (mExtLoadFbo > 0) { - GLCHK(glDeleteFramebuffers(1, &mExtLoadFbo)); - mExtLoadFbo = 0; - } - - if (mGles2Video) - mGles2Video->setExtTexture(mExtLoadFboTexture); - - return 0; -} - - -/* Called on the rendering thread */ -int Gles2Renderer::resize(const struct pdraw_rect *renderPos) -{ - int ret; - - if (renderPos == nullptr) - return -EINVAL; - - mX = renderPos->x; - mY = renderPos->y; - mWidth = renderPos->width; - mHeight = renderPos->height; - - GLCHK(); - GLCHK(glViewport(mX, mY, mWidth, mHeight)); - - if (mParams.enable_hmd_distortion_correction) { - ret = startHmd(); - if (ret < 0) - PDRAW_LOG_ERRNO("startHmd", -ret); - } else { - ret = stopHmd(); - if (ret < 0) - PDRAW_LOG_ERRNO("stopHmd", -ret); - } - - return ret; -} - - -/* Called on the rendering thread */ -int Gles2Renderer::setMediaId(unsigned int mediaId) -{ - if (mediaId == mMediaId) - return 0; - - mMediaId = mediaId; - int ret = pomp_loop_idle_add_with_cookie( - mSession->getLoop(), idleRenewMedia, this, this); - if (ret < 0) - PDRAW_LOG_ERRNO("pomp_loop_idle_add_with_cookie", -ret); - - return 0; -} - - -/* Called on the rendering thread */ -unsigned int Gles2Renderer::getMediaId(void) -{ - return mCurrentMediaId; -} - - -/* Called on the rendering thread */ -int Gles2Renderer::setParams(const struct pdraw_video_renderer_params *params) -{ - int ret; - - if (params == nullptr) - return -EINVAL; - - mParams = *params; - - if (mParams.video_scale_factor == 0.f) - mParams.video_scale_factor = 1.f; - - if (mParams.enable_hmd_distortion_correction) { - ret = startHmd(); - if (ret < 0) - PDRAW_LOG_ERRNO("startHmd", -ret); - } else { - ret = stopHmd(); - if (ret < 0) - PDRAW_LOG_ERRNO("stopHmd", -ret); - } - - if (!mExtLoadVideoTexture) { - mExtVideoTextureWidth = 0; - mExtVideoTextureHeight = 0; - mParams.video_texture_width = 0; - mParams.video_texture_dar_width = 0; - mParams.video_texture_dar_height = 0; - } - mFirstFrame = true; - - return ret; -} - - -/* Called on the rendering thread */ -int Gles2Renderer::getParams(struct pdraw_video_renderer_params *params) -{ - if (params) - *params = mParams; - return 0; -} - -} /* namespace Pdraw */ - -#endif /* USE_GLES2 */ +/** + * Parrot Drones Awesome Video Viewer Library + * OpenGL ES 2.0 renderer + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_rndvidgl +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_renderer_gles2.hpp" +#include "pdraw_session.hpp" +#include "pdraw_settings.hpp" + +#ifdef USE_GLES2 + +# include +# include +# include + +# include + +namespace Pdraw { + +# define GLES2_RENDERER_ANCILLARY_DATA_KEY_INPUT_TIME \ + "pdraw.gles2_renderer.input_time" +# define GLES2_RENDERER_QUEUE_MAX_FRAMES 5 + +# define GLES2_RENDERER_DEFAULT_DELAY_MS 33 +# define GLES2_RENDERER_FADE_FROM_BLACK_DURATION 500000 +# define GLES2_RENDERER_FADE_TO_BLACK_DURATION 500000 +# define GLES2_RENDERER_FADE_TO_BLACK_AND_WHITE_DURATION 2000000 +# define GLES2_RENDERER_FADE_TO_BLACK_AND_WHITE_HOLD 50000 +# define GLES2_RENDERER_FADE_TO_BLUR_DURATION 2000000 +# define GLES2_RENDERER_FADE_TO_BLUR_HOLD 500000 +# define GLES2_RENDERER_FLASH_THEN_BLACK_AND_WHITE_DURATION 200000 +# define GLES2_RENDERER_FLASH_THEN_BLACK_AND_WHITE_HOLD 200000 +# define GLES2_RENDERER_WATCHDOG_TIME_S 2 +# define GLES2_RENDERER_VIDEO_PRES_STATS_TIME_MS 200 +# define GLES2_RENDERER_SCHED_ADAPTIVE_EPSILON_PERCENT 5 + +# define NB_SUPPORTED_FORMATS 4 +static struct vdef_raw_format supportedFormats[NB_SUPPORTED_FORMATS]; +static pthread_once_t supportedFormatsIsInit = PTHREAD_ONCE_INIT; +static void initializeSupportedFormats(void) +{ + supportedFormats[0] = vdef_i420; + supportedFormats[1] = vdef_nv12; + supportedFormats[2] = vdef_i420_10_16le; + supportedFormats[3] = vdef_nv12_10_16le_high; +} + + +/* Called on the rendering thread */ +Gles2Renderer::Gles2Renderer(Session *session, + Element::Listener *listener, + IPdraw::IVideoRenderer *renderer, + IPdraw::IVideoRenderer::Listener *rndListener, + unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + struct egl_display *eglDisplay) : + Renderer(session, + listener, + renderer, + rndListener, + Media::Type::RAW_VIDEO, + nullptr, + 0, + mediaId, + renderPos, + params, + eglDisplay) +{ + int ret; + + Element::setClassName(__func__); + mMediaId = mediaId; + mCurrentMediaId = 0; + mRunning = false; + mCurrentFrame = nullptr; + mCurrentFrameData = {}; + mCurrentFrameInfo = {}; + mCurrentFrameMetadata = nullptr; + mLastAddedMedia = nullptr; + mMediaInfo = {}; + mMediaInfoSessionMeta = {}; + mTimer = nullptr; + mGles2Video = nullptr; + mGles2HmdFirstTexUnit = 0; + mGles2VideoFirstTexUnit = + mGles2HmdFirstTexUnit + Gles2Hmd::getTexUnitCount(); + mGles2Hmd = nullptr; + mDefaultFbo = 0; + mHmdFboSize = 0; + mHmdFbo = 0; + mHmdFboTexture = 0; + mHmdFboRenderBuffer = 0; + mExtLoadFbo = 0; + mExtLoadFboTexture = 0; + mX = 0; + mY = 0; + mWidth = 0; + mHeight = 0; + mPendingTransition = Transition::NONE; + mCurrentTransition = Transition::NONE; + mTransitionStartTime = 0; + mTransitionHoldTime = 0; + memset(&mParams, 0, sizeof(mParams)); + mExtLoadVideoTexture = false; + mExtVideoTextureWidth = 0; + mExtVideoTextureHeight = 0; + mRenderVideoOverlay = false; + mFirstFrame = false; + mFrameLoaded = false; + mLastRenderTimestamp = UINT64_MAX; + mLastFrameTimestamp = UINT64_MAX; + mRenderReadyScheduled = false; + mVideoPresStatsTimer = nullptr; + mSchedLastInputTimestamp = UINT64_MAX; + mSchedLastOutputTimestamp = UINT64_MAX; + mFrameLoadedLogged = false; + mWatchdogTimer = nullptr; + mWatchdogTriggered = false; + + (void)pthread_once(&supportedFormatsIsInit, initializeSupportedFormats); + setRawVideoMediaFormatCaps(supportedFormats, NB_SUPPORTED_FORMATS); + + if (mRendererListener != nullptr) { + ret = mRendererListener->loadVideoTexture( + nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr, 0); + if (ret != -ENOSYS) + mExtLoadVideoTexture = true; + ret = mRendererListener->renderVideoOverlay(nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr); + if (ret != -ENOSYS) + mRenderVideoOverlay = true; + } + + if (renderPos != nullptr && params != nullptr) { + ret = setup(renderPos, params, eglDisplay); + if (ret != 0) + return; + } + + /* Post a message on the loop thread */ + setStateAsyncNotify(CREATED); + return; +} + + +/* Must be called on the loop thread */ +Gles2Renderer::~Gles2Renderer(void) +{ + int ret; + + if (mState == STARTED) + PDRAW_LOGW("renderer is still running"); + + /* Make sure listener function will no longer be called */ + removeRendererListener(); + mExtLoadVideoTexture = false; + mRenderVideoOverlay = false; + + /* Remove any leftover idle callbacks */ + ret = pomp_loop_idle_remove_by_cookie(mSession->getLoop(), this); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_loop_idle_remove_by_cookie", -ret); + + unsigned int count = getInputMediaCount(); + if (count > 0) { + PDRAW_LOGW("not all input media have been removed"); + ret = removeInputMedias(); + if (ret < 0) + PDRAW_LOG_ERRNO("removeInputMedias", -ret); + } + + if (mCurrentFrameMetadata != nullptr) { + vmeta_frame_unref(mCurrentFrameMetadata); + mCurrentFrameMetadata = nullptr; + } + if (mCurrentFrame != nullptr) { + int releaseRet = mbuf_raw_video_frame_unref(mCurrentFrame); + if (releaseRet < 0) + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_unref", + -releaseRet); + mCurrentFrame = nullptr; + } + + Media::cleanupMediaInfo(&mMediaInfo); + + if (mTimer != nullptr) { + ret = pomp_timer_clear(mTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); + ret = pomp_timer_destroy(mTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_destroy", -ret); + mTimer = nullptr; + } + if (mWatchdogTimer != nullptr) { + ret = pomp_timer_clear(mWatchdogTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); + ret = pomp_timer_destroy(mWatchdogTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_destroy", -ret); + mWatchdogTimer = nullptr; + } + if (mVideoPresStatsTimer != nullptr) { + ret = pomp_timer_clear(mVideoPresStatsTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); + ret = pomp_timer_destroy(mVideoPresStatsTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_destroy", -ret); + mVideoPresStatsTimer = nullptr; + } + + if (mGles2Video != nullptr) { + delete mGles2Video; + mGles2Video = nullptr; + } + if (mGles2Hmd != nullptr) { + delete mGles2Hmd; + mGles2Hmd = nullptr; + } +} + + +/* Called on the rendering thread */ +int Gles2Renderer::setup(const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + struct egl_display *eglDisplay) +{ + int ret = 0; + + if (params == nullptr) + return -EINVAL; + if (renderPos == nullptr) + return -EINVAL; + + if ((mState != INVALID) && (mState != CREATED)) { + PDRAW_LOGE("invalid state"); + return -EPROTO; + } + + GLCHK(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mDefaultFbo)); + + ret = resize(renderPos); + if (ret < 0) { + PDRAW_LOG_ERRNO("resize", -ret); + goto out; + } + + ret = setParams(params); + if (ret < 0) { + PDRAW_LOG_ERRNO("setParams", -ret); + goto out; + } + + mGles2Video = new Gles2Video(mSession, + (mParams.enable_hmd_distortion_correction) + ? mHmdFbo + : mDefaultFbo, + mGles2VideoFirstTexUnit); + if (mGles2Video == nullptr) { + ret = -ENOMEM; + PDRAW_LOG_ERRNO("failed to create Gles2Video", -ret); + goto out; + } + mGles2Video->setExtTexture(mExtLoadFboTexture); + +out: + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); + return ret; +} + + +/* Called on the rendering thread */ +int Gles2Renderer::start(void) +{ + if ((mState == STARTED) || (mState == STARTING)) { + return 0; + } + if (mState != CREATED) { + PDRAW_LOGE("renderer is not created"); + return -EPROTO; + } + /* Post a message on the loop thread */ + setStateAsyncNotify(STARTING); + + mRunning = true; + + int ret = pomp_loop_idle_add_with_cookie( + mSession->getLoop(), idleStart, this, this); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_loop_idle_add_with_cookie", -ret); + + return ret; +} + +/* Called on the loop thread by start() */ +void Gles2Renderer::idleStart(void *renderer) +{ + Gles2Renderer *self = reinterpret_cast(renderer); + ULOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + int ret; + + if (self->mState != STARTING) { + PDRAW_LOGE("renderer is not starting"); + return; + } + + if (self->mTimer == nullptr) { + self->mTimer = pomp_timer_new( + self->mSession->getLoop(), timerCb, self); + if (self->mTimer == nullptr) { + PDRAW_LOGE("pomp_timer_new failed"); + goto err; + } + } + if (self->mWatchdogTimer == nullptr) { + self->mWatchdogTimer = pomp_timer_new( + self->mSession->getLoop(), watchdogTimerCb, self); + if (self->mWatchdogTimer == nullptr) { + PDRAW_LOGE("pomp_timer_new failed"); + goto err; + } + } + if (self->mVideoPresStatsTimer == nullptr) { + self->mVideoPresStatsTimer = pomp_timer_new( + self->mSession->getLoop(), videoPresStatsTimerCb, self); + if (self->mVideoPresStatsTimer == nullptr) { + PDRAW_LOGE("pomp_timer_new failed"); + goto err; + } + } + + pthread_mutex_lock(&self->mListenerMutex); + if (self->mRendererListener != nullptr) { + ret = pomp_timer_set(self->mTimer, + GLES2_RENDERER_DEFAULT_DELAY_MS); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_set", -ret); + } + pthread_mutex_unlock(&self->mListenerMutex); + + /* Post a message on the loop thread */ + self->setState(STARTED); + return; + +err: + if (self->mTimer != nullptr) { + ret = pomp_timer_clear(self->mTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); + ret = pomp_timer_destroy(self->mTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_destroy", -ret); + self->mTimer = nullptr; + } + if (self->mWatchdogTimer != nullptr) { + ret = pomp_timer_clear(self->mWatchdogTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); + ret = pomp_timer_destroy(self->mWatchdogTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_destroy", -ret); + self->mWatchdogTimer = nullptr; + } + if (self->mVideoPresStatsTimer != nullptr) { + ret = pomp_timer_clear(self->mVideoPresStatsTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); + ret = pomp_timer_destroy(self->mVideoPresStatsTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_destroy", -ret); + self->mVideoPresStatsTimer = nullptr; + } +} + + +/* Called on the rendering thread */ +int Gles2Renderer::stop(void) +{ + int ret; + + if ((mState == STOPPED) || (mState == STOPPING)) + return 0; + + /* Post a message on the loop thread */ + setStateAsyncNotify(STOPPING); + + mRunning = false; + + removeRendererListener(); + mExtLoadVideoTexture = false; + mRenderVideoOverlay = false; + + if (mGles2Video != nullptr) { + delete mGles2Video; + mGles2Video = nullptr; + } + + ret = stopHmd(); + if (ret < 0) + PDRAW_LOG_ERRNO("stopHmd", -ret); + ret = stopExtLoad(); + if (ret < 0) + PDRAW_LOG_ERRNO("stopExtLoad", -ret); + + /* Remove any leftover idle callbacks */ + ret = pomp_loop_idle_remove_by_cookie(mSession->getLoop(), this); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_loop_idle_remove_by_cookie", -ret); + + /* Post a message on the loop thread */ + asyncCompleteStop(); + + return 0; +} + + +/* Called on the loop thread */ +void Gles2Renderer::completeStop(void) +{ + int ret; + + if (mState == STOPPED) + return; + + if (mTimer != nullptr) { + ret = pomp_timer_clear(mTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); + } + if (mWatchdogTimer != nullptr) { + ret = pomp_timer_clear(mWatchdogTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); + } + if (mVideoPresStatsTimer != nullptr) { + ret = pomp_timer_clear(mVideoPresStatsTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); + } + + ret = removeInputMedias(); + if (ret < 0) + PDRAW_LOG_ERRNO("removeInputMedias", -ret); + + setState(STOPPED); +} + + +/* Called on the loop thread */ +void Gles2Renderer::queueEventCb(struct pomp_evt *evt, void *userdata) +{ + Gles2Renderer *self = (Gles2Renderer *)userdata; + int res = 0; + uint32_t delayMs; + uint64_t delayUs = 0; + + if (self == nullptr) { + PDRAW_LOGE("invalid renderer pointer"); + return; + } + + if ((!self->mRunning) || (self->mState != STARTED)) + return; + + self->RawSink::lock(); + + if (self->mLastAddedMedia == nullptr) { + /* No current media */ + self->RawSink::unlock(); + return; + } + + struct mbuf_raw_video_frame_queue *queue = + self->getLastAddedMediaQueue(); + if (queue == nullptr) { + self->RawSink::unlock(); + return; + } + + uint64_t curTime = 0; + struct timespec ts = {0, 0}; + res = time_get_monotonic(&ts); + if (res < 0) + PDRAW_LOG_ERRNO("time_get_monotonic", -res); + res = time_timespec_to_us(&ts, &curTime); + if (res < 0) + PDRAW_LOG_ERRNO("time_timespec_to_us", -res); + + res = self->getNextFrameDelay(queue, + curTime, + false, + nullptr, + &delayUs, + nullptr, + nullptr, + nullptr, + 0); + if (res < 0) { + PDRAW_LOG_ERRNO("getNextFrameDelay", -res); + self->RawSink::unlock(); + return; + } + + self->RawSink::unlock(); + + pthread_mutex_lock(&self->mListenerMutex); + if (self->mRendererListener && !self->mRenderReadyScheduled) { + delayMs = (delayUs + 500) / 1000; + if (delayMs == 0) { + /* Signal "render ready" now and schedule a renderer + * keepalive timer */ + self->mRendererListener->onVideoRenderReady( + self->mSession, self->mRenderer); + delayMs = GLES2_RENDERER_DEFAULT_DELAY_MS; + } else { + /* Only true when the timer is set by an incoming + * delayed frame and not the renderer keepalive timer */ + self->mRenderReadyScheduled = true; + } + res = pomp_timer_set(self->mTimer, delayMs); + if (res < 0) { + PDRAW_LOG_ERRNO("pomp_timer_set", -res); + self->mRenderReadyScheduled = false; + } + } + pthread_mutex_unlock(&self->mListenerMutex); +} + + +/* Called on the loop thread */ +void Gles2Renderer::timerCb(struct pomp_timer *timer, void *userdata) +{ + Gles2Renderer *self = (Gles2Renderer *)userdata; + int res; + uint32_t delayMs; + uint64_t delayUs = 0; + + if (self == nullptr) { + PDRAW_LOGE("invalid renderer pointer"); + return; + } + + if ((!self->mRunning) || (self->mState != STARTED)) + return; + + self->RawSink::lock(); + + if (self->mLastAddedMedia == nullptr) { + /* No current media */ + self->RawSink::unlock(); + return; + } + + struct mbuf_raw_video_frame_queue *queue = + self->getLastAddedMediaQueue(); + if (queue == nullptr) { + self->RawSink::unlock(); + return; + } + + uint64_t curTime = 0; + struct timespec ts = {0, 0}; + res = time_get_monotonic(&ts); + if (res < 0) + PDRAW_LOG_ERRNO("time_get_monotonic", -res); + res = time_timespec_to_us(&ts, &curTime); + if (res < 0) + PDRAW_LOG_ERRNO("time_timespec_to_us", -res); + + res = self->getNextFrameDelay(queue, + curTime, + false, + nullptr, + &delayUs, + nullptr, + nullptr, + nullptr, + 0); + if ((res < 0) && (res != -EAGAIN)) { + PDRAW_LOG_ERRNO("getNextFrameDelay", -res); + self->RawSink::unlock(); + return; + } + + self->RawSink::unlock(); + + pthread_mutex_lock(&self->mListenerMutex); + if (self->mRendererListener) { + delayMs = (delayUs + 500) / 1000; + self->mRendererListener->onVideoRenderReady(self->mSession, + self->mRenderer); + if ((res == -EAGAIN) || (delayMs == 0)) + delayMs = GLES2_RENDERER_DEFAULT_DELAY_MS; + res = pomp_timer_set(self->mTimer, delayMs); + if (res < 0) + PDRAW_LOG_ERRNO("pomp_timer_set", -res); + } + pthread_mutex_unlock(&self->mListenerMutex); +} + + +/* Called on the loop thread */ +void Gles2Renderer::watchdogTimerCb(struct pomp_timer *timer, void *userdata) +{ + Gles2Renderer *self = (Gles2Renderer *)userdata; + + if ((!self->mRunning) || (self->mState != STARTED)) + return; + + bool expected = false; + if (self->mWatchdogTriggered.compare_exchange_strong(expected, true)) { + PDRAW_LOGW("no new frame for %ds", + GLES2_RENDERER_WATCHDOG_TIME_S); + } +} + + +/* Called on the loop thread */ +void Gles2Renderer::videoPresStatsTimerCb(struct pomp_timer *timer, + void *userdata) +{ + Gles2Renderer *self = (Gles2Renderer *)userdata; + + if ((!self->mRunning) || (self->mState != STARTED)) + return; + + self->RawSink::lock(); + if (self->mLastAddedMedia == nullptr) { + /* No current media */ + self->RawSink::unlock(); + return; + } + + RawChannel *channel = self->getInputChannel(self->mLastAddedMedia); + if (channel == nullptr) { + self->RawSink::unlock(); + PDRAW_LOG_ERRNO("failed to get input port", EPROTO); + return; + } + + int err = channel->sendVideoPresStats(&self->mVideoPresStats); + if (err < 0) + PDRAW_LOG_ERRNO("channel->sendVideoPresStats", -err); + + self->RawSink::unlock(); +} + + +/* Must be called on the loop thread */ +void Gles2Renderer::onChannelFlush(RawChannel *channel) +{ + int ret; + + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + PDRAW_LOGD("flushing input channel"); + + RawSink::lock(); + + struct mbuf_raw_video_frame_queue *queue = channel->getQueue(); + if (queue != nullptr) { + ret = mbuf_raw_video_frame_queue_flush(queue); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_flush", + -ret); + } + if (mCurrentFrame != nullptr) { + int releaseRet = mbuf_raw_video_frame_unref(mCurrentFrame); + if (releaseRet < 0) + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_unref", + -releaseRet); + mCurrentFrame = nullptr; + } + + RawSink::unlock(); + + ret = channel->flushDone(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->flushDone", -ret); +} + + +/* Must be called on the loop thread */ +void Gles2Renderer::onChannelSos(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSink::lock(); + + RawSink::onChannelSos(channel); + if (mParams.enable_transition_flags & + PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_SOS) + mPendingTransition = Transition::FADE_FROM_BLACK; + + RawSink::unlock(); +} + + +/* Must be called on the loop thread */ +void Gles2Renderer::onChannelEos(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSink::lock(); + + RawSink::onChannelEos(channel); + int ret = pomp_timer_clear(mWatchdogTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); + ret = pomp_timer_clear(mVideoPresStatsTimer); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_timer_clear", -ret); + if (mParams.enable_transition_flags & + PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_EOS) + mPendingTransition = Transition::FADE_TO_BLACK; + + RawSink::unlock(); +} + + +/* Must be called on the loop thread */ +void Gles2Renderer::onChannelReconfigure(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSink::lock(); + + RawSink::onChannelReconfigure(channel); + if (mParams.enable_transition_flags & + PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_RECONFIGURE) + mPendingTransition = Transition::FADE_TO_BLUR; + + RawSink::unlock(); +} + + +/* Must be called on the loop thread */ +void Gles2Renderer::onChannelTimeout(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSink::lock(); + + RawSink::onChannelTimeout(channel); + if (mParams.enable_transition_flags & + PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_TIMEOUT) + mPendingTransition = Transition::FADE_TO_BLACK_AND_WHITE; + + RawSink::unlock(); +} + + +/* Must be called on the loop thread */ +void Gles2Renderer::onChannelPhotoTrigger(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSink::lock(); + + RawSink::onChannelPhotoTrigger(channel); + if (mParams.enable_transition_flags & + PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_PHOTO_TRIGGER) + mPendingTransition = Transition::FLASH_THEN_BLACK_AND_WHITE; + + RawSink::unlock(); +} + + +bool Gles2Renderer::queueFilter(struct mbuf_raw_video_frame *frame, + void *userdata) +{ + int err; + uint64_t ts_us; + struct timespec cur_ts = {0, 0}; + + /* Set the input time ancillary data to the frame */ + time_get_monotonic(&cur_ts); + time_timespec_to_us(&cur_ts, &ts_us); + err = mbuf_raw_video_frame_add_ancillary_buffer( + frame, + GLES2_RENDERER_ANCILLARY_DATA_KEY_INPUT_TIME, + &ts_us, + sizeof(ts_us)); + if (err < 0) + ULOG_ERRNO("mbuf_raw_video_frame_add_ancillary_buffer", -err); + + return true; +} + + +uint64_t Gles2Renderer::getFrameU64(struct mbuf_raw_video_frame *frame, + const char *key) +{ + int res; + struct mbuf_ancillary_data *data; + uint64_t val = 0; + const void *raw_data; + size_t len; + + res = mbuf_raw_video_frame_get_ancillary_data(frame, key, &data); + if (res < 0) + return 0; + + raw_data = mbuf_ancillary_data_get_buffer(data, &len); + if (!raw_data || len != sizeof(val)) + goto out; + memcpy(&val, raw_data, sizeof(val)); + +out: + mbuf_ancillary_data_unref(data); + return val; +} + + +struct mbuf_raw_video_frame_queue *Gles2Renderer::getLastAddedMediaQueue(void) +{ + RawSink::lock(); + RawChannel *channel = getInputChannel(mLastAddedMedia); + if (channel == nullptr) { + PDRAW_LOGE("failed to get input channel"); + RawSink::unlock(); + return nullptr; + } + struct mbuf_raw_video_frame_queue *queue = channel->getQueue(); + if (queue == nullptr) { + PDRAW_LOGE("failed to get input queue"); + RawSink::unlock(); + return nullptr; + } + RawSink::unlock(); + return queue; +} + + +/* Must be called on the loop thread */ +int Gles2Renderer::addInputMedia(RawVideoMedia *media) +{ + struct pomp_loop *loop = nullptr; + struct pomp_evt *evt = nullptr; + int res = 0; + + if ((mMediaId != 0) && (mMediaId != media->id)) + return -EPERM; + if (mLastAddedMedia != nullptr) + return -EBUSY; + if ((!mRunning) || (mState != STARTED)) + return -EAGAIN; + + RawSink::lock(); + + mFirstFrame = true; + + /* Reset the scheduling */ + mSchedLastInputTimestamp = UINT64_MAX; + mSchedLastOutputTimestamp = UINT64_MAX; + PDRAW_LOGD("RESET SCHEDULING"); + + res = RawSink::addInputMedia(media); + if (res == -EEXIST) { + RawSink::unlock(); + return res; + } else if (res < 0) { + RawSink::unlock(); + PDRAW_LOG_ERRNO("RawSink::addInputMedia", -res); + return res; + } + + RawChannel *channel = getInputChannel(media); + if (channel == nullptr) { + RawSink::unlock(); + PDRAW_LOGE("failed to get channel"); + return -EPROTO; + } + + channel->setKey(this); + struct mbuf_raw_video_frame_queue_args args = {}; + args.filter = &queueFilter; + args.filter_userdata = this; + args.max_frames = GLES2_RENDERER_QUEUE_MAX_FRAMES; + struct mbuf_raw_video_frame_queue *queue; + res = mbuf_raw_video_frame_queue_new_with_args(&args, &queue); + if (res < 0) { + RawSink::unlock(); + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_new_with_args", + -res); + return res; + } + channel->setQueue(queue); + + res = mbuf_raw_video_frame_queue_get_event(queue, &evt); + if (res < 0) { + RawSink::unlock(); + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_get_event", -res); + goto error; + } + + loop = mSession->getLoop(); + if (loop == nullptr) { + RawSink::unlock(); + PDRAW_LOGE("loop not found"); + res = -ENODEV; + goto error; + } + + res = pomp_evt_attach_to_loop(evt, loop, &queueEventCb, this); + if (res < 0) { + RawSink::unlock(); + PDRAW_LOG_ERRNO("pomp_evt_attach_to_loop", -res); + goto error; + } + + mLastAddedMedia = media; + mCurrentMediaId = mMediaId; + + media->fillMediaInfo(&mMediaInfo); + /* Deep copy: copy the session metadata */ + mMediaInfoSessionMeta = *mMediaInfo.session_meta; + mMediaInfo.session_meta = &mMediaInfoSessionMeta; + + RawSink::unlock(); + + pthread_mutex_lock(&mListenerMutex); + if (mRendererListener) { + mRendererListener->onVideoRendererMediaAdded( + mSession, mRenderer, &mMediaInfo); + } + pthread_mutex_unlock(&mListenerMutex); + + return 0; + +error: + removeQueueFdFromPomp(queue); + return res; +} + + +/* Must be called on the loop thread */ +int Gles2Renderer::removeQueueFdFromPomp( + struct mbuf_raw_video_frame_queue *queue) +{ + int ret; + struct pomp_loop *loop = nullptr; + struct pomp_evt *evt = nullptr; + + loop = mSession->getLoop(); + if (loop == nullptr) { + PDRAW_LOGE("loop not found"); + return -ENODEV; + } + + ret = mbuf_raw_video_frame_queue_get_event(queue, &evt); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_get_event", -ret); + return ret; + } + + ret = pomp_evt_detach_from_loop(evt, loop); + if (ret < 0) { + PDRAW_LOG_ERRNO("pomp_evt_detach_from_loop", -ret); + return ret; + } + + return 0; +} + + +/* Must be called on the loop thread */ +int Gles2Renderer::removeInputMedia(RawVideoMedia *media) +{ + int ret; + + RawSink::lock(); + + if (mLastAddedMedia == media) { + mLastAddedMedia = nullptr; + mCurrentMediaId = 0; + pthread_mutex_lock(&mListenerMutex); + if (mRendererListener) { + mRendererListener->onVideoRendererMediaRemoved( + mSession, mRenderer, &mMediaInfo); + } + pthread_mutex_unlock(&mListenerMutex); + + if (mCurrentFrame != nullptr) { + int releaseRet = + mbuf_raw_video_frame_unref(mCurrentFrame); + if (releaseRet < 0) + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_unref", + -releaseRet); + mCurrentFrame = nullptr; + } + + Media::cleanupMediaInfo(&mMediaInfo); + } + + + RawChannel *channel = getInputChannel(media); + if (channel == nullptr) { + RawSink::unlock(); + PDRAW_LOGE("failed to get channel"); + return -EPROTO; + } + /* Keep a reference on the queue to destroy it after removing the + * input media (avoids deadlocks when trying to push new frames out + * of the VideoDecoder whereas the queue is already destroyed) */ + struct mbuf_raw_video_frame_queue *queue = channel->getQueue(); + + ret = RawSink::removeInputMedia(media); + if (ret < 0) { + RawSink::unlock(); + PDRAW_LOG_ERRNO("RawSink::removeInputMedia", -ret); + return ret; + } + + RawSink::unlock(); + + if (queue != nullptr) { + ret = removeQueueFdFromPomp(queue); + if (ret < 0) + PDRAW_LOG_ERRNO("removeQueueFdFromPomp", -ret); + ret = mbuf_raw_video_frame_queue_flush(queue); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_flush", + -ret); + ret = mbuf_raw_video_frame_queue_destroy(queue); + if (ret < 0) + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_destroy", + -ret); + } + + return 0; +} + + +/* Must be called on the loop thread */ +int Gles2Renderer::removeInputMedias(void) +{ + int ret, inputMediaCount, i; + + RawSink::lock(); + + inputMediaCount = getInputMediaCount(); + + /* Note: loop downwards because calling removeInputMedia removes + * input ports and decreases the media count */ + for (i = inputMediaCount - 1; i >= 0; i--) { + RawVideoMedia *media = getInputMedia(i); + if (media == nullptr) { + PDRAW_LOG_ERRNO("getInputMedia", ENOENT); + continue; + } + ret = removeInputMedia(media); + if (ret < 0) { + PDRAW_LOG_ERRNO("removeInputMedia", -ret); + continue; + } + } + + mLastAddedMedia = nullptr; + mCurrentMediaId = 0; + RawSink::unlock(); + + return 0; +} + + +void Gles2Renderer::idleRenewMedia(void *userdata) +{ + Gles2Renderer *self = reinterpret_cast(userdata); + PDRAW_LOG_ERRNO_RETURN_IF(self == nullptr, EINVAL); + if (self->mLastAddedMedia != nullptr) + self->removeInputMedia(self->mLastAddedMedia); + self->mSession->addMediaToRenderer(self->mMediaId, self); +} + + +/* Called on the rendering thread */ +void Gles2Renderer::abortTransition(void) +{ + RawSink::lock(); + mCurrentTransition = Transition::NONE; + mTransitionStartTime = 0; + mTransitionHoldTime = 0; + RawSink::unlock(); + + if (mGles2Video) { + mGles2Video->setSatCoef(1.0f); + mGles2Video->setLightCoef(1.0f); + mGles2Video->setDarkCoef(1.0f); + mGles2Video->abortTransition(); + } +} + + +/* Called on the rendering thread */ +int Gles2Renderer::doTransition(uint64_t timestamp, + bool frameReady, + bool *loadFrame) +{ + if (timestamp == 0) + return -EINVAL; + if (loadFrame == nullptr) + return -EINVAL; + + RawSink::lock(); + + if (mPendingTransition == Transition::NONE) + goto out; + + if (mCurrentTransition != Transition::NONE) { + /* Abort previous transition */ + abortTransition(); + } + + mCurrentTransition = mPendingTransition; + mPendingTransition = Transition::NONE; + mTransitionStartTime = timestamp; + + switch (mCurrentTransition) { + case Transition::FADE_FROM_BLACK: + mTransitionHoldTime = GLES2_RENDERER_FADE_FROM_BLACK_DURATION; + if (mGles2Video) { + mGles2Video->startTransition( + GLES2_VIDEO_TRANSITION_FADE_FROM_BLACK, + GLES2_RENDERER_FADE_FROM_BLACK_DURATION, + false); + } + break; + case Transition::FADE_TO_BLACK: + mTransitionHoldTime = GLES2_RENDERER_FADE_TO_BLACK_DURATION; + if (mGles2Video) { + mGles2Video->startTransition( + GLES2_VIDEO_TRANSITION_FADE_TO_BLACK, + GLES2_RENDERER_FADE_TO_BLACK_DURATION, + true); + } + break; + case Transition::FADE_TO_BLACK_AND_WHITE: + mTransitionHoldTime = + GLES2_RENDERER_FADE_TO_BLACK_AND_WHITE_HOLD; + if (mGles2Video) { + mGles2Video->startTransition( + GLES2_VIDEO_TRANSITION_FADE_TO_BLACK_AND_WHITE, + GLES2_RENDERER_FADE_TO_BLACK_AND_WHITE_DURATION, + true); + } + break; + case Transition::FADE_TO_BLUR: + mTransitionHoldTime = GLES2_RENDERER_FADE_TO_BLUR_HOLD; + if (mGles2Video) { + mGles2Video->startTransition( + GLES2_VIDEO_TRANSITION_FADE_TO_BLUR, + GLES2_RENDERER_FADE_TO_BLUR_DURATION, + true); + } + break; + case Transition::FLASH_THEN_BLACK_AND_WHITE: + mTransitionHoldTime = + GLES2_RENDERER_FLASH_THEN_BLACK_AND_WHITE_HOLD; + if (mGles2Video) { + mGles2Video->startTransition( + GLES2_VIDEO_TRANSITION_FLASH, + GLES2_RENDERER_FLASH_THEN_BLACK_AND_WHITE_DURATION, + true); + } + break; + default: + abortTransition(); + break; + } + +out: + if ((frameReady) && (mCurrentTransition != Transition::NONE) && + (timestamp >= mTransitionStartTime + mTransitionHoldTime)) { + /* End the transition */ + abortTransition(); + *loadFrame = true; + } else if ((frameReady) && + ((mCurrentTransition == Transition::NONE) || + (mCurrentTransition == Transition::FADE_FROM_BLACK) || + (mCurrentTransition == + Transition::FLASH_THEN_BLACK_AND_WHITE))) { + *loadFrame = true; + } else { + *loadFrame = false; + } + RawSink::unlock(); + return 0; +} + + +/* Called on the rendering thread */ +int Gles2Renderer::loadVideoFrame(struct mbuf_raw_video_frame *mbufFrame, + RawVideoMedia::Frame *frame) +{ + int ret; + unsigned int planeCount = 0; + const void *planes[VDEF_RAW_MAX_PLANE_COUNT] = {}; + + if (vdef_dim_is_null(&mCurrentFrameInfo.info.resolution) || + vdef_dim_is_null(&mCurrentFrameInfo.info.sar)) { + PDRAW_LOGE("invalid frame dimensions"); + ret = -EINVAL; + goto out; + } + + planeCount = vdef_get_raw_frame_plane_count(&mCurrentFrameInfo.format); + for (unsigned int i = 0; i < planeCount; i++) { + size_t dummyPlaneLen; + ret = mbuf_raw_video_frame_get_plane( + mbufFrame, i, &planes[i], &dummyPlaneLen); + if (ret < 0) { + PDRAW_LOG_ERRNO( + "mbuf_raw_video_frame_get_plane(%u)", -ret, i); + goto out; + } + } + + ret = mGles2Video->loadFrame((const uint8_t **)planes, + mCurrentFrameInfo.plane_stride, + &mCurrentFrameInfo.format, + &mCurrentFrameInfo.info); + if (ret < 0) + PDRAW_LOG_ERRNO("gles2Video->loadFrame", -ret); + +out: + for (unsigned int i = 0; i < planeCount; i++) { + if (planes[i] == nullptr) + continue; + int err = mbuf_raw_video_frame_release_plane( + mbufFrame, i, planes[i]); + if (err < 0) + PDRAW_LOG_ERRNO( + "mbuf_raw_video_frame_release_plane(%u)", + -err, + i); + } + + mFrameLoaded = (ret == 0); + return ret; +} + + +/* Called on the rendering thread */ +int Gles2Renderer::loadExternalVideoFrame( + struct mbuf_raw_video_frame *mbufFrame, + RawVideoMedia::Frame *frame, + const struct pdraw_media_info *mediaInfo) +{ + int ret; + struct mbuf_ancillary_data *userData = nullptr; + size_t frameUserdataLen; + const void *frameUserdata = nullptr; + + ret = mbuf_raw_video_frame_get_ancillary_data( + mbufFrame, MBUF_ANCILLARY_KEY_USERDATA_SEI, &userData); + if (ret == -ENOENT) { + /* No userdata */ + frameUserdata = nullptr; + frameUserdataLen = 0; + } else if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_get_ancillary_data", + -ret); + goto out; + } else { + frameUserdata = mbuf_ancillary_data_get_buffer( + userData, &frameUserdataLen); + } + + if (vdef_dim_is_null(&mCurrentFrameInfo.info.resolution) || + vdef_dim_is_null(&mCurrentFrameInfo.info.sar)) { + PDRAW_LOGE("invalid frame dimensions"); + ret = -EINVAL; + goto out; + } + + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mExtLoadFbo)); + GLCHK(glViewport(0, 0, mExtVideoTextureWidth, mExtVideoTextureHeight)); + + /* Texture loading callback function */ + pthread_mutex_lock(&mListenerMutex); + if (mRendererListener != nullptr) { + ret = mRendererListener->loadVideoTexture( + mSession, + mRenderer, + mExtVideoTextureWidth, + mExtVideoTextureHeight, + mediaInfo, + mbufFrame, + frameUserdata, + (size_t)frameUserdataLen); + } + pthread_mutex_unlock(&mListenerMutex); + + if (mParams.enable_hmd_distortion_correction) { + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mHmdFbo)); + GLCHK(glViewport(0, 0, mHmdFboSize, mHmdFboSize)); + } else { + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); + GLCHK(glViewport(mX, mY, mWidth, mHeight)); + } + +out: + if (userData) + mbuf_ancillary_data_unref(userData); + mFrameLoaded = (ret == 0); + return ret; +} + + +/* Called on the rendering thread */ +int Gles2Renderer::renderVideoFrame(const struct pdraw_rect *renderPos, + struct pdraw_rect *contentPos, + Eigen::Matrix4f &viewProjMat) +{ + struct vdef_rect crop = { + .left = 0, + .top = 0, + .width = mCurrentFrameInfo.info.resolution.width, + .height = mCurrentFrameInfo.info.resolution.height, + }; + + return mGles2Video->renderFrame(renderPos, + contentPos, + viewProjMat, + mCurrentFrameInfo.plane_stride, + &mCurrentFrameInfo.format, + &mCurrentFrameInfo.info, + &crop, + mCurrentFrameMetadata, + &mParams); +} + + +/* Called on the rendering thread */ +int Gles2Renderer::renderExternalVideoFrame(const struct pdraw_rect *renderPos, + struct pdraw_rect *contentPos, + Eigen::Matrix4f &viewProjMat) +{ + struct vdef_frame_info info = mCurrentFrameInfo.info; + struct vdef_rect crop = { + .left = 0, + .top = 0, + .width = mExtVideoTextureWidth, + .height = mExtVideoTextureHeight, + }; + size_t frameStride[3] = {mExtVideoTextureWidth, 0, 0}; + + info.sar.width = mCurrentFrameInfo.info.resolution.width * + mExtVideoTextureHeight; + info.sar.height = mCurrentFrameInfo.info.resolution.height * + mExtVideoTextureWidth; + + return mGles2Video->renderFrame(renderPos, + contentPos, + viewProjMat, + frameStride, + &vdef_rgb, + &info, + &crop, + mCurrentFrameMetadata, + &mParams); +} + + +int Gles2Renderer::setupExtTexture(const struct vdef_raw_frame *frameInfo, + const RawVideoMedia::Frame *frame) +{ + int ret = 0; + + PDRAW_LOGI( + "external video texture: source=%ux%u (SAR=%u:%u) " + "DAR=%u:%u textureWidth=%u", + frameInfo->info.resolution.width, + frameInfo->info.resolution.height, + frameInfo->info.sar.width, + frameInfo->info.sar.height, + mParams.video_texture_dar_width, + mParams.video_texture_dar_height, + mParams.video_texture_width); + + /* Compute the external texture size */ + if (mParams.video_texture_dar_width != 0 && + mParams.video_texture_dar_height != 0) { + /* Custom DAR is provided */ + if (mParams.video_texture_width > 0) { + /* Custom DAR with custom texture width */ + mExtVideoTextureWidth = mParams.video_texture_width; + mExtVideoTextureHeight = + (mParams.video_texture_width * + mParams.video_texture_dar_height + + mParams.video_texture_dar_width / 2) / + mParams.video_texture_dar_width; + } else { + /* Custom DAR without custom texture width */ + float dar, ar; + dar = (float)mParams.video_texture_dar_width / + (float)mParams.video_texture_dar_height; + ar = (float)frameInfo->info.resolution.width / + (float)frameInfo->info.resolution.height; + /* Take the DAR into account, never decreasing + * the dimensions */ + if (dar > ar) { + mExtVideoTextureWidth = + (frameInfo->info.resolution.height * + mParams.video_texture_dar_width + + mParams.video_texture_dar_height / 2) / + mParams.video_texture_dar_height; + mExtVideoTextureHeight = + frameInfo->info.resolution.height; + } else { + mExtVideoTextureWidth = + frameInfo->info.resolution.width; + mExtVideoTextureHeight = + (frameInfo->info.resolution.width * + mParams.video_texture_dar_height + + mParams.video_texture_dar_width / 2) / + mParams.video_texture_dar_width; + } + } + } else if (mParams.video_texture_width > 0) { + /* Custom texture width without custom DAR */ + mExtVideoTextureWidth = mParams.video_texture_width; + mExtVideoTextureHeight = + (mParams.video_texture_width * + frameInfo->info.resolution.height + + frameInfo->info.resolution.width / 2) / + frameInfo->info.resolution.width; + + /* Take the SAR into account */ + mExtVideoTextureHeight = + (mExtVideoTextureHeight * frameInfo->info.sar.height + + frameInfo->info.sar.width / 2) / + frameInfo->info.sar.width; + } else { + /* No custom texture width and no custom DAR */ + float sar = (float)frameInfo->info.sar.width / + (float)frameInfo->info.sar.height; + /* Take the SAR into account, never decreasing the dimensions */ + if (sar < 1.f) { + mExtVideoTextureWidth = + frameInfo->info.resolution.width; + mExtVideoTextureHeight = + (frameInfo->info.resolution.height * + frameInfo->info.sar.height + + frameInfo->info.sar.width / 2) / + frameInfo->info.sar.width; + } else { + mExtVideoTextureWidth = + (frameInfo->info.resolution.width * + frameInfo->info.sar.width + + frameInfo->info.sar.height / 2) / + frameInfo->info.sar.height; + mExtVideoTextureHeight = + frameInfo->info.resolution.height; + } + } + + /* Round up to nearest multiple of 2 */ + mExtVideoTextureWidth = (mExtVideoTextureWidth + 1) & ~1; + mExtVideoTextureHeight = (mExtVideoTextureHeight + 1) & ~1; + + /* start external Texture */ + if (mExtVideoTextureWidth != 0 && mExtVideoTextureHeight != 0) { + ret = startExtLoad(); + if (ret < 0) { + PDRAW_LOG_ERRNO("startExtLoad", -ret); + mExtLoadVideoTexture = false; + mExtVideoTextureWidth = 0; + mExtVideoTextureHeight = 0; + mParams.video_texture_width = 0; + mParams.video_texture_dar_width = 0; + mParams.video_texture_dar_height = 0; + } + } else { + mExtLoadVideoTexture = false; + mExtVideoTextureWidth = 0; + mExtVideoTextureHeight = 0; + mParams.video_texture_width = 0; + mParams.video_texture_dar_width = 0; + mParams.video_texture_dar_height = 0; + ret = stopExtLoad(); + if (ret < 0) + PDRAW_LOG_ERRNO("stopExtLoad", -ret); + } + + PDRAW_LOGI("external video texture: size=%ux%u", + mExtVideoTextureWidth, + mExtVideoTextureHeight); + + return 0; +} + + +void Gles2Renderer::createProjMatrix(Eigen::Matrix4f &projMat, + float aspectRatio, + float near, + float far) +{ + float w = 1.f; + float h = aspectRatio; + float a = (far + near) / (far - near); + float b = -((2 * far * near) / (far - near)); + + projMat << w, 0, 0, 0, 0, h, 0, 0, 0, 0, a, b, 0, 0, 1, 0; +} + + +int Gles2Renderer::getNextFrameDelay(mbuf_raw_video_frame_queue *queue, + uint64_t curTime, + bool allowDrop, + bool *shouldBreak, + uint64_t *delayUs, + int64_t *compensationUs, + int64_t *timingErrorUs, + uint64_t *frameTsUs, + int logLevel) +{ + int ret; + uint32_t delay; + struct mbuf_raw_video_frame *frame = nullptr; + struct vdef_raw_frame frameInfo = {}; + uint64_t frameTs = 0; + + int count = mbuf_raw_video_frame_queue_get_count(queue); + if ((mParams.scheduling_mode == + PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ADAPTIVE) && + allowDrop && (count >= GLES2_RENDERER_QUEUE_MAX_FRAMES)) { + /* The queue is full, discard the next frame to catch up */ + ret = mbuf_raw_video_frame_queue_pop(queue, &frame); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_pop", -ret); + } else { + if (logLevel) { + (void)mbuf_raw_video_frame_get_frame_info( + frame, &frameInfo); + _PDRAW_LOG_INT(logLevel, + "QUEUE FULL (queue_count=%u) " + "frame #%u is discarded", + count, + frameInfo.info.index); + } + (void)mbuf_raw_video_frame_unref(frame); + frame = nullptr; + count--; + } + } + + ret = mbuf_raw_video_frame_queue_peek(queue, &frame); + if (ret < 0) { + if (ret != -EAGAIN) { + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_peek", + -ret); + } + return ret; + } + + ret = mbuf_raw_video_frame_get_frame_info(frame, &frameInfo); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_get_frame_info", -ret); + mbuf_raw_video_frame_unref(frame); + return ret; + } + frameTs = frameInfo.info.timescale + ? (frameInfo.info.timestamp * 1000000 + + frameInfo.info.timescale / 2) / + frameInfo.info.timescale + : 0; + + uint64_t inputTime = UINT64_MAX; + inputTime = getFrameU64(frame, + GLES2_RENDERER_ANCILLARY_DATA_KEY_INPUT_TIME); + + mbuf_raw_video_frame_unref(frame); + frame = nullptr; + + uint32_t outputDelay = + (inputTime != UINT64_MAX) ? curTime - inputTime : 0; + int64_t timingError = 0, compensation, epsilon = 0; + if (mSchedLastInputTimestamp != UINT64_MAX) { + epsilon = (GLES2_RENDERER_SCHED_ADAPTIVE_EPSILON_PERCENT * + (frameTs - mSchedLastInputTimestamp) + + 50) / + 100; + } + if (mSchedLastInputTimestamp != UINT64_MAX && + mSchedLastOutputTimestamp != UINT64_MAX) { + timingError = + (int64_t)frameTs - (int64_t)mSchedLastInputTimestamp - + (int64_t)curTime + (int64_t)mSchedLastOutputTimestamp; + } + + bool _shouldBreak = false; + switch (mParams.scheduling_mode) { + default: + case PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ASAP: + /* Render the frame ASAP */ + delay = 0; + compensation = 0; + break; + case PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ADAPTIVE: + if ((mSchedLastInputTimestamp == UINT64_MAX) && + (count <= GLES2_RENDERER_QUEUE_MAX_FRAMES / 2)) { + /* Initial buffering: half the queue must be filled + * (rounded down); break */ + if (logLevel) { + _PDRAW_LOG_INT( + logLevel, + "INITIAL BUFFERING (queue_count=%u)", + count); + } + /* TODO: reset the mSchedLast*putTimestamp variables + * somewhere to reset the initial buffering when + * needed (i.e. when we will have that information + * in downstream pipeline events) */ + delay = 0; + compensation = 0; + _shouldBreak = true; + } else if (timingError > epsilon) { + /* The frame is early */ + if (count < GLES2_RENDERER_QUEUE_MAX_FRAMES - 1) { + /* Process the frame later; break */ + delay = timingError; + compensation = 0; + _shouldBreak = true; + if (logLevel) { + _PDRAW_LOG_INT( + logLevel, + "[EARLY] frame #%u is %.2fms " + "early (delay=%.2fms, " + "queue_count=%u)", + frameInfo.info.index, + (float)timingError / 1000., + (float)outputDelay / 1000., + count); + } + } else { + /* Process the frame now anyway; do not take the + * timing error into account as compensation in + * mSchedLastOutputTimestamp */ + if (logLevel) { + _PDRAW_LOG_INT( + logLevel, + "[EARLY] frame #%u is %.2fms " + "early (delay=%.2fms, " + "queue_count=%u) " + "=> PROCESS ANYWAY", + frameInfo.info.index, + (float)timingError / 1000., + (float)outputDelay / 1000., + count); + } + compensation = 0; + delay = 0; + } + } else if (timingError < -epsilon) { + /* The frame is late */ + if (logLevel) { + _PDRAW_LOG_INT(logLevel, + "[LATE] frame #%u is %.2fms " + "late (delay=%.2fms, " + "queue_count=%u)", + frameInfo.info.index, + (float)timingError / -1000., + (float)outputDelay / 1000., + count); + } + delay = 0; + compensation = timingError; + } else { + /* The frame is on time */ + if (logLevel) { + _PDRAW_LOG_INT(logLevel, + "[ONTIME] frame #%u is on time " + "(delay=%.2fms, queue_count=%u)", + frameInfo.info.index, + (float)outputDelay / 1000., + count); + } + delay = 0; + compensation = timingError; + } + break; + } + + if (shouldBreak != nullptr) + *shouldBreak = _shouldBreak; + if (delayUs != nullptr) + *delayUs = delay; + if (compensationUs != nullptr) + *compensationUs = compensation; + if (timingErrorUs != nullptr) + *timingErrorUs = timingError; + if (frameTsUs != nullptr) + *frameTsUs = frameTs; + + return 0; +} + + +int Gles2Renderer::scheduleFrame(uint64_t curTime, + bool *load, + int64_t *compensationUs) +{ + int ret = 0, err; + bool _load = false; + struct mbuf_raw_video_frame *frame = nullptr; + int count; + bool shouldBreak; + uint64_t frameTs; + int64_t compensation; + + struct mbuf_raw_video_frame_queue *queue = getLastAddedMediaQueue(); + if (queue == nullptr) { + ret = -EPROTO; + goto out; + } + + count = mbuf_raw_video_frame_queue_get_count(queue); + if (count == 0) { + /* Empty queue, reset the scheduling (the shortage of frames + * can be because of a seek or pause in the playback) */ + mSchedLastInputTimestamp = UINT64_MAX; + mSchedLastOutputTimestamp = UINT64_MAX; + PDRAW_LOGD("RESET SCHEDULING"); + goto out; + } + + /* Get a new frame to render */ + do { + shouldBreak = false; + frameTs = 0; + compensation = 0; + + err = getNextFrameDelay(queue, + curTime, + true, + &shouldBreak, + nullptr, + &compensation, + nullptr, + &frameTs, + 0); + if (err < 0) { + if (err != -EAGAIN) + PDRAW_LOG_ERRNO("getNextFrameDelay", -err); + break; + } + + if (shouldBreak) + break; + + err = mbuf_raw_video_frame_queue_pop(queue, &frame); + if (err < 0) { + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_queue_pop", -err); + break; + } + + if (mCurrentFrame != nullptr) + (void)mbuf_raw_video_frame_unref(mCurrentFrame); + mCurrentFrame = frame; + _load = true; + frame = nullptr; + mSchedLastOutputTimestamp = curTime + compensation; + mSchedLastInputTimestamp = frameTs; + if (compensationUs != nullptr) + *compensationUs = compensation; + } while (true); + +out: + if ((ret == 0) && (load != nullptr)) + *load = _load; + return ret; +} + + +/* Called on the rendering thread */ +int Gles2Renderer::render(struct pdraw_rect *contentPos, + const float *viewMat, + const float *projMat) +{ + int err; + struct pdraw_rect renderPos; + bool load = false, render = false; + uint64_t curTime = 0, inputTime, delay = 0; + int64_t compensation = 0; + struct timespec ts = {0, 0}; + struct pdraw_media_info *mediaInfoPtr = nullptr; + Eigen::Matrix4f vpMat, vMat, pMat; + struct pdraw_rect content; + struct mbuf_ancillary_data *ancillaryData = nullptr; + RawVideoMedia::Frame *data; + + memset(&content, 0, sizeof(content)); + + if (contentPos != nullptr) + *contentPos = content; + + if ((!mRunning) || (mState != STARTED)) + return 0; + + if ((mWidth == 0) || (mHeight == 0)) + return 0; + + err = time_get_monotonic(&ts); + if (err < 0) + PDRAW_LOG_ERRNO("time_get_monotonic", -err); + err = time_timespec_to_us(&ts, &curTime); + if (err < 0) + PDRAW_LOG_ERRNO("time_timespec_to_us", -err); + + GLCHK(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mDefaultFbo)); + + if (mParams.enable_hmd_distortion_correction) { + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mHmdFbo)); + GLCHK(glViewport(0, 0, mHmdFboSize, mHmdFboSize)); + GLCHK(glDisable(GL_DITHER)); + renderPos.x = 0; + renderPos.y = 0; + renderPos.width = mHmdFboSize; + renderPos.height = mHmdFboSize; + } else { + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); + GLCHK(glViewport(mX, mY, mWidth, mHeight)); + GLCHK(glDisable(GL_DITHER)); + renderPos.x = mX; + renderPos.y = mY; + renderPos.width = mWidth; + renderPos.height = mHeight; + } + + if (viewMat != nullptr) { + unsigned int i, j; + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) + vMat(i, j) = viewMat[j * 4 + i]; + } + } else { + vMat = Eigen::Matrix4f::Identity(); + } + if (projMat != nullptr) { + unsigned int i, j; + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) + pMat(i, j) = projMat[j * 4 + i]; + } + } else { + createProjMatrix(pMat, + (float)renderPos.width / + (float)renderPos.height, + 0.1f, + 100.f); + } + vpMat = pMat * vMat; + + RawSink::lock(); + if (mLastAddedMedia == nullptr) { + /* No current media */ + goto skip_dequeue; + } + + Media::cleanupMediaInfo(&mMediaInfo); + mLastAddedMedia->fillMediaInfo(&mMediaInfo); + /* Deep copy: copy the session metadata */ + mMediaInfoSessionMeta = *mMediaInfo.session_meta; + mMediaInfo.session_meta = &mMediaInfoSessionMeta; + mediaInfoPtr = &mMediaInfo; + + err = scheduleFrame(curTime, &load, &compensation); + if (err < 0) { + RawSink::unlock(); + goto skip_dequeue; + } + + if (load) { + /* We have a new frame */ + bool expected = true; + if (mWatchdogTriggered.compare_exchange_strong(expected, + false)) { + PDRAW_LOGI("new frame to render"); + } + int err = pomp_timer_set(mWatchdogTimer, + 1000 * GLES2_RENDERER_WATCHDOG_TIME_S); + if (err != 0) { + PDRAW_LOG_ERRNO("pomp_timer_set", -err); + } + } + + if (mCurrentFrame == nullptr) { + /* No new frame to load */ + goto skip_dequeue; + } + + if (mCurrentFrameMetadata) { + vmeta_frame_unref(mCurrentFrameMetadata); + mCurrentFrameMetadata = nullptr; + } + + err = mbuf_raw_video_frame_get_ancillary_data( + mCurrentFrame, + PDRAW_ANCILLARY_DATA_KEY_RAWVIDEOFRAME, + &ancillaryData); + if (err < 0) { + RawSink::unlock(); + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_get_ancillary_data", + -err); + goto skip_render; + } + data = (RawVideoMedia::Frame *)mbuf_ancillary_data_get_buffer( + ancillaryData, NULL); + if (data == nullptr) { + RawSink::unlock(); + PDRAW_LOGE("invalid ancillary data pointer"); + goto skip_render; + } + mCurrentFrameData = *data; + mbuf_ancillary_data_unref(ancillaryData); + + err = mbuf_raw_video_frame_get_frame_info(mCurrentFrame, + &mCurrentFrameInfo); + if (err < 0) { + RawSink::unlock(); + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_get_frame_info", -err); + goto skip_render; + } + + err = mbuf_raw_video_frame_get_metadata(mCurrentFrame, + &mCurrentFrameMetadata); + if (err < 0 && err != -ENOENT) { + RawSink::unlock(); + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_get_metadata", -err); + goto skip_render; + } + + inputTime = getFrameU64(mCurrentFrame, + GLES2_RENDERER_ANCILLARY_DATA_KEY_INPUT_TIME); + delay = (inputTime != 0) ? curTime - inputTime : 0; + + if (mFirstFrame) { + mFirstFrame = false; + int err = pomp_timer_set_periodic( + mVideoPresStatsTimer, + GLES2_RENDERER_VIDEO_PRES_STATS_TIME_MS, + GLES2_RENDERER_VIDEO_PRES_STATS_TIME_MS); + if (err != 0) + PDRAW_LOG_ERRNO("pomp_timer_set", -err); + if (mExtLoadVideoTexture) { + err = setupExtTexture(&mCurrentFrameInfo, + &mCurrentFrameData); + if (err < 0) + PDRAW_LOG_ERRNO("setupExtTexture", -err); + } + } + +skip_dequeue: + if (mGles2Video == nullptr) { + RawSink::unlock(); + goto skip_render; + } + + err = doTransition(curTime, load, &load); + if (err < 0) { + PDRAW_LOG_ERRNO("doTransition", -err); + goto skip_render; + } + + if (mCurrentFrame != nullptr) { + if (mExtLoadVideoTexture) { + err = loadExternalVideoFrame(mCurrentFrame, + &mCurrentFrameData, + mediaInfoPtr); + if (err < 0) + goto skip_render; + } else if (load) { + err = loadVideoFrame(mCurrentFrame, &mCurrentFrameData); + if (err < 0) + goto skip_render; + } + } + + if (mCurrentFrame != nullptr && load) { + /* First rendering of the current frame */ + + int queueCount = 0; + struct mbuf_raw_video_frame_queue *queue = + getLastAddedMediaQueue(); + if (queue != nullptr) { + queueCount = + mbuf_raw_video_frame_queue_get_count(queue); + } + + mCurrentFrameData.renderTimestamp = curTime; + uint64_t timestampDelta = 0; + if ((mCurrentFrameData.ntpRawTimestamp != 0) && + (mLastFrameTimestamp != UINT64_MAX)) { + timestampDelta = mCurrentFrameData.ntpRawTimestamp - + mLastFrameTimestamp; + } + uint64_t renderDelta = 0; + if ((mCurrentFrameData.renderTimestamp != 0) && + (mLastRenderTimestamp != UINT64_MAX)) { + renderDelta = mCurrentFrameData.renderTimestamp - + mLastRenderTimestamp; + } + int64_t timingError = 0; + if ((timestampDelta != 0) && (renderDelta != 0)) { + timingError = + (int64_t)timestampDelta - (int64_t)renderDelta; + } + uint64_t timingErrorAbs = + (timingError < 0) ? -timingError : timingError; + uint64_t estLatency = 0; + if ((mCurrentFrameData.renderTimestamp != 0) && + (mCurrentFrameData.localTimestamp != 0) && + (mCurrentFrameData.renderTimestamp > + mCurrentFrameData.localTimestamp)) { + estLatency = mCurrentFrameData.renderTimestamp - + mCurrentFrameData.localTimestamp; + } + uint64_t playerLatency = 0; + if ((mCurrentFrameData.renderTimestamp != 0) && + (mCurrentFrameData.recvStartTimestamp != 0) && + (mCurrentFrameData.renderTimestamp > + mCurrentFrameData.recvStartTimestamp)) { + playerLatency = mCurrentFrameData.renderTimestamp - + mCurrentFrameData.recvStartTimestamp; + } + PDRAW_LOGD( + "frame #%u est_total_latency=%.2fms " + "player_latency=%.2fms render_interval=%.2fms " + "render_delay=%.2fms timing_error=%.2fms " + "queue_count=%u", + mCurrentFrameInfo.info.index, + (float)estLatency / 1000., + (float)playerLatency / 1000., + (float)renderDelta / 1000., + (float)delay / 1000., + (float)timingError / 1000., + queueCount); + mVideoPresStats.timestamp = mCurrentFrameData.captureTimestamp; + mVideoPresStats.presentationFrameCount++; + mVideoPresStats.presentationTimestampDeltaIntegral += + timestampDelta; + mVideoPresStats.presentationTimestampDeltaIntegralSq += + timestampDelta * timestampDelta; + mVideoPresStats.presentationTimingErrorIntegral += + timingErrorAbs; + mVideoPresStats.presentationTimingErrorIntegralSq += + timingErrorAbs * timingErrorAbs; + mVideoPresStats.presentationEstimatedLatencyIntegral += + estLatency; + mVideoPresStats.presentationEstimatedLatencyIntegralSq += + estLatency * estLatency; + mVideoPresStats.playerLatencyIntegral += playerLatency; + mVideoPresStats.playerLatencyIntegralSq += + playerLatency * playerLatency; + mVideoPresStats.estimatedLatencyPrecisionIntegral += + mCurrentFrameData.localTimestampPrecision; + mLastRenderTimestamp = + mCurrentFrameData.renderTimestamp + compensation; + mLastFrameTimestamp = mCurrentFrameData.ntpRawTimestamp; + } + + RawSink::unlock(); + + /* Actual rendering */ + if (mFrameLoaded) { + if (mExtLoadVideoTexture) { + err = renderExternalVideoFrame( + &renderPos, &content, vpMat); + if (err < 0) { + PDRAW_LOG_ERRNO( + "gles2Video->" + "renderExternalVideoFrame", + -err); + } else { + render = true; + } + } else { + err = renderVideoFrame(&renderPos, &content, vpMat); + if (err < 0) { + PDRAW_LOG_ERRNO("gles2Video->renderVideoFrame", + -err); + } else { + render = true; + } + } + } else { + err = mGles2Video->clear(vpMat); + if (err < 0) + PDRAW_LOG_ERRNO("gles2Video->clear", -err); + } + if (mFrameLoaded != mFrameLoadedLogged) { + mFrameLoadedLogged = mFrameLoaded; + PDRAW_LOGI("render state: %s", + mFrameLoaded ? "video" : "black"); + } + +skip_render: + /* Overlay rendering callback function */ + if (mRenderVideoOverlay) { + struct pdraw_video_frame_extra frameExtra = {}; + struct vmeta_frame *frameMetaPtr = nullptr; + const struct pdraw_video_frame_extra *frameExtraPtr = nullptr; + if (render) { + frameExtra.play_timestamp = + mCurrentFrameData.playTimestamp; + if (mGles2Video) { + mGles2Video->getHistograms( + frameExtra.histogram, + frameExtra.histogram_len); + } + frameMetaPtr = mCurrentFrameMetadata; + frameExtraPtr = &frameExtra; + } + pthread_mutex_lock(&mListenerMutex); + if (mRendererListener != nullptr) { + mRendererListener->renderVideoOverlay(mSession, + mRenderer, + &renderPos, + &content, + vMat.data(), + pMat.data(), + mediaInfoPtr, + frameMetaPtr, + frameExtraPtr); + } + pthread_mutex_unlock(&mListenerMutex); + } + + /* HMD rendering */ + if (mParams.enable_hmd_distortion_correction) { + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); + GLCHK(glViewport(mX, mY, mWidth, mHeight)); + + if (mGles2Hmd) { + err = mGles2Hmd->renderHmd( + mHmdFboTexture, mHmdFboSize, mHmdFboSize); + if (err < 0) + PDRAW_LOG_ERRNO("gles2Hmd->renderHmd", -err); + } + } + + if (contentPos != nullptr) + *contentPos = content; + + return 0; +} + + +/* Called on the rendering thread */ +int Gles2Renderer::startHmd(void) +{ + int ret = 0; + GLenum gle; + + int err = stopHmd(); + if (err < 0) + PDRAW_LOG_ERRNO("stopHmd", -err); + + mHmdFboSize = (mWidth / 2 > mHeight) ? mWidth / 2 : mHeight; + GLCHK(); + + GLCHK(glGenFramebuffers(1, &mHmdFbo)); + if (mHmdFbo <= 0) { + PDRAW_LOGE("failed to create framebuffer"); + ret = -EPROTO; + goto out; + } + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mHmdFbo)); + + GLCHK(glGenTextures(1, &mHmdFboTexture)); + if (mHmdFboTexture <= 0) { + PDRAW_LOGE("failed to create texture"); + ret = -EPROTO; + goto out; + } + GLCHK(glActiveTexture(GL_TEXTURE0 + mGles2HmdFirstTexUnit)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mHmdFboTexture)); + GLCHK(glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA, + mHmdFboSize, + mHmdFboSize, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + nullptr)); + + GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GLCHK(glTexParameterf( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GLCHK(glTexParameterf( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + + GLCHK(glGenRenderbuffers(1, &mHmdFboRenderBuffer)); + if (mHmdFboRenderBuffer <= 0) { + PDRAW_LOGE("failed to create render buffer"); + ret = -EPROTO; + goto out; + } + GLCHK(glBindRenderbuffer(GL_RENDERBUFFER, mHmdFboRenderBuffer)); + GLCHK(glRenderbufferStorage(GL_RENDERBUFFER, + GL_DEPTH_COMPONENT16, + mHmdFboSize, + mHmdFboSize)); + + GLCHK(glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + mHmdFboTexture, + 0)); + GLCHK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, + mHmdFboRenderBuffer)); + + gle = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (gle != GL_FRAMEBUFFER_COMPLETE) { + PDRAW_LOGE("invalid framebuffer status"); + ret = -EPROTO; + goto out; + } + + GLCHK(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); + GLCHK(glClear(GL_COLOR_BUFFER_BIT)); + GLCHK(glBindTexture(GL_TEXTURE_2D, 0)); + GLCHK(glBindRenderbuffer(GL_RENDERBUFFER, 0)); + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); + + mGles2Hmd = new Gles2Hmd(mSession, + mGles2HmdFirstTexUnit, + mWidth, + mHeight, + mParams.hmd_ipd_offset, + mParams.hmd_x_offset, + mParams.hmd_y_offset); + if (mGles2Hmd == nullptr) { + PDRAW_LOGE("failed to create Gles2Hmd context"); + ret = -ENOMEM; + goto out; + } + + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); + if (mGles2Video) + mGles2Video->setDefaultFbo(mHmdFbo); + + return 0; + +out: + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); + if (mGles2Video) + mGles2Video->setDefaultFbo(mDefaultFbo); + + return ret; +} + + +/* Called on the rendering thread */ +int Gles2Renderer::stopHmd(void) +{ + GLCHK(); + + if (mGles2Hmd != nullptr) { + delete mGles2Hmd; + mGles2Hmd = nullptr; + } + if (mHmdFboRenderBuffer > 0) { + GLCHK(glDeleteRenderbuffers(1, &mHmdFboRenderBuffer)); + mHmdFboRenderBuffer = 0; + } + if (mHmdFboTexture > 0) { + GLCHK(glDeleteTextures(1, &mHmdFboTexture)); + mHmdFboTexture = 0; + } + if (mHmdFbo > 0) { + GLCHK(glDeleteFramebuffers(1, &mHmdFbo)); + mHmdFbo = 0; + } + if (mGles2Video) + mGles2Video->setDefaultFbo(mDefaultFbo); + mHmdFboSize = 0; + + return 0; +} + + +/* Called on the rendering thread */ +int Gles2Renderer::startExtLoad(void) +{ + int ret = 0, err; + GLenum gle; + + err = stopExtLoad(); + if (err < 0) + PDRAW_LOG_ERRNO("stopExtLoad", -err); + + GLCHK(); + + GLCHK(glGenFramebuffers(1, &mExtLoadFbo)); + if (mExtLoadFbo <= 0) { + PDRAW_LOGE("failed to create framebuffer"); + ret = -EPROTO; + goto out; + } + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mExtLoadFbo)); + + GLCHK(glGenTextures(1, &mExtLoadFboTexture)); + if (mExtLoadFboTexture <= 0) { + PDRAW_LOGE("failed to create texture"); + ret = -EPROTO; + goto out; + } + GLCHK(glActiveTexture(GL_TEXTURE0)); + GLCHK(glBindTexture(GL_TEXTURE_2D, mExtLoadFboTexture)); + GLCHK(glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA, + mExtVideoTextureWidth, + mExtVideoTextureHeight, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + nullptr)); + + GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GLCHK(glTexParameterf( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GLCHK(glTexParameterf( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + + GLCHK(glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + mExtLoadFboTexture, + 0)); + + gle = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (gle != GL_FRAMEBUFFER_COMPLETE) { + PDRAW_LOGE("invalid framebuffer status"); + ret = -EPROTO; + goto out; + } + + GLCHK(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); + GLCHK(glClear(GL_COLOR_BUFFER_BIT)); + GLCHK(glBindTexture(GL_TEXTURE_2D, 0)); + + if (mGles2Video) + mGles2Video->setExtTexture(mExtLoadFboTexture); + +out: + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFbo)); + return ret; +} + + +/* Called on the rendering thread */ +int Gles2Renderer::stopExtLoad(void) +{ + GLCHK(); + + if (mExtLoadFboTexture > 0) { + GLCHK(glDeleteTextures(1, &mExtLoadFboTexture)); + mExtLoadFboTexture = 0; + } + if (mExtLoadFbo > 0) { + GLCHK(glDeleteFramebuffers(1, &mExtLoadFbo)); + mExtLoadFbo = 0; + } + + if (mGles2Video) + mGles2Video->setExtTexture(mExtLoadFboTexture); + + return 0; +} + + +/* Called on the rendering thread */ +int Gles2Renderer::resize(const struct pdraw_rect *renderPos) +{ + int ret; + + if (renderPos == nullptr) + return -EINVAL; + + mX = renderPos->x; + mY = renderPos->y; + mWidth = renderPos->width; + mHeight = renderPos->height; + + GLCHK(); + GLCHK(glViewport(mX, mY, mWidth, mHeight)); + + if (mParams.enable_hmd_distortion_correction) { + ret = startHmd(); + if (ret < 0) + PDRAW_LOG_ERRNO("startHmd", -ret); + } else { + ret = stopHmd(); + if (ret < 0) + PDRAW_LOG_ERRNO("stopHmd", -ret); + } + + return ret; +} + + +/* Called on the rendering thread */ +int Gles2Renderer::setMediaId(unsigned int mediaId) +{ + if (mediaId == mMediaId) + return 0; + + mMediaId = mediaId; + int ret = pomp_loop_idle_add_with_cookie( + mSession->getLoop(), idleRenewMedia, this, this); + if (ret < 0) + PDRAW_LOG_ERRNO("pomp_loop_idle_add_with_cookie", -ret); + + return 0; +} + + +/* Called on the rendering thread */ +unsigned int Gles2Renderer::getMediaId(void) +{ + return mCurrentMediaId; +} + + +/* Called on the rendering thread */ +int Gles2Renderer::setParams(const struct pdraw_video_renderer_params *params) +{ + int ret; + + if (params == nullptr) + return -EINVAL; + + mParams = *params; + + if (mParams.video_scale_factor == 0.f) + mParams.video_scale_factor = 1.f; + + if (mParams.enable_hmd_distortion_correction) { + ret = startHmd(); + if (ret < 0) + PDRAW_LOG_ERRNO("startHmd", -ret); + } else { + ret = stopHmd(); + if (ret < 0) + PDRAW_LOG_ERRNO("stopHmd", -ret); + } + + if (!mExtLoadVideoTexture) { + mExtVideoTextureWidth = 0; + mExtVideoTextureHeight = 0; + mParams.video_texture_width = 0; + mParams.video_texture_dar_width = 0; + mParams.video_texture_dar_height = 0; + } + mFirstFrame = true; + + return ret; +} + + +/* Called on the rendering thread */ +int Gles2Renderer::getParams(struct pdraw_video_renderer_params *params) +{ + if (params) + *params = mParams; + return 0; +} + +} /* namespace Pdraw */ + +#endif /* USE_GLES2 */ diff --git a/libpdraw/src/pdraw_renderer_gles2.hpp b/libpdraw/src/pdraw_renderer_gles2.hpp index 86dee23..91bf294 100644 --- a/libpdraw/src/pdraw_renderer_gles2.hpp +++ b/libpdraw/src/pdraw_renderer_gles2.hpp @@ -1,243 +1,243 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * OpenGL ES 2.0 renderer - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_RENDERER_GLES2_HPP_ -#define _PDRAW_RENDERER_GLES2_HPP_ - -#ifdef USE_GLES2 - -# include "pdraw_gles2_hmd.hpp" -# include "pdraw_gles2_video.hpp" -# include "pdraw_renderer.hpp" -# include - -namespace Pdraw { - -class Gles2Renderer : public Renderer { -public: - Gles2Renderer(Session *session, - Element::Listener *listener, - IPdraw::IVideoRenderer *renderer, - IPdraw::IVideoRenderer::Listener *rndListener, - unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - struct egl_display *eglDisplay); - - ~Gles2Renderer(void); - - int start(void); - - int stop(void); - - int render(struct pdraw_rect *contentPos, - const float *viewMat = nullptr, - const float *projMat = nullptr); - - int resize(const struct pdraw_rect *renderPos); - - int setMediaId(unsigned int mediaId); - - unsigned int getMediaId(void); - - int setParams(const struct pdraw_video_renderer_params *params); - - - int getParams(struct pdraw_video_renderer_params *params); - - int addInputMedia(RawVideoMedia *media); - - int removeInputMedia(RawVideoMedia *media); - - int removeInputMedias(void); - - void completeStop(void); - -protected: - enum Transition { - NONE = 0, - FADE_FROM_BLACK, - FADE_TO_BLACK, - FADE_TO_BLACK_AND_WHITE, - FADE_TO_BLUR, - FLASH_THEN_BLACK_AND_WHITE, - }; - - virtual int setup(const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - struct egl_display *eglDisplay); - - int startHmd(void); - - int stopHmd(void); - - int startExtLoad(void); - - int stopExtLoad(void); - - void onChannelFlush(RawChannel *channel); - - void onChannelSos(RawChannel *channel); - - void onChannelEos(RawChannel *channel); - - void onChannelReconfigure(RawChannel *channel); - - void onChannelTimeout(RawChannel *channel); - - void onChannelPhotoTrigger(RawChannel *channel); - - int doTransition(uint64_t timestamp, bool frameReady, bool *loadFrame); - - void abortTransition(void); - - virtual int loadVideoFrame(struct mbuf_raw_video_frame *mbufFrame, - RawVideoMedia::Frame *frame); - - void createProjMatrix(Eigen::Matrix4f &projMat, - float aspectRatio, - float near, - float far); - - void updateViewProjMatrix(Eigen::Matrix4f &viewProjMat, - vmeta_frame *meta); - - virtual int - loadExternalVideoFrame(struct mbuf_raw_video_frame *mbufFrame, - RawVideoMedia::Frame *frame, - const struct pdraw_media_info *mediaInfo); - - virtual int renderVideoFrame(const struct pdraw_rect *renderPos, - struct pdraw_rect *contentPos, - Eigen::Matrix4f &viewProjMat); - - virtual int renderExternalVideoFrame(const struct pdraw_rect *renderPos, - struct pdraw_rect *contentPos, - Eigen::Matrix4f &viewProjMat); - - static void timerCb(struct pomp_timer *timer, void *userdata); - - static void watchdogTimerCb(struct pomp_timer *timer, void *userdata); - - static void videoPresStatsTimerCb(struct pomp_timer *timer, - void *userdata); - - static void queueEventCb(struct pomp_evt *evt, void *userdata); - - int removeQueueFdFromPomp(struct mbuf_raw_video_frame_queue *queue); - - unsigned int mMediaId; - unsigned int mCurrentMediaId; - bool mRunning; - struct mbuf_raw_video_frame *mCurrentFrame; - RawVideoMedia::Frame mCurrentFrameData; - struct vdef_raw_frame mCurrentFrameInfo; - struct vmeta_frame *mCurrentFrameMetadata; - RawVideoMedia *mLastAddedMedia; - struct pdraw_media_info mMediaInfo; - struct vmeta_session mMediaInfoSessionMeta; - struct pomp_timer *mTimer; - Gles2Hmd *mGles2Hmd; - unsigned int mGles2HmdFirstTexUnit; - Gles2Video *mGles2Video; - unsigned int mGles2VideoFirstTexUnit; - GLint mDefaultFbo; - unsigned int mHmdFboSize; - GLuint mHmdFbo; - GLuint mHmdFboTexture; - GLuint mHmdFboRenderBuffer; - GLuint mExtLoadFbo; - GLuint mExtLoadFboTexture; - int mX; - int mY; - unsigned int mWidth; - unsigned int mHeight; - enum Transition mPendingTransition; - enum Transition mCurrentTransition; - uint64_t mTransitionStartTime; - uint64_t mTransitionHoldTime; - struct pdraw_video_renderer_params mParams; - bool mExtLoadVideoTexture; - unsigned int mExtVideoTextureWidth; - unsigned int mExtVideoTextureHeight; - bool mRenderVideoOverlay; - bool mFirstFrame; - bool mFrameLoaded; - uint64_t mLastRenderTimestamp; - uint64_t mLastFrameTimestamp; - VideoPresStats mVideoPresStats; - struct pomp_timer *mVideoPresStatsTimer; - uint64_t mSchedLastInputTimestamp; - uint64_t mSchedLastOutputTimestamp; - bool mRenderReadyScheduled; - - /* Logging-related variables */ - /* previous mFrameLoaded value logged */ - bool mFrameLoadedLogged; - /* Watchdog timer: triggered if no new frame is received for a given - * amount of time */ - struct pomp_timer *mWatchdogTimer; - std::atomic_bool mWatchdogTriggered; - -private: - int setupExtTexture(const struct vdef_raw_frame *frameInfo, - const RawVideoMedia::Frame *frame); - - static bool queueFilter(struct mbuf_raw_video_frame *frame, - void *userdata); - - static uint64_t getFrameU64(struct mbuf_raw_video_frame *frame, - const char *key); - - struct mbuf_raw_video_frame_queue *getLastAddedMediaQueue(void); - - int getNextFrameDelay(mbuf_raw_video_frame_queue *queue, - uint64_t curTime, - bool allowDrop, - bool *shouldBreak, - uint64_t *delayUs, - int64_t *compensationUs, - int64_t *timingErrorUs, - uint64_t *frameTsUs, - int logLevel); - - int - scheduleFrame(uint64_t curTime, bool *load, int64_t *compensationUs); - - static void idleRenewMedia(void *userdata); - - static void idleStart(void *renderer); -}; - -} /* namespace Pdraw */ - -#endif /* USE_GLES2 */ - -#endif /* !_PDRAW_RENDERER_GLES2_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * OpenGL ES 2.0 renderer + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_RENDERER_GLES2_HPP_ +#define _PDRAW_RENDERER_GLES2_HPP_ + +#ifdef USE_GLES2 + +# include "pdraw_gles2_hmd.hpp" +# include "pdraw_gles2_video.hpp" +# include "pdraw_renderer.hpp" +# include + +namespace Pdraw { + +class Gles2Renderer : public Renderer { +public: + Gles2Renderer(Session *session, + Element::Listener *listener, + IPdraw::IVideoRenderer *renderer, + IPdraw::IVideoRenderer::Listener *rndListener, + unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + struct egl_display *eglDisplay); + + ~Gles2Renderer(void); + + int start(void); + + int stop(void); + + int render(struct pdraw_rect *contentPos, + const float *viewMat = nullptr, + const float *projMat = nullptr); + + int resize(const struct pdraw_rect *renderPos); + + int setMediaId(unsigned int mediaId); + + unsigned int getMediaId(void); + + int setParams(const struct pdraw_video_renderer_params *params); + + + int getParams(struct pdraw_video_renderer_params *params); + + int addInputMedia(RawVideoMedia *media); + + int removeInputMedia(RawVideoMedia *media); + + int removeInputMedias(void); + + void completeStop(void); + +protected: + enum Transition { + NONE = 0, + FADE_FROM_BLACK, + FADE_TO_BLACK, + FADE_TO_BLACK_AND_WHITE, + FADE_TO_BLUR, + FLASH_THEN_BLACK_AND_WHITE, + }; + + virtual int setup(const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + struct egl_display *eglDisplay); + + int startHmd(void); + + int stopHmd(void); + + int startExtLoad(void); + + int stopExtLoad(void); + + void onChannelFlush(RawChannel *channel); + + void onChannelSos(RawChannel *channel); + + void onChannelEos(RawChannel *channel); + + void onChannelReconfigure(RawChannel *channel); + + void onChannelTimeout(RawChannel *channel); + + void onChannelPhotoTrigger(RawChannel *channel); + + int doTransition(uint64_t timestamp, bool frameReady, bool *loadFrame); + + void abortTransition(void); + + virtual int loadVideoFrame(struct mbuf_raw_video_frame *mbufFrame, + RawVideoMedia::Frame *frame); + + void createProjMatrix(Eigen::Matrix4f &projMat, + float aspectRatio, + float near, + float far); + + void updateViewProjMatrix(Eigen::Matrix4f &viewProjMat, + vmeta_frame *meta); + + virtual int + loadExternalVideoFrame(struct mbuf_raw_video_frame *mbufFrame, + RawVideoMedia::Frame *frame, + const struct pdraw_media_info *mediaInfo); + + virtual int renderVideoFrame(const struct pdraw_rect *renderPos, + struct pdraw_rect *contentPos, + Eigen::Matrix4f &viewProjMat); + + virtual int renderExternalVideoFrame(const struct pdraw_rect *renderPos, + struct pdraw_rect *contentPos, + Eigen::Matrix4f &viewProjMat); + + static void timerCb(struct pomp_timer *timer, void *userdata); + + static void watchdogTimerCb(struct pomp_timer *timer, void *userdata); + + static void videoPresStatsTimerCb(struct pomp_timer *timer, + void *userdata); + + static void queueEventCb(struct pomp_evt *evt, void *userdata); + + int removeQueueFdFromPomp(struct mbuf_raw_video_frame_queue *queue); + + unsigned int mMediaId; + unsigned int mCurrentMediaId; + bool mRunning; + struct mbuf_raw_video_frame *mCurrentFrame; + RawVideoMedia::Frame mCurrentFrameData; + struct vdef_raw_frame mCurrentFrameInfo; + struct vmeta_frame *mCurrentFrameMetadata; + RawVideoMedia *mLastAddedMedia; + struct pdraw_media_info mMediaInfo; + struct vmeta_session mMediaInfoSessionMeta; + struct pomp_timer *mTimer; + Gles2Hmd *mGles2Hmd; + unsigned int mGles2HmdFirstTexUnit; + Gles2Video *mGles2Video; + unsigned int mGles2VideoFirstTexUnit; + GLint mDefaultFbo; + unsigned int mHmdFboSize; + GLuint mHmdFbo; + GLuint mHmdFboTexture; + GLuint mHmdFboRenderBuffer; + GLuint mExtLoadFbo; + GLuint mExtLoadFboTexture; + int mX; + int mY; + unsigned int mWidth; + unsigned int mHeight; + enum Transition mPendingTransition; + enum Transition mCurrentTransition; + uint64_t mTransitionStartTime; + uint64_t mTransitionHoldTime; + struct pdraw_video_renderer_params mParams; + bool mExtLoadVideoTexture; + unsigned int mExtVideoTextureWidth; + unsigned int mExtVideoTextureHeight; + bool mRenderVideoOverlay; + bool mFirstFrame; + bool mFrameLoaded; + uint64_t mLastRenderTimestamp; + uint64_t mLastFrameTimestamp; + VideoPresStats mVideoPresStats; + struct pomp_timer *mVideoPresStatsTimer; + uint64_t mSchedLastInputTimestamp; + uint64_t mSchedLastOutputTimestamp; + bool mRenderReadyScheduled; + + /* Logging-related variables */ + /* previous mFrameLoaded value logged */ + bool mFrameLoadedLogged; + /* Watchdog timer: triggered if no new frame is received for a given + * amount of time */ + struct pomp_timer *mWatchdogTimer; + std::atomic_bool mWatchdogTriggered; + +private: + int setupExtTexture(const struct vdef_raw_frame *frameInfo, + const RawVideoMedia::Frame *frame); + + static bool queueFilter(struct mbuf_raw_video_frame *frame, + void *userdata); + + static uint64_t getFrameU64(struct mbuf_raw_video_frame *frame, + const char *key); + + struct mbuf_raw_video_frame_queue *getLastAddedMediaQueue(void); + + int getNextFrameDelay(mbuf_raw_video_frame_queue *queue, + uint64_t curTime, + bool allowDrop, + bool *shouldBreak, + uint64_t *delayUs, + int64_t *compensationUs, + int64_t *timingErrorUs, + uint64_t *frameTsUs, + int logLevel); + + int + scheduleFrame(uint64_t curTime, bool *load, int64_t *compensationUs); + + static void idleRenewMedia(void *userdata); + + static void idleStart(void *renderer); +}; + +} /* namespace Pdraw */ + +#endif /* USE_GLES2 */ + +#endif /* !_PDRAW_RENDERER_GLES2_HPP_ */ diff --git a/libpdraw/src/pdraw_renderer_videocoreegl.cpp b/libpdraw/src/pdraw_renderer_videocoreegl.cpp index db48448..a864df8 100644 --- a/libpdraw/src/pdraw_renderer_videocoreegl.cpp +++ b/libpdraw/src/pdraw_renderer_videocoreegl.cpp @@ -1,168 +1,168 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Broadcom VideoCore 4 EGL renderer - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_rndvidvcgl -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_renderer_videocoreegl.hpp" -#include "pdraw_session.hpp" - -#ifdef USE_VIDEOCOREEGL - -# include -# include -# include - -namespace Pdraw { - - -VideoCoreEglRenderer::VideoCoreEglRenderer( - Session *session, - Element::Listener *listener, - IPdraw::IVideoRenderer *renderer, - IPdraw::IVideoRenderer::Listener *rndListener, - unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - struct egl_display *eglDisplay) : - Gles2Renderer(session, - listener, - renderer, - rndListener, - mediaId, - nullptr, - nullptr, - nullptr) -{ - Element::setClassName(__func__); - mDisplay = EGL_NO_DISPLAY; - setRawVideoMediaFormatCaps(VDEF_BCM_MMAL_OPAQUE); - setup(renderPos, params, eglDisplay); -} - - -VideoCoreEglRenderer::~VideoCoreEglRenderer(void) {} - - -int VideoCoreEglRenderer::setup( - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - struct egl_display *eglDisplay) -{ - if (eglDisplay == nullptr) { - PDRAW_LOGE("invalid EGL display"); - return -EINVAL; - } - mDisplay = (EGLDisplay)eglDisplay; - - struct pdraw_video_renderer_params _params = *params; - /* HMD distortion correction is not supported with VideoCore 4 */ - _params.enableHmdDistortionCorrection = 0; /* TODO */ - - return Gles2Renderer::setup(renderPos, &_params, eglDisplay); -} - - -int VideoCoreEglRenderer::loadVideoFrame(const uint8_t *data, - RawVideoMedia::Frame *frame) -{ - int ret; - - mColorConversion = GLES2_VIDEO_COLOR_CONVERSION_NONE; - - if (vdef_dim_is_null(&frame->frame.info.resolution) || - vdef_dim_is_null(&frame->frame.info.sar)) { - PDRAW_LOGE("invalid frame dimensions"); - return -EINVAL; - } - - ret = mGles2Video->loadFrame(data, - nullptr, - nullptr, - frame->frame.info.resolution.width, - frame->frame.info.resolution.height, - mColorConversion, - (struct egl_display *)mDisplay); - if (ret < 0) - PDRAW_LOG_ERRNO("gles2Video->loadFrame", -ret); - - return 0; -} - - -int VideoCoreEglRenderer::loadExternalVideoFrame( - const uint8_t *data, - RawVideoMedia::Frame *frame, - const struct pdraw_media_info *media_info) -{ - return -ENOSYS; -} - - -int VideoCoreEglRenderer::renderVideoFrame(RawVideoMedia::Frame *frame, - const struct pdraw_rect *renderPos, - struct pdraw_rect *contentPos, - Eigen::Matrix4f &viewProjMat) -{ - enum gles2_video_yuv_range yuvRange = - frame->opaqueFrame.fullRange ? GLES2_VIDEO_YUV_FULL_RANGE - : GLES2_VIDEO_YUV_LIMITED_RANGE; - - return mGles2Video->renderFrame(&frame->frame.plane_stride, - frame->frame.info.resolution.height, - 0, - 0, - frame->frame.info.resolution.width, - frame->frame.info.resolution.height, - frame->frame.info.sar.width, - frame->frame.info.sar.height, - renderPos, - contentPos, - viewProjMat, - mColorConversion, - yuvRange, - &frame->metadata, - &mParams); -} - - -int VideoCoreEglRenderer::renderExternalVideoFrame( - RawVideoMedia::Frame *frame, - const struct pdraw_rect *renderPos, - struct pdraw_rect *contentPos, - Eigen::Matrix4f &viewProjMat) -{ - return -ENOSYS; -} - -} /* namespace Pdraw */ - -#endif /* USE_VIDEOCOREEGL */ +/** + * Parrot Drones Awesome Video Viewer Library + * Broadcom VideoCore 4 EGL renderer + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_rndvidvcgl +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_renderer_videocoreegl.hpp" +#include "pdraw_session.hpp" + +#ifdef USE_VIDEOCOREEGL + +# include +# include +# include + +namespace Pdraw { + + +VideoCoreEglRenderer::VideoCoreEglRenderer( + Session *session, + Element::Listener *listener, + IPdraw::IVideoRenderer *renderer, + IPdraw::IVideoRenderer::Listener *rndListener, + unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + struct egl_display *eglDisplay) : + Gles2Renderer(session, + listener, + renderer, + rndListener, + mediaId, + nullptr, + nullptr, + nullptr) +{ + Element::setClassName(__func__); + mDisplay = EGL_NO_DISPLAY; + setRawVideoMediaFormatCaps(VDEF_BCM_MMAL_OPAQUE); + setup(renderPos, params, eglDisplay); +} + + +VideoCoreEglRenderer::~VideoCoreEglRenderer(void) {} + + +int VideoCoreEglRenderer::setup( + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + struct egl_display *eglDisplay) +{ + if (eglDisplay == nullptr) { + PDRAW_LOGE("invalid EGL display"); + return -EINVAL; + } + mDisplay = (EGLDisplay)eglDisplay; + + struct pdraw_video_renderer_params _params = *params; + /* HMD distortion correction is not supported with VideoCore 4 */ + _params.enableHmdDistortionCorrection = 0; /* TODO */ + + return Gles2Renderer::setup(renderPos, &_params, eglDisplay); +} + + +int VideoCoreEglRenderer::loadVideoFrame(const uint8_t *data, + RawVideoMedia::Frame *frame) +{ + int ret; + + mColorConversion = GLES2_VIDEO_COLOR_CONVERSION_NONE; + + if (vdef_dim_is_null(&frame->frame.info.resolution) || + vdef_dim_is_null(&frame->frame.info.sar)) { + PDRAW_LOGE("invalid frame dimensions"); + return -EINVAL; + } + + ret = mGles2Video->loadFrame(data, + nullptr, + nullptr, + frame->frame.info.resolution.width, + frame->frame.info.resolution.height, + mColorConversion, + (struct egl_display *)mDisplay); + if (ret < 0) + PDRAW_LOG_ERRNO("gles2Video->loadFrame", -ret); + + return 0; +} + + +int VideoCoreEglRenderer::loadExternalVideoFrame( + const uint8_t *data, + RawVideoMedia::Frame *frame, + const struct pdraw_media_info *media_info) +{ + return -ENOSYS; +} + + +int VideoCoreEglRenderer::renderVideoFrame(RawVideoMedia::Frame *frame, + const struct pdraw_rect *renderPos, + struct pdraw_rect *contentPos, + Eigen::Matrix4f &viewProjMat) +{ + enum gles2_video_yuv_range yuvRange = + frame->opaqueFrame.fullRange ? GLES2_VIDEO_YUV_FULL_RANGE + : GLES2_VIDEO_YUV_LIMITED_RANGE; + + return mGles2Video->renderFrame(&frame->frame.plane_stride, + frame->frame.info.resolution.height, + 0, + 0, + frame->frame.info.resolution.width, + frame->frame.info.resolution.height, + frame->frame.info.sar.width, + frame->frame.info.sar.height, + renderPos, + contentPos, + viewProjMat, + mColorConversion, + yuvRange, + &frame->metadata, + &mParams); +} + + +int VideoCoreEglRenderer::renderExternalVideoFrame( + RawVideoMedia::Frame *frame, + const struct pdraw_rect *renderPos, + struct pdraw_rect *contentPos, + Eigen::Matrix4f &viewProjMat) +{ + return -ENOSYS; +} + +} /* namespace Pdraw */ + +#endif /* USE_VIDEOCOREEGL */ diff --git a/libpdraw/src/pdraw_renderer_videocoreegl.hpp b/libpdraw/src/pdraw_renderer_videocoreegl.hpp index 20761d1..4843cd9 100644 --- a/libpdraw/src/pdraw_renderer_videocoreegl.hpp +++ b/libpdraw/src/pdraw_renderer_videocoreegl.hpp @@ -1,86 +1,86 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Broadcom VideoCore 4 EGL renderer - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_RENDERER_VIDEOCOREEGL_HPP_ -#define _PDRAW_RENDERER_VIDEOCOREEGL_HPP_ - -#ifdef USE_VIDEOCOREEGL - -# include "pdraw_gles2_hmd.hpp" -# include "pdraw_gles2_video.hpp" -# include "pdraw_renderer_gles2.hpp" - -# include - -namespace Pdraw { - - -class VideoCoreEglRenderer : public Gles2Renderer { -public: - VideoCoreEglRenderer(Session *session, - Element::Listener *listener, - IPdraw::IVideoRenderer *renderer, - IPdraw::IVideoRenderer::Listener *rndListener, - unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - struct egl_display *eglDisplay); - - ~VideoCoreEglRenderer(void); - -private: - int setup(const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - struct egl_display *eglDisplay); - - int loadVideoFrame(const uint8_t *data, RawVideoMedia::Frame *frame); - - int loadExternalVideoFrame(const uint8_t *data, - RawVideoMedia::Frame *frame, - const struct pdraw_media_info *media_info); - - int renderVideoFrame(RawVideoMedia::Frame *frame, - const struct pdraw_rect *renderPos, - struct pdraw_rect *contentPos, - Eigen::Matrix4f &viewProjMat); - - int renderExternalVideoFrame(RawVideoMedia::Frame *frame, - const struct pdraw_rect *renderPos, - struct pdraw_rect *contentPos, - Eigen::Matrix4f &viewProjMat); - - EGLDisplay mDisplay; -}; - -} /* namespace Pdraw */ - -#endif /* USE_VIDEOCOREEGL */ - -#endif /* !_PDRAW_RENDERER_VIDEOCOREEGL_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Broadcom VideoCore 4 EGL renderer + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_RENDERER_VIDEOCOREEGL_HPP_ +#define _PDRAW_RENDERER_VIDEOCOREEGL_HPP_ + +#ifdef USE_VIDEOCOREEGL + +# include "pdraw_gles2_hmd.hpp" +# include "pdraw_gles2_video.hpp" +# include "pdraw_renderer_gles2.hpp" + +# include + +namespace Pdraw { + + +class VideoCoreEglRenderer : public Gles2Renderer { +public: + VideoCoreEglRenderer(Session *session, + Element::Listener *listener, + IPdraw::IVideoRenderer *renderer, + IPdraw::IVideoRenderer::Listener *rndListener, + unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + struct egl_display *eglDisplay); + + ~VideoCoreEglRenderer(void); + +private: + int setup(const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + struct egl_display *eglDisplay); + + int loadVideoFrame(const uint8_t *data, RawVideoMedia::Frame *frame); + + int loadExternalVideoFrame(const uint8_t *data, + RawVideoMedia::Frame *frame, + const struct pdraw_media_info *media_info); + + int renderVideoFrame(RawVideoMedia::Frame *frame, + const struct pdraw_rect *renderPos, + struct pdraw_rect *contentPos, + Eigen::Matrix4f &viewProjMat); + + int renderExternalVideoFrame(RawVideoMedia::Frame *frame, + const struct pdraw_rect *renderPos, + struct pdraw_rect *contentPos, + Eigen::Matrix4f &viewProjMat); + + EGLDisplay mDisplay; +}; + +} /* namespace Pdraw */ + +#endif /* USE_VIDEOCOREEGL */ + +#endif /* !_PDRAW_RENDERER_VIDEOCOREEGL_HPP_ */ diff --git a/libpdraw/src/pdraw_scaler_video.cpp b/libpdraw/src/pdraw_scaler_video.cpp index 1231f27..b981716 100644 --- a/libpdraw/src/pdraw_scaler_video.cpp +++ b/libpdraw/src/pdraw_scaler_video.cpp @@ -1,699 +1,699 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Video scaler element - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_vscale -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_scaler_video.hpp" -#include "pdraw_session.hpp" -#include "pdraw_utils.hpp" - -#include -#include - -#include - -namespace Pdraw { - - -const struct vscale_cbs VideoScaler::mScalerCbs = { - .frame_output = &VideoScaler::frameOutputCb, - .flush = &VideoScaler::flushCb, - .stop = &VideoScaler::stopCb, -}; - - -VideoScaler::VideoScaler(Session *session, - Element::Listener *elementListener, - RawSource::Listener *sourceListener) : - RawToRawFilterElement(session, - elementListener, - 1, - nullptr, - 0, - 1, - sourceListener), - mInputMedia(nullptr), mOutputMedia(nullptr), - mInputBufferPool(nullptr), mInputBufferQueue(nullptr), - mVscale(nullptr), mIsFlushed(true), - mInputChannelFlushPending(false), mVscaleFlushPending(false), - mVscaleStopPending(false), mCompleteStopPendingCount(0) -{ - const struct vdef_raw_format *supportedInputFormats; - int supportedInputFormatsCount; - - Element::setClassName(__func__); - - /* Supported input formats */ - supportedInputFormatsCount = vscale_get_supported_input_formats( - VSCALE_SCALER_IMPLEM_AUTO, &supportedInputFormats); - if (supportedInputFormatsCount < 0) - PDRAW_LOG_ERRNO("vscale_get_supported_input_formats", - -supportedInputFormatsCount); - else - setRawVideoMediaFormatCaps(supportedInputFormats, - supportedInputFormatsCount); - - setState(CREATED); -} - - -VideoScaler::~VideoScaler(void) -{ - int ret; - - if (mState != STOPPED) - PDRAW_LOGW("scaler is still running"); - - if (mVscale) { - ret = vscale_destroy(mVscale); - if (ret < 0) - PDRAW_LOG_ERRNO("vscale_destroy", -ret); - } - - if (mOutputMedia != nullptr) - PDRAW_LOGW("output media was not properly removed"); -} - - -int VideoScaler::start(void) -{ - int ret = 0; - - if ((mState == STARTED) || (mState == STARTING)) { - return 0; - } - if (mState != CREATED) { - PDRAW_LOGE("scaler is not created"); - return -EPROTO; - } - setState(STARTING); - - /* Get the input media and port */ - RawSink::lock(); - unsigned int inputMediaCount = getInputMediaCount(); - if (inputMediaCount != 1) { - RawSink::unlock(); - PDRAW_LOGE("invalid input media count"); - return -EPROTO; - } - Media *media = getInputMedia(0); - if (media == nullptr) { - RawSink::unlock(); - PDRAW_LOGE("invalid input media"); - return -EPROTO; - } - mInputMedia = dynamic_cast(media); - if (mInputMedia == nullptr) { - RawSink::unlock(); - PDRAW_LOGE("invalid input media"); - return -EPROTO; - } - InputPort *port = getInputPort(mInputMedia); - if (port == nullptr) { - RawSink::unlock(); - PDRAW_LOGE("invalid input port"); - return -EPROTO; - } - - /* Initialize the scaler */ - struct vscale_config cfg = {}; - cfg.implem = VSCALE_SCALER_IMPLEM_AUTO; - cfg.input.format = mInputMedia->format; - cfg.input.info = mInputMedia->info; - cfg.output.info = cfg.input.info; - cfg.output.info.resolution.width = 1280; /* TODO */ - cfg.output.info.resolution.height = 720; /* TODO */ - ret = vscale_new( - mSession->getLoop(), &cfg, &mScalerCbs, this, &mVscale); - if (ret < 0) { - RawSink::unlock(); - PDRAW_LOG_ERRNO("vscale_new", -ret); - return ret; - } - - /* Setup the input port */ - port->channel->setKey(this); - mInputBufferQueue = vscale_get_input_buffer_queue(mVscale); - port->channel->setQueue(mInputBufferQueue); - mInputBufferPool = vscale_get_input_buffer_pool(mVscale); - port->channel->setPool(mInputBufferPool); - - RawSink::unlock(); - - setState(STARTED); - - return 0; -} - - -int VideoScaler::stop(void) -{ - int ret; - - if ((mState == STOPPED) || (mState == STOPPING)) - return 0; - if (mState != STARTED) { - PDRAW_LOGE("scaler is not started"); - return -EPROTO; - } - setState(STOPPING); - mVscaleStopPending = true; - - /* Flush everything */ - ret = flush(); - if (ret < 0) - PDRAW_LOG_ERRNO("flush", -ret); - - /* When the flush is complete, stopping will be triggered */ - return ret; -} - - -int VideoScaler::flush(void) -{ - int ret; - unsigned int outputChannelCount, i; - RawChannel *outputChannel; - - if (mIsFlushed) { - PDRAW_LOGD("scaler is already flushed, nothing to do"); - completeFlush(); - return 0; - } - - mVscaleFlushPending = true; - - /* Flush the output channels (async) */ - RawSource::lock(); - if (mOutputMedia != nullptr) { - outputChannelCount = getOutputChannelCount(mOutputMedia); - for (i = 0; i < outputChannelCount; i++) { - outputChannel = getOutputChannel(mOutputMedia, i); - if (outputChannel == nullptr) { - PDRAW_LOGW( - "failed to get output channel " - "at index %d", - i); - continue; - } - ret = outputChannel->flush(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->flush", -ret); - } - } - RawSource::unlock(); - - /* Flush the scaler (async) - * (the input channel queue is flushed by vscale) */ - ret = vscale_flush(mVscale, 1); - if (ret < 0) - PDRAW_LOG_ERRNO("vscale_flush", -ret); - - return ret; -} - - -void VideoScaler::completeFlush(void) -{ - int ret; - unsigned int outputChannelCount, i; - RawChannel *outputChannel; - bool pending = false; - - if (mVscaleFlushPending) - return; - - RawSource::lock(); - if (mOutputMedia != nullptr) { - outputChannelCount = getOutputChannelCount(mOutputMedia); - for (i = 0; i < outputChannelCount; i++) { - outputChannel = getOutputChannel(mOutputMedia, i); - if (outputChannel == nullptr) { - PDRAW_LOGW( - "failed to get output channel " - "at index %d", - i); - continue; - } - if (outputChannel->isFlushPending()) { - pending = true; - break; - } - } - } - RawSource::unlock(); - - if (pending) - return; - - RawSink::lock(); - if (mInputMedia != nullptr) { - mIsFlushed = true; - if (mInputChannelFlushPending) { - mInputChannelFlushPending = false; - RawChannel *inputChannel = getInputChannel(mInputMedia); - if (inputChannel == nullptr) { - PDRAW_LOGE("failed to get input channel"); - } else { - ret = inputChannel->flushDone(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->flushDone", - -ret); - } - } - } - RawSink::unlock(); - - tryStop(); -} - - -int VideoScaler::tryStop(void) -{ - int ret; - int outputChannelCount = 0, i; - RawChannel *channel; - - if (mState != STOPPING) - return 0; - - /* Remove the input port */ - RawSink::lock(); - if (mInputMedia != nullptr) { - channel = getInputChannel(mInputMedia); - if (channel == nullptr) { - PDRAW_LOGE("failed to get channel"); - } else { - channel->setQueue(nullptr); - channel->setPool(nullptr); - } - - ret = removeInputMedia(mInputMedia); - if (ret < 0) - PDRAW_LOG_ERRNO("removeInputMedia", -ret); - else - mInputMedia = nullptr; - } - RawSink::unlock(); - - /* Teardown the output channels - * Note: loop downwards because calling teardown on a channel may or - * may not synchronously remove the channel from the output port */ - RawSource::lock(); - if (mOutputMedia != nullptr) { - outputChannelCount = getOutputChannelCount(mOutputMedia); - - for (i = outputChannelCount - 1; i >= 0; i--) { - channel = getOutputChannel(mOutputMedia, i); - if (channel == nullptr) { - PDRAW_LOGW("failed to get channel at index %d", - i); - continue; - } - ret = channel->teardown(); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->teardown", -ret); - } - } - RawSource::unlock(); - - /* Stop the scaler */ - ret = vscale_stop(mVscale); - if (ret < 0) { - PDRAW_LOG_ERRNO("vscale_stop", -ret); - return ret; - } - - return 0; -} - - -void VideoScaler::completeStop(void) -{ - int ret; - unsigned int outputChannelCount; - - mCompleteStopPendingCount--; - - RawSource::lock(); - if (mOutputMedia == nullptr) { - RawSource::unlock(); - goto exit; - } - outputChannelCount = getOutputChannelCount(mOutputMedia); - if (outputChannelCount > 0) { - RawSource::unlock(); - return; - } - - /* Remove the output port */ - if (RawSource::mListener) { - RawSource::mListener->onOutputMediaRemoved(this, mOutputMedia); - } - ret = removeOutputPort(mOutputMedia); - if (ret < 0) { - PDRAW_LOG_ERRNO("removeOutputPort", -ret); - } else { - delete mOutputMedia; - mOutputMedia = nullptr; - } - - RawSource::unlock(); - -exit: - if ((!mVscaleStopPending) && (mCompleteStopPendingCount == 0)) - setState(STOPPED); -} - - -int VideoScaler::createOutputMedia(struct vdef_raw_frame *frameInfo, - RawVideoMedia::Frame &frame) -{ - int ret; - - RawSource::lock(); - - mOutputMedia = new RawVideoMedia(mSession); - if (mOutputMedia == nullptr) { - RawSource::unlock(); - PDRAW_LOGE("output media allocation failed"); - return -ENOMEM; - } - std::string path = mInputMedia->getPath() + ">" + Element::getName() + - "$" + mOutputMedia->getName(); - mOutputMedia->setPath(path); - - ret = addOutputPort(mOutputMedia); - if (ret < 0) { - RawSource::unlock(); - PDRAW_LOG_ERRNO("addOutputPort", -ret); - return ret; - } - - mOutputMedia->format = frameInfo->format; - vdef_frame_to_format_info(&frameInfo->info, &mOutputMedia->info); - mOutputMedia->info.framerate = mInputMedia->info.framerate; - mOutputMedia->sessionMeta = mInputMedia->sessionMeta; - mOutputMedia->playbackType = mInputMedia->playbackType; - mOutputMedia->duration = mInputMedia->duration; - - RawSource::unlock(); - - if (RawSource::mListener) - RawSource::mListener->onOutputMediaAdded(this, mOutputMedia); - - return 0; -} - - -void VideoScaler::onChannelQueue(RawChannel *channel, - struct mbuf_raw_video_frame *frame) -{ - - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - if (frame == nullptr) { - PDRAW_LOG_ERRNO("frame", EINVAL); - return; - } - if (mState != STARTED) { - PDRAW_LOGE("scaler is not started"); - return; - } - if ((mVscaleFlushPending) || (mInputChannelFlushPending)) { - PDRAW_LOGI("frame input: flush pending, discard frame"); - return; - } - RawSink::lock(); - struct mbuf_raw_video_frame_queue *queue = channel->getQueue(); - if (queue == nullptr) { - RawSink::unlock(); - PDRAW_LOGE("invalid queue"); - return; - } - if (queue != mInputBufferQueue) { - RawSink::unlock(); - PDRAW_LOGE("invalid input buffer queue"); - return; - } - - RawSink::onChannelQueue(channel, frame); - mIsFlushed = false; - RawSink::unlock(); -} - - -void VideoScaler::onChannelFlush(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - PDRAW_LOGD("flushing input channel"); - mInputChannelFlushPending = true; - - int ret = flush(); - if (ret < 0) - PDRAW_LOG_ERRNO("flush", -ret); -} - - -void VideoScaler::onChannelFlushed(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - Media *media = getOutputMediaFromChannel(channel->getKey()); - if (media == nullptr) { - PDRAW_LOGE("media not found"); - return; - } - PDRAW_LOGD("'%s': channel flushed media name=%s (channel key=%p)", - Element::getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - completeFlush(); -} - - -void VideoScaler::onChannelTeardown(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - PDRAW_LOGD("tearing down input channel"); - - int ret = stop(); - if (ret < 0) - PDRAW_LOG_ERRNO("stop", -ret); -} - - -void VideoScaler::onChannelUnlink(RawChannel *channel) -{ - if (channel == nullptr) { - PDRAW_LOG_ERRNO("channel", EINVAL); - return; - } - - RawSource::onChannelUnlink(channel); - - if (mState == STOPPING) { - mCompleteStopPendingCount++; - completeStop(); - } -} - - -void VideoScaler::frameOutputCb(struct vscale_scaler *scaler, - int status, - struct mbuf_raw_video_frame *out_frame, - void *userdata) -{ - int ret; - VideoScaler *self = (VideoScaler *)userdata; - struct vdef_raw_frame info; - struct mbuf_ancillary_data *ancillaryData = nullptr; - RawVideoMedia::Frame *in_meta; - RawVideoMedia::Frame out_meta; - unsigned int outputChannelCount, i; - RawChannel *channel; - - if (status != 0) { - PDRAW_LOGE("scaler error: %d(%s)", -status, strerror(-status)); - return; - } - - if (userdata == nullptr) { - PDRAW_LOG_ERRNO("userdata", EINVAL); - return; - } - if (out_frame == nullptr) { - PDRAW_LOG_ERRNO("out_frame", EINVAL); - return; - } - if (self->mState != STARTED) { - PDRAW_LOGE("scaler is not started"); - return; - } - if ((self->mVscaleFlushPending) || (self->mInputChannelFlushPending)) { - PDRAW_LOGI("frame output: flush pending, discard frame"); - return; - } - ret = mbuf_raw_video_frame_get_frame_info(out_frame, &info); - if (ret < 0) { - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_get_frame_info", -ret); - return; - } - - self->RawSink::lock(); - if (self->mInputMedia == nullptr) { - self->RawSink::unlock(); - PDRAW_LOG_ERRNO("invalid input media", EPROTO); - return; - } - - ret = mbuf_raw_video_frame_get_ancillary_data( - out_frame, - PDRAW_ANCILLARY_DATA_KEY_RAWVIDEOFRAME, - &ancillaryData); - if (ret < 0) { - self->RawSink::unlock(); - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_get_ancillary_data", - -ret); - return; - } - in_meta = (RawVideoMedia::Frame *)mbuf_ancillary_data_get_buffer( - ancillaryData, NULL); - out_meta = *in_meta; - out_meta.scalerOutputTimestamp = pdraw_getTimestampFromMbufFrame( - out_frame, VSCALE_ANCILLARY_KEY_OUTPUT_TIME); - mbuf_ancillary_data_unref(ancillaryData); - ancillaryData = NULL; - - ret = mbuf_raw_video_frame_remove_ancillary_data( - out_frame, PDRAW_ANCILLARY_DATA_KEY_RAWVIDEOFRAME); - if (ret < 0) { - self->RawSink::unlock(); - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_remove_ancillary_data", - -ret); - return; - } - - ret = mbuf_raw_video_frame_add_ancillary_buffer( - out_frame, - PDRAW_ANCILLARY_DATA_KEY_RAWVIDEOFRAME, - &out_meta, - sizeof(out_meta)); - if (ret < 0) { - self->RawSink::unlock(); - PDRAW_LOG_ERRNO("mbuf_raw_video_frame_add_ancillary_buffer", - -ret); - return; - } - - self->RawSink::unlock(); - self->RawSource::lock(); - - if (self->mOutputMedia == nullptr) { - ret = self->createOutputMedia(&info, out_meta); - if (ret < 0) { - self->RawSource::unlock(); - PDRAW_LOG_ERRNO("createOutputMedia", -ret); - return; - } - } - - /* Push the frame (unless it is silent) */ - if (!(info.info.flags & VDEF_FRAME_FLAG_SILENT)) { - outputChannelCount = - self->getOutputChannelCount(self->mOutputMedia); - for (i = 0; i < outputChannelCount; i++) { - channel = self->getOutputChannel(self->mOutputMedia, i); - if (channel == nullptr) { - PDRAW_LOGE("failed to get channel at index %d", - i); - continue; - } - ret = channel->queue(out_frame); - if (ret < 0) - PDRAW_LOG_ERRNO("channel->queue", -ret); - } - } else { - PDRAW_LOGD("silent frame (ignored)"); - } - - self->RawSource::unlock(); -} - - -void VideoScaler::flushCb(struct vscale_scaler *scaler, void *userdata) -{ - VideoScaler *self = (VideoScaler *)userdata; - - if (userdata == nullptr) { - PDRAW_LOG_ERRNO("userdata", EINVAL); - return; - } - - PDRAW_LOGD("scaler is flushed"); - self->mVscaleFlushPending = false; - - self->completeFlush(); -} - - -void VideoScaler::stopCb(struct vscale_scaler *scaler, void *userdata) -{ - VideoScaler *self = (VideoScaler *)userdata; - - if (userdata == nullptr) { - PDRAW_LOG_ERRNO("userdata", EINVAL); - return; - } - - PDRAW_LOGD("scaler is stopped"); - self->mVscaleStopPending = false; - - self->mCompleteStopPendingCount++; - self->completeStop(); -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Video scaler element + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_vscale +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_scaler_video.hpp" +#include "pdraw_session.hpp" +#include "pdraw_utils.hpp" + +#include +#include + +#include + +namespace Pdraw { + + +const struct vscale_cbs VideoScaler::mScalerCbs = { + .frame_output = &VideoScaler::frameOutputCb, + .flush = &VideoScaler::flushCb, + .stop = &VideoScaler::stopCb, +}; + + +VideoScaler::VideoScaler(Session *session, + Element::Listener *elementListener, + RawSource::Listener *sourceListener) : + RawToRawFilterElement(session, + elementListener, + 1, + nullptr, + 0, + 1, + sourceListener), + mInputMedia(nullptr), mOutputMedia(nullptr), + mInputBufferPool(nullptr), mInputBufferQueue(nullptr), + mVscale(nullptr), mIsFlushed(true), + mInputChannelFlushPending(false), mVscaleFlushPending(false), + mVscaleStopPending(false), mCompleteStopPendingCount(0) +{ + const struct vdef_raw_format *supportedInputFormats; + int supportedInputFormatsCount; + + Element::setClassName(__func__); + + /* Supported input formats */ + supportedInputFormatsCount = vscale_get_supported_input_formats( + VSCALE_SCALER_IMPLEM_AUTO, &supportedInputFormats); + if (supportedInputFormatsCount < 0) + PDRAW_LOG_ERRNO("vscale_get_supported_input_formats", + -supportedInputFormatsCount); + else + setRawVideoMediaFormatCaps(supportedInputFormats, + supportedInputFormatsCount); + + setState(CREATED); +} + + +VideoScaler::~VideoScaler(void) +{ + int ret; + + if (mState != STOPPED) + PDRAW_LOGW("scaler is still running"); + + if (mVscale) { + ret = vscale_destroy(mVscale); + if (ret < 0) + PDRAW_LOG_ERRNO("vscale_destroy", -ret); + } + + if (mOutputMedia != nullptr) + PDRAW_LOGW("output media was not properly removed"); +} + + +int VideoScaler::start(void) +{ + int ret = 0; + + if ((mState == STARTED) || (mState == STARTING)) { + return 0; + } + if (mState != CREATED) { + PDRAW_LOGE("scaler is not created"); + return -EPROTO; + } + setState(STARTING); + + /* Get the input media and port */ + RawSink::lock(); + unsigned int inputMediaCount = getInputMediaCount(); + if (inputMediaCount != 1) { + RawSink::unlock(); + PDRAW_LOGE("invalid input media count"); + return -EPROTO; + } + Media *media = getInputMedia(0); + if (media == nullptr) { + RawSink::unlock(); + PDRAW_LOGE("invalid input media"); + return -EPROTO; + } + mInputMedia = dynamic_cast(media); + if (mInputMedia == nullptr) { + RawSink::unlock(); + PDRAW_LOGE("invalid input media"); + return -EPROTO; + } + InputPort *port = getInputPort(mInputMedia); + if (port == nullptr) { + RawSink::unlock(); + PDRAW_LOGE("invalid input port"); + return -EPROTO; + } + + /* Initialize the scaler */ + struct vscale_config cfg = {}; + cfg.implem = VSCALE_SCALER_IMPLEM_AUTO; + cfg.input.format = mInputMedia->format; + cfg.input.info = mInputMedia->info; + cfg.output.info = cfg.input.info; + cfg.output.info.resolution.width = 1280; /* TODO */ + cfg.output.info.resolution.height = 720; /* TODO */ + ret = vscale_new( + mSession->getLoop(), &cfg, &mScalerCbs, this, &mVscale); + if (ret < 0) { + RawSink::unlock(); + PDRAW_LOG_ERRNO("vscale_new", -ret); + return ret; + } + + /* Setup the input port */ + port->channel->setKey(this); + mInputBufferQueue = vscale_get_input_buffer_queue(mVscale); + port->channel->setQueue(mInputBufferQueue); + mInputBufferPool = vscale_get_input_buffer_pool(mVscale); + port->channel->setPool(mInputBufferPool); + + RawSink::unlock(); + + setState(STARTED); + + return 0; +} + + +int VideoScaler::stop(void) +{ + int ret; + + if ((mState == STOPPED) || (mState == STOPPING)) + return 0; + if (mState != STARTED) { + PDRAW_LOGE("scaler is not started"); + return -EPROTO; + } + setState(STOPPING); + mVscaleStopPending = true; + + /* Flush everything */ + ret = flush(); + if (ret < 0) + PDRAW_LOG_ERRNO("flush", -ret); + + /* When the flush is complete, stopping will be triggered */ + return ret; +} + + +int VideoScaler::flush(void) +{ + int ret; + unsigned int outputChannelCount, i; + RawChannel *outputChannel; + + if (mIsFlushed) { + PDRAW_LOGD("scaler is already flushed, nothing to do"); + completeFlush(); + return 0; + } + + mVscaleFlushPending = true; + + /* Flush the output channels (async) */ + RawSource::lock(); + if (mOutputMedia != nullptr) { + outputChannelCount = getOutputChannelCount(mOutputMedia); + for (i = 0; i < outputChannelCount; i++) { + outputChannel = getOutputChannel(mOutputMedia, i); + if (outputChannel == nullptr) { + PDRAW_LOGW( + "failed to get output channel " + "at index %d", + i); + continue; + } + ret = outputChannel->flush(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->flush", -ret); + } + } + RawSource::unlock(); + + /* Flush the scaler (async) + * (the input channel queue is flushed by vscale) */ + ret = vscale_flush(mVscale, 1); + if (ret < 0) + PDRAW_LOG_ERRNO("vscale_flush", -ret); + + return ret; +} + + +void VideoScaler::completeFlush(void) +{ + int ret; + unsigned int outputChannelCount, i; + RawChannel *outputChannel; + bool pending = false; + + if (mVscaleFlushPending) + return; + + RawSource::lock(); + if (mOutputMedia != nullptr) { + outputChannelCount = getOutputChannelCount(mOutputMedia); + for (i = 0; i < outputChannelCount; i++) { + outputChannel = getOutputChannel(mOutputMedia, i); + if (outputChannel == nullptr) { + PDRAW_LOGW( + "failed to get output channel " + "at index %d", + i); + continue; + } + if (outputChannel->isFlushPending()) { + pending = true; + break; + } + } + } + RawSource::unlock(); + + if (pending) + return; + + RawSink::lock(); + if (mInputMedia != nullptr) { + mIsFlushed = true; + if (mInputChannelFlushPending) { + mInputChannelFlushPending = false; + RawChannel *inputChannel = getInputChannel(mInputMedia); + if (inputChannel == nullptr) { + PDRAW_LOGE("failed to get input channel"); + } else { + ret = inputChannel->flushDone(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->flushDone", + -ret); + } + } + } + RawSink::unlock(); + + tryStop(); +} + + +int VideoScaler::tryStop(void) +{ + int ret; + int outputChannelCount = 0, i; + RawChannel *channel; + + if (mState != STOPPING) + return 0; + + /* Remove the input port */ + RawSink::lock(); + if (mInputMedia != nullptr) { + channel = getInputChannel(mInputMedia); + if (channel == nullptr) { + PDRAW_LOGE("failed to get channel"); + } else { + channel->setQueue(nullptr); + channel->setPool(nullptr); + } + + ret = removeInputMedia(mInputMedia); + if (ret < 0) + PDRAW_LOG_ERRNO("removeInputMedia", -ret); + else + mInputMedia = nullptr; + } + RawSink::unlock(); + + /* Teardown the output channels + * Note: loop downwards because calling teardown on a channel may or + * may not synchronously remove the channel from the output port */ + RawSource::lock(); + if (mOutputMedia != nullptr) { + outputChannelCount = getOutputChannelCount(mOutputMedia); + + for (i = outputChannelCount - 1; i >= 0; i--) { + channel = getOutputChannel(mOutputMedia, i); + if (channel == nullptr) { + PDRAW_LOGW("failed to get channel at index %d", + i); + continue; + } + ret = channel->teardown(); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->teardown", -ret); + } + } + RawSource::unlock(); + + /* Stop the scaler */ + ret = vscale_stop(mVscale); + if (ret < 0) { + PDRAW_LOG_ERRNO("vscale_stop", -ret); + return ret; + } + + return 0; +} + + +void VideoScaler::completeStop(void) +{ + int ret; + unsigned int outputChannelCount; + + mCompleteStopPendingCount--; + + RawSource::lock(); + if (mOutputMedia == nullptr) { + RawSource::unlock(); + goto exit; + } + outputChannelCount = getOutputChannelCount(mOutputMedia); + if (outputChannelCount > 0) { + RawSource::unlock(); + return; + } + + /* Remove the output port */ + if (RawSource::mListener) { + RawSource::mListener->onOutputMediaRemoved(this, mOutputMedia); + } + ret = removeOutputPort(mOutputMedia); + if (ret < 0) { + PDRAW_LOG_ERRNO("removeOutputPort", -ret); + } else { + delete mOutputMedia; + mOutputMedia = nullptr; + } + + RawSource::unlock(); + +exit: + if ((!mVscaleStopPending) && (mCompleteStopPendingCount == 0)) + setState(STOPPED); +} + + +int VideoScaler::createOutputMedia(struct vdef_raw_frame *frameInfo, + RawVideoMedia::Frame &frame) +{ + int ret; + + RawSource::lock(); + + mOutputMedia = new RawVideoMedia(mSession); + if (mOutputMedia == nullptr) { + RawSource::unlock(); + PDRAW_LOGE("output media allocation failed"); + return -ENOMEM; + } + std::string path = mInputMedia->getPath() + ">" + Element::getName() + + "$" + mOutputMedia->getName(); + mOutputMedia->setPath(path); + + ret = addOutputPort(mOutputMedia); + if (ret < 0) { + RawSource::unlock(); + PDRAW_LOG_ERRNO("addOutputPort", -ret); + return ret; + } + + mOutputMedia->format = frameInfo->format; + vdef_frame_to_format_info(&frameInfo->info, &mOutputMedia->info); + mOutputMedia->info.framerate = mInputMedia->info.framerate; + mOutputMedia->sessionMeta = mInputMedia->sessionMeta; + mOutputMedia->playbackType = mInputMedia->playbackType; + mOutputMedia->duration = mInputMedia->duration; + + RawSource::unlock(); + + if (RawSource::mListener) + RawSource::mListener->onOutputMediaAdded(this, mOutputMedia); + + return 0; +} + + +void VideoScaler::onChannelQueue(RawChannel *channel, + struct mbuf_raw_video_frame *frame) +{ + + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + if (frame == nullptr) { + PDRAW_LOG_ERRNO("frame", EINVAL); + return; + } + if (mState != STARTED) { + PDRAW_LOGE("scaler is not started"); + return; + } + if ((mVscaleFlushPending) || (mInputChannelFlushPending)) { + PDRAW_LOGI("frame input: flush pending, discard frame"); + return; + } + RawSink::lock(); + struct mbuf_raw_video_frame_queue *queue = channel->getQueue(); + if (queue == nullptr) { + RawSink::unlock(); + PDRAW_LOGE("invalid queue"); + return; + } + if (queue != mInputBufferQueue) { + RawSink::unlock(); + PDRAW_LOGE("invalid input buffer queue"); + return; + } + + RawSink::onChannelQueue(channel, frame); + mIsFlushed = false; + RawSink::unlock(); +} + + +void VideoScaler::onChannelFlush(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + PDRAW_LOGD("flushing input channel"); + mInputChannelFlushPending = true; + + int ret = flush(); + if (ret < 0) + PDRAW_LOG_ERRNO("flush", -ret); +} + + +void VideoScaler::onChannelFlushed(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + Media *media = getOutputMediaFromChannel(channel->getKey()); + if (media == nullptr) { + PDRAW_LOGE("media not found"); + return; + } + PDRAW_LOGD("'%s': channel flushed media name=%s (channel key=%p)", + Element::getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + completeFlush(); +} + + +void VideoScaler::onChannelTeardown(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + PDRAW_LOGD("tearing down input channel"); + + int ret = stop(); + if (ret < 0) + PDRAW_LOG_ERRNO("stop", -ret); +} + + +void VideoScaler::onChannelUnlink(RawChannel *channel) +{ + if (channel == nullptr) { + PDRAW_LOG_ERRNO("channel", EINVAL); + return; + } + + RawSource::onChannelUnlink(channel); + + if (mState == STOPPING) { + mCompleteStopPendingCount++; + completeStop(); + } +} + + +void VideoScaler::frameOutputCb(struct vscale_scaler *scaler, + int status, + struct mbuf_raw_video_frame *out_frame, + void *userdata) +{ + int ret; + VideoScaler *self = (VideoScaler *)userdata; + struct vdef_raw_frame info; + struct mbuf_ancillary_data *ancillaryData = nullptr; + RawVideoMedia::Frame *in_meta; + RawVideoMedia::Frame out_meta; + unsigned int outputChannelCount, i; + RawChannel *channel; + + if (status != 0) { + PDRAW_LOGE("scaler error: %d(%s)", -status, strerror(-status)); + return; + } + + if (userdata == nullptr) { + PDRAW_LOG_ERRNO("userdata", EINVAL); + return; + } + if (out_frame == nullptr) { + PDRAW_LOG_ERRNO("out_frame", EINVAL); + return; + } + if (self->mState != STARTED) { + PDRAW_LOGE("scaler is not started"); + return; + } + if ((self->mVscaleFlushPending) || (self->mInputChannelFlushPending)) { + PDRAW_LOGI("frame output: flush pending, discard frame"); + return; + } + ret = mbuf_raw_video_frame_get_frame_info(out_frame, &info); + if (ret < 0) { + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_get_frame_info", -ret); + return; + } + + self->RawSink::lock(); + if (self->mInputMedia == nullptr) { + self->RawSink::unlock(); + PDRAW_LOG_ERRNO("invalid input media", EPROTO); + return; + } + + ret = mbuf_raw_video_frame_get_ancillary_data( + out_frame, + PDRAW_ANCILLARY_DATA_KEY_RAWVIDEOFRAME, + &ancillaryData); + if (ret < 0) { + self->RawSink::unlock(); + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_get_ancillary_data", + -ret); + return; + } + in_meta = (RawVideoMedia::Frame *)mbuf_ancillary_data_get_buffer( + ancillaryData, NULL); + out_meta = *in_meta; + out_meta.scalerOutputTimestamp = pdraw_getTimestampFromMbufFrame( + out_frame, VSCALE_ANCILLARY_KEY_OUTPUT_TIME); + mbuf_ancillary_data_unref(ancillaryData); + ancillaryData = NULL; + + ret = mbuf_raw_video_frame_remove_ancillary_data( + out_frame, PDRAW_ANCILLARY_DATA_KEY_RAWVIDEOFRAME); + if (ret < 0) { + self->RawSink::unlock(); + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_remove_ancillary_data", + -ret); + return; + } + + ret = mbuf_raw_video_frame_add_ancillary_buffer( + out_frame, + PDRAW_ANCILLARY_DATA_KEY_RAWVIDEOFRAME, + &out_meta, + sizeof(out_meta)); + if (ret < 0) { + self->RawSink::unlock(); + PDRAW_LOG_ERRNO("mbuf_raw_video_frame_add_ancillary_buffer", + -ret); + return; + } + + self->RawSink::unlock(); + self->RawSource::lock(); + + if (self->mOutputMedia == nullptr) { + ret = self->createOutputMedia(&info, out_meta); + if (ret < 0) { + self->RawSource::unlock(); + PDRAW_LOG_ERRNO("createOutputMedia", -ret); + return; + } + } + + /* Push the frame (unless it is silent) */ + if (!(info.info.flags & VDEF_FRAME_FLAG_SILENT)) { + outputChannelCount = + self->getOutputChannelCount(self->mOutputMedia); + for (i = 0; i < outputChannelCount; i++) { + channel = self->getOutputChannel(self->mOutputMedia, i); + if (channel == nullptr) { + PDRAW_LOGE("failed to get channel at index %d", + i); + continue; + } + ret = channel->queue(out_frame); + if (ret < 0) + PDRAW_LOG_ERRNO("channel->queue", -ret); + } + } else { + PDRAW_LOGD("silent frame (ignored)"); + } + + self->RawSource::unlock(); +} + + +void VideoScaler::flushCb(struct vscale_scaler *scaler, void *userdata) +{ + VideoScaler *self = (VideoScaler *)userdata; + + if (userdata == nullptr) { + PDRAW_LOG_ERRNO("userdata", EINVAL); + return; + } + + PDRAW_LOGD("scaler is flushed"); + self->mVscaleFlushPending = false; + + self->completeFlush(); +} + + +void VideoScaler::stopCb(struct vscale_scaler *scaler, void *userdata) +{ + VideoScaler *self = (VideoScaler *)userdata; + + if (userdata == nullptr) { + PDRAW_LOG_ERRNO("userdata", EINVAL); + return; + } + + PDRAW_LOGD("scaler is stopped"); + self->mVscaleStopPending = false; + + self->mCompleteStopPendingCount++; + self->completeStop(); +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_scaler_video.hpp b/libpdraw/src/pdraw_scaler_video.hpp index 3582afd..ebdedfb 100644 --- a/libpdraw/src/pdraw_scaler_video.hpp +++ b/libpdraw/src/pdraw_scaler_video.hpp @@ -1,102 +1,102 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Video scaler element - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_SCALER_VIDEO_HPP_ -#define _PDRAW_SCALER_VIDEO_HPP_ - -#include "pdraw_element.hpp" - -#include - -#include -#include - -namespace Pdraw { - -class VideoScaler : public RawToRawFilterElement { -public: - VideoScaler(Session *session, - Element::Listener *elementListener, - RawSource::Listener *sourceListener); - - ~VideoScaler(void); - - int start(void); - - int stop(void); - - void completeFlush(void); - - void completeStop(void); - -private: - int createOutputMedia(struct vdef_raw_frame *frameInfo, - RawVideoMedia::Frame &frame); - - int flush(void); - - int tryStop(void); - - void onChannelQueue(RawChannel *channel, - struct mbuf_raw_video_frame *frame); - - void onChannelFlush(RawChannel *channel); - - void onChannelFlushed(RawChannel *channel); - - void onChannelTeardown(RawChannel *channel); - - void onChannelUnlink(RawChannel *channel); - - static void frameOutputCb(struct vscale_scaler *scaler, - int status, - struct mbuf_raw_video_frame *out_frame, - void *userdata); - - static void flushCb(struct vscale_scaler *scaler, void *userdata); - - static void stopCb(struct vscale_scaler *scaler, void *userdata); - - RawVideoMedia *mInputMedia; - RawVideoMedia *mOutputMedia; - struct mbuf_pool *mInputBufferPool; - struct mbuf_raw_video_frame_queue *mInputBufferQueue; - struct vscale_scaler *mVscale; - bool mIsFlushed; - bool mInputChannelFlushPending; - bool mVscaleFlushPending; - bool mVscaleStopPending; - int mCompleteStopPendingCount; - static const struct vscale_cbs mScalerCbs; -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_SCALER_VIDEO_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Video scaler element + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_SCALER_VIDEO_HPP_ +#define _PDRAW_SCALER_VIDEO_HPP_ + +#include "pdraw_element.hpp" + +#include + +#include +#include + +namespace Pdraw { + +class VideoScaler : public RawToRawFilterElement { +public: + VideoScaler(Session *session, + Element::Listener *elementListener, + RawSource::Listener *sourceListener); + + ~VideoScaler(void); + + int start(void); + + int stop(void); + + void completeFlush(void); + + void completeStop(void); + +private: + int createOutputMedia(struct vdef_raw_frame *frameInfo, + RawVideoMedia::Frame &frame); + + int flush(void); + + int tryStop(void); + + void onChannelQueue(RawChannel *channel, + struct mbuf_raw_video_frame *frame); + + void onChannelFlush(RawChannel *channel); + + void onChannelFlushed(RawChannel *channel); + + void onChannelTeardown(RawChannel *channel); + + void onChannelUnlink(RawChannel *channel); + + static void frameOutputCb(struct vscale_scaler *scaler, + int status, + struct mbuf_raw_video_frame *out_frame, + void *userdata); + + static void flushCb(struct vscale_scaler *scaler, void *userdata); + + static void stopCb(struct vscale_scaler *scaler, void *userdata); + + RawVideoMedia *mInputMedia; + RawVideoMedia *mOutputMedia; + struct mbuf_pool *mInputBufferPool; + struct mbuf_raw_video_frame_queue *mInputBufferQueue; + struct vscale_scaler *mVscale; + bool mIsFlushed; + bool mInputChannelFlushPending; + bool mVscaleFlushPending; + bool mVscaleStopPending; + int mCompleteStopPendingCount; + static const struct vscale_cbs mScalerCbs; +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_SCALER_VIDEO_HPP_ */ diff --git a/libpdraw/src/pdraw_session.cpp b/libpdraw/src/pdraw_session.cpp index 2e6a562..528e48b 100644 --- a/libpdraw/src/pdraw_session.cpp +++ b/libpdraw/src/pdraw_session.cpp @@ -1,2268 +1,2268 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Session - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_session -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_decoder_video.hpp" -#include "pdraw_demuxer_record.hpp" -#include "pdraw_demuxer_stream_mux.hpp" -#include "pdraw_demuxer_stream_net.hpp" -#include "pdraw_encoder_video.hpp" -#include "pdraw_muxer_record.hpp" -#include "pdraw_muxer_stream_rtmp.hpp" -#include "pdraw_scaler_video.hpp" -#include "pdraw_session.hpp" -#include "pdraw_utils.hpp" - -#include -#include - -#include -#include -#include - -namespace Pdraw { - - -int createPdraw(struct pomp_loop *loop, - IPdraw::Listener *listener, - IPdraw **retObj) -{ - IPdraw *pdraw = nullptr; - if (loop == nullptr) { - ULOGE("invalid loop"); - return -EINVAL; - } - if (retObj == nullptr) { - ULOGE("invalid pointer"); - return -EINVAL; - } - pdraw = new Session(loop, listener); - if (pdraw == nullptr) { - ULOGE("failed to create pdraw instance"); - return -EPROTO; - } - *retObj = pdraw; - return 0; -} - - -const char *pdrawHmdModelStr(enum pdraw_hmd_model val) -{ - return pdraw_hmdModelStr(val); -} - - -const char *pdrawPipelineModeStr(enum pdraw_pipeline_mode val) -{ - return pdraw_pipelineModeStr(val); -} - - -const char *pdrawPlaybackTypeStr(enum pdraw_playback_type val) -{ - return pdraw_playbackTypeStr(val); -} - - -const char *pdrawMediaTypeStr(enum pdraw_media_type val) -{ - return pdraw_mediaTypeStr(val); -} - - -const char *pdrawVideoTypeStr(enum pdraw_video_type val) -{ - return pdraw_videoTypeStr(val); -} - - -const char *pdrawHistogramChannelStr(enum pdraw_histogram_channel val) -{ - return pdraw_histogramChannelStr(val); -} - - -const char *pdrawVideoRendererSchedulingModeStr( - enum pdraw_video_renderer_scheduling_mode val) -{ - return pdraw_videoRendererSchedulingModeStr(val); -} - - -const char * -pdrawVideoRendererFillModeStr(enum pdraw_video_renderer_fill_mode val) -{ - return pdraw_videoRendererFillModeStr(val); -} - - -const char *pdrawVideoRendererTransitionFlagStr( - enum pdraw_video_renderer_transition_flag val) -{ - return pdraw_videoRendererTransitionFlagStr(val); -} - - -int pdrawVideoFrameToJsonStr(const struct pdraw_video_frame *frame, - struct vmeta_frame *metadata, - char *str, - unsigned int len) -{ - return pdraw_frameMetadataToJsonStr(frame, metadata, str, len); -} - - -int pdrawVideoFrameToJson(const struct pdraw_video_frame *frame, - struct vmeta_frame *metadata, - struct json_object *jobj) -{ - return pdraw_frameMetadataToJson(frame, metadata, jobj); -} - - -struct pdraw_media_info *pdrawMediaInfoDup(const struct pdraw_media_info *src) -{ - return pdraw_mediaInfoDup(src); -} - - -void pdrawMediaInfoFree(struct pdraw_media_info *media_info) -{ - return pdraw_mediaInfoFree(media_info); -} - - -Session::Session(struct pomp_loop *loop, IPdraw::Listener *listener) : - mFactory(this), mListener(listener), mState(STOPPED), - mLoop(loop), mAndroidJvm(nullptr) - -{ - int res; - pthread_mutexattr_t attr; - bool attr_created = false; - bool mutex_created = false; - - mLoopThread = pthread_self(); - - res = pthread_mutexattr_init(&attr); - if (res != 0) { - ULOG_ERRNO("pthread_mutexattr_init", res); - goto error; - } - attr_created = true; - - res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - if (res != 0) { - ULOG_ERRNO("pthread_mutexattr_settype", res); - goto error; - } - - res = pthread_mutex_init(&mMutex, &attr); - if (res != 0) { - ULOG_ERRNO("pthread_mutex_init(mMutex)", res); - goto error; - } - pthread_mutexattr_destroy(&attr); - mutex_created = true; - - res = pthread_mutex_init(&mAsyncMutex, NULL); - if (res != 0) { - ULOG_ERRNO("pthread_mutex_init(mAsyncMutex)", res); - goto error; - } - - setState(READY); - return; - -error: - if (mutex_created) - pthread_mutex_destroy(&mMutex); - else if (attr_created) - pthread_mutexattr_destroy(&attr); -} - - -Session::~Session(void) -{ - if (mState != STOPPED) - ULOGW("destroying while instance is still running"); - - /* Remove any leftover idle callbacks */ - if (mLoop != nullptr) { - pomp_loop_idle_remove(mLoop, idleElementStateChange, this); - pomp_loop_idle_remove(mLoop, idleElementDelete, this); - pomp_loop_idle_remove(mLoop, idleRendererCompleteStop, this); - pomp_loop_idle_remove(mLoop, callStopResponse, this); - pomp_loop_idle_remove(mLoop, callOnMediaAdded, this); - pomp_loop_idle_remove(mLoop, callOnMediaRemoved, this); - } - - pthread_mutex_lock(&mMutex); - std::vector::iterator e = mElements.begin(); - while (e != mElements.end()) { - delete *e; - e++; - } - mElements.clear(); - pthread_mutex_unlock(&mMutex); - - pthread_mutex_lock(&mAsyncMutex); - while (!mMediaAddedInfoArgs.empty()) { - struct pdraw_media_info info = mMediaAddedInfoArgs.front(); - mMediaAddedInfoArgs.pop(); - Media::cleanupMediaInfo(&info); - } - while (!mMediaRemovedInfoArgs.empty()) { - struct pdraw_media_info info = mMediaRemovedInfoArgs.front(); - mMediaRemovedInfoArgs.pop(); - Media::cleanupMediaInfo(&info); - } - pthread_mutex_unlock(&mAsyncMutex); - - pthread_mutex_destroy(&mMutex); - pthread_mutex_destroy(&mAsyncMutex); -} - - -/* - * API methods - */ - -int Session::stop(void) -{ - int ret; - bool stopped = true; - std::vector::iterator e; - - if (mState == STOPPING) { - /* Return without calling the stopResponse() function */ - ULOGI("%s: already in %s state, nothing to do", - __func__, - stateStr(mState)); - return 0; - } - - if (mState == STOPPED) { - /* Call the stopResponse() function with OK status */ - ULOGI("%s: state is %s, nothing to do", - __func__, - stateStr(mState)); - ret = 0; - goto already_stopped; - } - - if (mState != READY) { - ULOGE("%s: invalid state (%s)", __func__, stateStr(mState)); - return -EPROTO; - } - - setState(STOPPING); - - pthread_mutex_lock(&mMutex); - e = mElements.begin(); - while (e != mElements.end()) { - if ((*e)->getState() != Element::State::STOPPED) { - stopped = false; - } else { - int err = (*e)->stop(); - if (err < 0) - ULOG_ERRNO("element->stop", -err); - } - e++; - } - pthread_mutex_unlock(&mMutex); - - if (stopped) { - /* Call the stopResponse() function with OK status */ - ULOGI("%s: all elements are stopped, closing", __func__); - setState(STOPPED); - ret = 0; - goto already_stopped; - } - - /* Waiting for the asynchronous stop; stopResponse() - * will be called when it's done */ - return 0; - -already_stopped: - if (mListener != nullptr && ret == 0) - stopResp(ret); - return ret; -} - - -/* Called on the rendering thread */ -int Session::createVideoRenderer( - unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - IPdraw::IVideoRenderer::Listener *listener, - IPdraw::IVideoRenderer **retObj, - struct egl_display *eglDisplay) -{ - int res; - Session::VideoRenderer *renderer = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(renderPos == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(params == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(listener == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); - - pthread_mutex_lock(&mMutex); - if (mState == STOPPING || mState == STOPPED) { - ULOGE("renderer creation refused in %s state", - stateStr(mState)); - pthread_mutex_unlock(&mMutex); - return -EPROTO; - } - - renderer = new Session::VideoRenderer( - this, mediaId, renderPos, params, listener, eglDisplay); - if (renderer == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOGE("%s: failed to create the video renderer", __func__); - return -ENOMEM; - } - - mElements.push_back(renderer->getElement()); - pthread_mutex_unlock(&mMutex); - - res = renderer->getElement()->start(); - if (res < 0) { - ULOG_ERRNO("renderer->start", -res); - return res; - } - - *retObj = renderer; - - return 0; -} - - -/* Called on the rendering thread */ -Session::VideoRenderer::VideoRenderer( - Session *session, - unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - IPdraw::IVideoRenderer::Listener *listener, - struct egl_display *eglDisplay) -{ - mRenderer = Renderer::create(session, - session, - this, - listener, - mediaId, - renderPos, - params, - eglDisplay); - if (mRenderer == nullptr) { - ULOGE("%s: failed to create the video renderer", __func__); - return; - } -} - - -/* Called on the rendering thread */ -Session::VideoRenderer::~VideoRenderer(void) -{ - if (mRenderer == nullptr) - return; - - int ret = mRenderer->stop(); - if (ret < 0) - ULOG_ERRNO("renderer->stop", -ret); -} - - -/* Called on the rendering thread */ -int Session::VideoRenderer::resize(const struct pdraw_rect *renderPos) -{ - if (mRenderer == nullptr) - return -EPROTO; - - return mRenderer->resize(renderPos); -} - - -/* Called on the rendering thread */ -int Session::VideoRenderer::setMediaId(unsigned int mediaId) -{ - if (mRenderer == nullptr) - return -EPROTO; - - return mRenderer->setMediaId(mediaId); -} - - -/* Called on the rendering thread */ -unsigned int Session::VideoRenderer::getMediaId(void) -{ - if (mRenderer == nullptr) - return -EPROTO; - - return mRenderer->getMediaId(); -} - - -/* Called on the rendering thread */ -int Session::VideoRenderer::setParams( - const struct pdraw_video_renderer_params *params) -{ - if (mRenderer == nullptr) - return -EPROTO; - - return mRenderer->setParams(params); -} - - -/* Called on the rendering thread */ -int Session::VideoRenderer::getParams( - struct pdraw_video_renderer_params *params) -{ - if (mRenderer == nullptr) - return -EPROTO; - - return mRenderer->getParams(params); -} - - -/* Called on the rendering thread */ -int Session::VideoRenderer::render(struct pdraw_rect *contentPos, - const float *viewMat, - const float *projMat) -{ - if (mRenderer == nullptr) - return -EPROTO; - - return mRenderer->render(contentPos, viewMat, projMat); -} - - -int Session::createDemuxer(const std::string &url, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj) -{ - return createDemuxer(url, nullptr, listener, retObj); -} - - -int Session::createDemuxer(const std::string &localAddr, - uint16_t localStreamPort, - uint16_t localControlPort, - const std::string &remoteAddr, - uint16_t remoteStreamPort, - uint16_t remoteControlPort, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj) -{ - int res; - Session::Demuxer *demuxer = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(listener == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); - - pthread_mutex_lock(&mMutex); - if (mState == STOPPING || mState == STOPPED) { - ULOGE("demuxer creation refused in %s state", stateStr(mState)); - pthread_mutex_unlock(&mMutex); - return -EPROTO; - } - - demuxer = new Session::Demuxer(this, - localAddr, - localStreamPort, - localControlPort, - remoteAddr, - remoteStreamPort, - remoteControlPort, - listener); - if (demuxer == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOGE("%s: failed to create the demuxer", __func__); - return -ENOMEM; - } - if (demuxer->getElement() == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOGE("%s: failed to create the demuxer", __func__); - delete demuxer; - return -EINVAL; - } - - mElements.push_back(demuxer->getElement()); - pthread_mutex_unlock(&mMutex); - - res = demuxer->getElement()->start(); - if (res < 0) { - ULOG_ERRNO("demuxer->start", -res); - goto error; - } - - *retObj = demuxer; - - /* Waiting for the asynchronous open; openResponse() - * will be called when it's done */ - return 0; - -error: - if (demuxer != nullptr) { - pthread_mutex_lock(&mMutex); - std::vector::iterator e = mElements.begin(); - while (e != mElements.end()) { - if (*e != demuxer->getElement()) { - e++; - continue; - } - mElements.erase(e); - break; - } - pthread_mutex_unlock(&mMutex); - delete demuxer; - } - return res; -} - - -int Session::createDemuxer(const std::string &url, - struct mux_ctx *mux, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj) -{ - int res; - Session::Demuxer *demuxer = nullptr; - - ULOG_ERRNO_RETURN_ERR_IF(url.length() == 0, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(listener == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); - - pthread_mutex_lock(&mMutex); - if (mState == STOPPING || mState == STOPPED) { - ULOGE("demuxer creation refused in %s state", stateStr(mState)); - pthread_mutex_unlock(&mMutex); - return -EPROTO; - } - - demuxer = new Session::Demuxer(this, url, mux, listener); - if (demuxer == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOGE("%s: failed to create the demuxer", __func__); - return -ENOMEM; - } - if (demuxer->getElement() == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOGE("%s: failed to create the demuxer", __func__); - delete demuxer; - return -EINVAL; - } - - mElements.push_back(demuxer->getElement()); - pthread_mutex_unlock(&mMutex); - - res = demuxer->getElement()->start(); - if (res < 0) { - ULOG_ERRNO("demuxer->start", -res); - goto error; - } - - *retObj = demuxer; - - /* Waiting for the asynchronous open; openResponse() - * will be called when it's done */ - return 0; - -error: - if (demuxer != nullptr) { - pthread_mutex_lock(&mMutex); - std::vector::iterator e = mElements.begin(); - while (e != mElements.end()) { - if (*e != demuxer->getElement()) { - e++; - continue; - } - mElements.erase(e); - break; - } - pthread_mutex_unlock(&mMutex); - delete demuxer; - } - return res; -} - - -Session::Demuxer::Demuxer(Session *session, - const std::string &url, - struct mux_ctx *mux, - IPdraw::IDemuxer::Listener *listener) : - mSession(session), - mDemuxer(nullptr) -{ - std::string ext; - - if (url.length() < 4) { - ULOGE("%s: invalid URL length", __func__); - return; - } - ext = url.substr(url.length() - 4, 4); - std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); - - if ((mux != nullptr) && (url.substr(0, 7) == "rtsp://")) { -#ifdef BUILD_LIBMUX - StreamDemuxerMux *demuxer; - demuxer = new StreamDemuxerMux( - mSession, mSession, mSession, this, listener, url, mux); - if (demuxer == nullptr) { - ULOGE("%s: failed to create the stream demuxer", - __func__); - return; - } - mDemuxer = demuxer; -#else /* BUILD_LIBMUX */ - ULOGE("%s: libmux is not supported", __func__); -#endif /* BUILD_LIBMUX */ - } else if (url.substr(0, 7) == "rtsp://") { - StreamDemuxerNet *demuxer; - demuxer = new StreamDemuxerNet( - mSession, mSession, mSession, this, listener, url); - if (demuxer == nullptr) { - ULOGE("%s: failed to create the stream demuxer", - __func__); - return; - } - mDemuxer = demuxer; - } else if (ext == ".mp4") { - RecordDemuxer *demuxer; - demuxer = new RecordDemuxer( - mSession, mSession, mSession, this, listener, url); - if (demuxer == nullptr) { - ULOGE("%s: failed to create the record demuxer", - __func__); - return; - } - mDemuxer = demuxer; - } else { - ULOGE("%s: unsupported URL", __func__); - } -} - - -Session::Demuxer::Demuxer(Session *session, - const std::string &localAddr, - uint16_t localStreamPort, - uint16_t localControlPort, - const std::string &remoteAddr, - uint16_t remoteStreamPort, - uint16_t remoteControlPort, - IPdraw::IDemuxer::Listener *listener) : - mSession(session), - mDemuxer(nullptr) -{ - StreamDemuxerNet *demuxer; - demuxer = new StreamDemuxerNet(mSession, - mSession, - mSession, - this, - listener, - localAddr, - localStreamPort, - localControlPort, - remoteAddr, - remoteStreamPort, - remoteControlPort); - if (demuxer == nullptr) { - ULOGE("%s: failed to create the stream demuxer", __func__); - return; - } - mDemuxer = demuxer; -} - - -Session::Demuxer::~Demuxer(void) -{ - if (mDemuxer == nullptr) - return; - - int res = mDemuxer->stop(); - if (res < 0) - ULOG_ERRNO("Demuxer::stop", -res); -} - - -int Session::Demuxer::close(void) -{ - int res; - - if (mDemuxer == nullptr) - return -EPROTO; - - res = mDemuxer->stop(); - if (res < 0) { - ULOG_ERRNO("Demuxer::stop", -res); - return res; - } - - /* Waiting for the asynchronous stop; closeResponse() - * will be called when it's done */ - mDemuxer = nullptr; - return 0; -} - - -uint16_t Session::Demuxer::getSingleStreamLocalStreamPort(void) -{ - if (mDemuxer == nullptr) - return 0; - - StreamDemuxerNet *demuxer = dynamic_cast(mDemuxer); - if (demuxer == nullptr) { - ULOGE("%s: invalid demuxer", __func__); - return 0; - } - - return demuxer->getSingleStreamLocalStreamPort(); -} - - -uint16_t Session::Demuxer::getSingleStreamLocalControlPort(void) -{ - if (mDemuxer == nullptr) - return 0; - - StreamDemuxerNet *demuxer = dynamic_cast(mDemuxer); - if (demuxer == nullptr) { - ULOGE("%s: invalid demuxer", __func__); - return 0; - } - - return demuxer->getSingleStreamLocalControlPort(); -} - - -bool Session::Demuxer::isReadyToPlay(void) -{ - if (mDemuxer == nullptr) - return false; - - return mDemuxer->isReadyToPlay(); -} - - -bool Session::Demuxer::isPaused(void) -{ - if (mDemuxer == nullptr) - return false; - - return mDemuxer->isPaused(); -} - - -int Session::Demuxer::play(float speed) -{ - if (mDemuxer == nullptr) - return -EPROTO; - - return mDemuxer->play(speed); -} - - -int Session::Demuxer::pause(void) -{ - return play(0.); -} - - -int Session::Demuxer::previousFrame(void) -{ - if (mDemuxer == nullptr) - return -EPROTO; - return mDemuxer->previous(); -} - - -int Session::Demuxer::nextFrame(void) -{ - if (mDemuxer == nullptr) - return -EPROTO; - return mDemuxer->next(); -} - - -int Session::Demuxer::seek(int64_t delta, bool exact) -{ - if (mDemuxer == nullptr) - return -EPROTO; - - return mDemuxer->seek(delta, exact); -} - - -int Session::Demuxer::seekForward(uint64_t delta, bool exact) -{ - return seek((int64_t)delta); -} - - -int Session::Demuxer::seekBack(uint64_t delta, bool exact) -{ - return seek(-((int64_t)delta)); -} - - -int Session::Demuxer::seekTo(uint64_t timestamp, bool exact) -{ - if (mDemuxer == nullptr) - return -EPROTO; - - return mDemuxer->seekTo(timestamp, exact); -} - - -uint64_t Session::Demuxer::getDuration(void) -{ - if (mDemuxer == nullptr) - return 0; - - return mDemuxer->getDuration(); -} - - -uint64_t Session::Demuxer::getCurrentTime(void) -{ - if (mDemuxer == nullptr) - return 0; - - return mDemuxer->getCurrentTime(); -} - - -int Session::createMuxer(const std::string &url, IPdraw::IMuxer **retObj) -{ - ULOG_ERRNO_RETURN_ERR_IF(url.length() == 0, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); - -#if MUXER_TEST - int res; - Session::Muxer *muxer = nullptr; - - pthread_mutex_lock(&mMutex); - if (mState == STOPPING || mState == STOPPED) { - ULOGE("muxer creation refused in %s state", stateStr(mState)); - pthread_mutex_unlock(&mMutex); - return -EPROTO; - } - - muxer = new Session::Muxer(this, url); - if (muxer == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOGE("%s: failed to create the muxer", __func__); - return -ENOMEM; - } - - mElements.push_back(muxer->getElement()); - pthread_mutex_unlock(&mMutex); - - res = muxer->getElement()->start(); - if (res < 0) { - ULOG_ERRNO("muxer->start", -res); - goto error; - } - - *retObj = muxer; - - return 0; - -error: - if (muxer != nullptr) { - pthread_mutex_lock(&mMutex); - std::vector::iterator e = mElements.begin(); - while (e != mElements.end()) { - if (*e != muxer->getElement()) { - e++; - continue; - } - mElements.erase(e); - break; - } - pthread_mutex_unlock(&mMutex); - delete muxer; - } - return res; - -#else - /* Not supported yet */ - return -ENOSYS; -#endif -} - - -Session::Muxer::Muxer(Session *session, const std::string &url) -{ - std::string ext; - - mSession = session; - mMuxer = nullptr; - - if (url.length() < 4) { - ULOGE("%s: invalid URL length", __func__); - return; - } - ext = url.substr(url.length() - 4, 4); - std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); - - if (url.substr(0, 7) == "rtmp://") { -#ifdef BUILD_LIBRTMP - RtmpStreamMuxer *muxer; - muxer = new Pdraw::RtmpStreamMuxer(session, session, url); - if (muxer == nullptr) { - ULOGE("%s: failed to create the RTMP stream muxer", - __func__); - return; - } - mMuxer = muxer; -#else - ULOGE("%s: librtmp is not supported", __func__); -#endif - } else if (ext == ".mp4") { - RecordMuxer *muxer; - muxer = new Pdraw::RecordMuxer(session, session, url); - if (muxer == nullptr) { - ULOGE("%s: failed to create the record muxer", - __func__); - return; - } - mMuxer = muxer; - } else { - ULOGE("%s: unsupported URL", __func__); - } -} - - -Session::Muxer::~Muxer(void) -{ - if (mMuxer == nullptr) - return; - - int res = mMuxer->stop(); - if (res < 0) - ULOG_ERRNO("Muxer::stop", -res); -} - - -int Session::Muxer::addMedia( - unsigned int mediaId, - const struct pdraw_muxer_video_media_params *params) -{ - int res = 0; - CodedSource *source = nullptr; - CodedVideoMedia *media = nullptr; - CodedChannel *channel = nullptr; - bool found = false; - - if (mMuxer == nullptr) - return -EPROTO; - - pthread_mutex_lock(&mSession->mMutex); - - std::vector::iterator e = mSession->mElements.begin(); - while (e != mSession->mElements.end()) { - source = dynamic_cast(*e); - if (source == nullptr) { - e++; - continue; - } - unsigned int mediaCount = source->getOutputMediaCount(); - for (unsigned int i = 0; i < mediaCount; i++) { - media = source->getOutputMedia(i); - if ((media != nullptr) && (media->id == mediaId)) { - found = true; - break; - } - } - if (found) - break; - e++; - } - if ((!found) || (source == nullptr) || (media == nullptr)) { - pthread_mutex_unlock(&mSession->mMutex); - return -ENOENT; - } - - res = mMuxer->addInputMedia(media); - if (res < 0) { - ULOG_ERRNO("RecordMuxer::addInputMedia", -res); - goto out; - } - channel = mMuxer->getInputChannel(media); - if (channel == nullptr) { - ULOGE("failed to get muxer input channel"); - res = -EPROTO; - goto out; - } - res = source->addOutputChannel(media, channel); - if (res < 0) { - ULOG_ERRNO("Source::addOutputChannel", -res); - goto out; - } - -out: - pthread_mutex_unlock(&mSession->mMutex); - return res; -} - - -Session::CodedVideoSink::CodedVideoSink( - Session *session, - const struct pdraw_video_sink_params *params, - IPdraw::ICodedVideoSink::Listener *listener) -{ - mSink = new Pdraw::ExternalCodedVideoSink( - session, - ¶ms->required_coded_format, - session, - listener, - this, - params); - if (mSink == nullptr) { - ULOGE("%s: failed to create the video sink", __func__); - return; - } -} - - -Session::CodedVideoSink::~CodedVideoSink(void) -{ - if (mSink == nullptr) - return; - int ret = mSink->stop(); - if (ret < 0) - ULOG_ERRNO("sink->stop", -ret); -} - - -int Session::CodedVideoSink::resync(void) -{ - if (mSink == nullptr) - return -EPROTO; - return mSink->resync(); -} - - -struct mbuf_coded_video_frame_queue *Session::CodedVideoSink::getQueue(void) -{ - if (mSink == nullptr) - return nullptr; - return mSink->getQueue(); -} - - -int Session::CodedVideoSink::queueFlushed(void) -{ - if (mSink == nullptr) - return -EPROTO; - return mSink->flushDone(); -} - - -Session::RawVideoSink::RawVideoSink( - Session *session, - const struct pdraw_video_sink_params *params, - IPdraw::IRawVideoSink::Listener *listener) -{ - mSink = new Pdraw::ExternalRawVideoSink( - session, session, listener, this, params); - if (mSink == nullptr) { - ULOGE("%s: failed to create the video sink", __func__); - return; - } -} - - -Session::RawVideoSink::~RawVideoSink(void) -{ - if (mSink == nullptr) - return; - int ret = mSink->stop(); - if (ret < 0) - ULOG_ERRNO("sink->stop", -ret); -} - - -struct mbuf_raw_video_frame_queue *Session::RawVideoSink::getQueue(void) -{ - if (mSink == nullptr) - return nullptr; - return mSink->getQueue(); -} - - -int Session::RawVideoSink::queueFlushed(void) -{ - if (mSink == nullptr) - return -EPROTO; - return mSink->flushDone(); -} - - -int Session::internalCreateCodedVideoSink( - CodedSource *source, - CodedVideoMedia *media, - const struct pdraw_video_sink_params *params, - IPdraw::ICodedVideoSink::Listener *listener, - IPdraw::ICodedVideoSink **retObj) -{ - /* Note: mMutex is held while this function is called */ - int res; - Session::CodedVideoSink *sink = nullptr; - CodedChannel *channel = nullptr; - - sink = new CodedVideoSink(this, params, listener); - if (sink == nullptr) { - ULOGE("coded video sink creation failed"); - return -ENOMEM; - } - - mElements.push_back(sink->getElement()); - - res = sink->getSink()->addInputMedia(media); - if (res < 0) { - ULOG_ERRNO("codedVideoSink->addInputMedia", -res); - goto error; - } - - res = sink->getElement()->start(); - if (res < 0) { - ULOG_ERRNO("codedVideoSink->start", -res); - goto error; - } - - channel = sink->getSink()->getInputChannel(media); - if (channel == nullptr) { - ULOGE("failed to get coded video sink input channel"); - res = -EPROTO; - goto error; - } - - res = source->addOutputChannel(media, channel); - if (res < 0) { - ULOG_ERRNO("source->addOutputChannel", -res); - goto error; - } - - /* Force a resync after linking the elements; this allows a coded - * video sink to start on an IDR frame for example */ - res = sink->getCodedVideoSink()->resync(); - if (res < 0) { - ULOG_ERRNO("codedVideoSink->resync", -res); - goto error; - } - - *retObj = sink; - - return 0; - -error: - if (sink != nullptr) { - if (channel != nullptr) { - /* removeOutputChannel must be called without mMutex - * being held, so release it here */ - pthread_mutex_unlock(&mMutex); - source->removeOutputChannel(media, channel->getKey()); - pthread_mutex_lock(&mMutex); - } - std::vector::iterator e = mElements.begin(); - while (e != mElements.end()) { - if (*e != sink->getElement()) { - e++; - continue; - } - mElements.erase(e); - break; - } - delete sink; - } - return res; -} - - -int Session::internalCreateRawVideoSink( - RawSource *source, - RawVideoMedia *media, - const struct pdraw_video_sink_params *params, - IPdraw::IRawVideoSink::Listener *listener, - IPdraw::IRawVideoSink **retObj) -{ - /* Note: mMutex is held while this function is called */ - int res; - Session::RawVideoSink *sink = nullptr; - RawChannel *channel = nullptr; - - sink = new RawVideoSink(this, params, listener); - if (sink == nullptr) { - ULOGE("raw video sink creation failed"); - return -ENOMEM; - } - - mElements.push_back(sink->getElement()); - - res = sink->getSink()->addInputMedia(media); - if (res < 0) { - ULOG_ERRNO("rawVideoSink->addInputMedia", -res); - goto error; - } - - res = sink->getElement()->start(); - if (res < 0) { - ULOG_ERRNO("rawVideoSink->start", -res); - goto error; - } - - channel = sink->getSink()->getInputChannel(media); - if (channel == nullptr) { - ULOGE("failed to get raw video sink input channel"); - res = -EPROTO; - goto error; - } - - res = source->addOutputChannel(media, channel); - if (res < 0) { - ULOG_ERRNO("source->addOutputChannel", -res); - goto error; - } - - *retObj = sink; - - return 0; - -error: - if (sink != nullptr) { - if (channel != nullptr) { - /* removeOutputChannel must be called without mMutex - * being held, so release it here */ - pthread_mutex_unlock(&mMutex); - source->removeOutputChannel(media, channel->getKey()); - pthread_mutex_lock(&mMutex); - } - std::vector::iterator e = mElements.begin(); - while (e != mElements.end()) { - if (*e != sink->getElement()) { - e++; - continue; - } - mElements.erase(e); - break; - } - delete sink; - } - return res; -} - - -int Session::createCodedVideoSink(unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::ICodedVideoSink::Listener *listener, - IPdraw::ICodedVideoSink **retObj) -{ - int ret = 0; - bool found = false; - - if (params == nullptr) - return -EINVAL; - if (listener == nullptr) - return -EINVAL; - if (retObj == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - - std::vector::iterator e = mElements.begin(); - while (e != mElements.end()) { - CodedVideoMedia *media; - CodedSource *source = dynamic_cast(*e); - if (source == nullptr) { - e++; - continue; - } - unsigned int mediaCount = source->getOutputMediaCount(); - for (unsigned int i = 0; i < mediaCount; i++) { - media = source->getOutputMedia(i); - if ((media != nullptr) && (media->id == mediaId)) { - found = true; - break; - } - } - if (found) { - ret = internalCreateCodedVideoSink( - source, media, params, listener, retObj); - goto exit; - } - e++; - } - ret = -ENOENT; -exit: - pthread_mutex_unlock(&mMutex); - return ret; -} - - -int Session::createRawVideoSink(unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::IRawVideoSink::Listener *listener, - IPdraw::IRawVideoSink **retObj) -{ - int ret = 0; - bool found = false; - - if (params == nullptr) - return -EINVAL; - if (listener == nullptr) - return -EINVAL; - if (retObj == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - - std::vector::iterator e = mElements.begin(); - while (e != mElements.end()) { - RawVideoMedia *media; - RawSource *source = dynamic_cast(*e); - if (source == nullptr) { - e++; - continue; - } - unsigned int mediaCount = source->getOutputMediaCount(); - for (unsigned int i = 0; i < mediaCount; i++) { - media = source->getOutputMedia(i); - if ((media != nullptr) && (media->id == mediaId)) { - found = true; - break; - } - } - if (found) { - ret = internalCreateRawVideoSink( - source, media, params, listener, retObj); - goto exit; - } - e++; - } - ret = -ENOENT; -exit: - pthread_mutex_unlock(&mMutex); - return ret; -} - - -void Session::getFriendlyNameSetting(std::string *friendlyName) -{ - mSettings.getFriendlyName(friendlyName); -} - - -void Session::setFriendlyNameSetting(const std::string &friendlyName) -{ - mSettings.setFriendlyName(friendlyName); -} - - -void Session::getSerialNumberSetting(std::string *serialNumber) -{ - mSettings.getSerialNumber(serialNumber); -} - - -void Session::setSerialNumberSetting(const std::string &serialNumber) -{ - mSettings.setSerialNumber(serialNumber); -} - - -void Session::getSoftwareVersionSetting(std::string *softwareVersion) -{ - mSettings.getSoftwareVersion(softwareVersion); -} - - -void Session::setSoftwareVersionSetting(const std::string &softwareVersion) -{ - mSettings.setSoftwareVersion(softwareVersion); -} - - -enum pdraw_pipeline_mode Session::getPipelineModeSetting(void) -{ - return mSettings.getPipelineMode(); -} - - -void Session::setPipelineModeSetting(enum pdraw_pipeline_mode mode) -{ - if (mState != READY) { - ULOGE("%s: invalid state", __func__); - return; - } - - mSettings.setPipelineMode(mode); -} - - -void Session::getDisplayScreenSettings(float *xdpi, - float *ydpi, - float *deviceMarginTop, - float *deviceMarginBottom, - float *deviceMarginLeft, - float *deviceMarginRight) -{ - mSettings.getDisplayScreenSettings(xdpi, - ydpi, - deviceMarginTop, - deviceMarginBottom, - deviceMarginLeft, - deviceMarginRight); -} - - -void Session::setDisplayScreenSettings(float xdpi, - float ydpi, - float deviceMarginTop, - float deviceMarginBottom, - float deviceMarginLeft, - float deviceMarginRight) -{ - mSettings.setDisplayScreenSettings(xdpi, - ydpi, - deviceMarginTop, - deviceMarginBottom, - deviceMarginLeft, - deviceMarginRight); -} - - -enum pdraw_hmd_model Session::getHmdModelSetting(void) -{ - return mSettings.getHmdModelSetting(); -} - - -void Session::setHmdModelSetting(enum pdraw_hmd_model hmdModel) -{ - mSettings.setHmdModelSetting(hmdModel); -} - - -int Session::dumpPipeline(const std::string &fileName) -{ - return mFactory.dumpPipeline(fileName); -} - - -/* - * Internal methods - */ - -void Session::asyncElementStateChange(Element *element, Element::State state) -{ - pthread_mutex_lock(&mAsyncMutex); - mElementStateChangeElementArgs.push(element); - mElementStateChangeStateArgs.push(state); - pomp_loop_idle_add(mLoop, idleElementStateChange, this); - pthread_mutex_unlock(&mAsyncMutex); -} - - -int Session::addMediaToRenderer(unsigned int mediaId, Renderer *renderer) -{ - return mFactory.addMediaToRenderer(mediaId, renderer); -} - - -void Session::asyncElementDelete(Element *element) -{ - pthread_mutex_lock(&mAsyncMutex); - mElementDeleteElementArgs.push(element); - pomp_loop_idle_add(mLoop, idleElementDelete, this); - pthread_mutex_unlock(&mAsyncMutex); -} - - -void Session::setState(enum State state) -{ - pthread_mutex_lock(&mMutex); - if (state == mState) { - pthread_mutex_unlock(&mMutex); - return; - } - - mState = state; - pthread_mutex_unlock(&mMutex); - ULOGI("state change to %s", stateStr(state)); -} - - -void Session::socketCreated(int fd) -{ - if (mListener != nullptr) - mListener->onSocketCreated(this, fd); -} - - -/** - * Calls from idle functions - */ - -void Session::idleElementStateChange(void *userdata) -{ - Session *self = reinterpret_cast(userdata); - pthread_mutex_lock(&self->mAsyncMutex); - Element *element = self->mElementStateChangeElementArgs.front(); - Element::State state = self->mElementStateChangeStateArgs.front(); - self->mElementStateChangeElementArgs.pop(); - self->mElementStateChangeStateArgs.pop(); - pthread_mutex_unlock(&self->mAsyncMutex); - ULOG_ERRNO_RETURN_IF(element == nullptr, EINVAL); - self->onElementStateChanged(element, state); -} - - -void Session::idleElementDelete(void *userdata) -{ - Session *self = reinterpret_cast(userdata); - pthread_mutex_lock(&self->mAsyncMutex); - Element *element = self->mElementDeleteElementArgs.front(); - self->mElementDeleteElementArgs.pop(); - pthread_mutex_unlock(&self->mAsyncMutex); - ULOG_ERRNO_RETURN_IF(element == nullptr, EINVAL); - - pthread_mutex_lock(&self->mMutex); - std::vector::iterator e = self->mElements.begin(); - while (e != self->mElements.end()) { - if (*e != element) { - e++; - continue; - } - self->mElements.erase(e); - delete element; - break; - } - pthread_mutex_unlock(&self->mMutex); -} - - -void Session::idleRendererCompleteStop(void *userdata) -{ - Session *self = reinterpret_cast(userdata); - pthread_mutex_lock(&self->mAsyncMutex); - Renderer *renderer = self->mRendererCompleteStopRendererArgs.front(); - self->mRendererCompleteStopRendererArgs.pop(); - pthread_mutex_unlock(&self->mAsyncMutex); - ULOG_ERRNO_RETURN_IF(renderer == nullptr, EINVAL); - renderer->completeStop(); -} - - -void Session::callStopResponse(void *userdata) -{ - Session *self = reinterpret_cast(userdata); - pthread_mutex_lock(&self->mAsyncMutex); - int status = self->mStopRespStatusArgs.front(); - self->mStopRespStatusArgs.pop(); - pthread_mutex_unlock(&self->mAsyncMutex); - if (self->mListener == nullptr) - return; - self->mListener->stopResponse(self, status); -} - - -void Session::callOnMediaAdded(void *userdata) -{ - Session *self = reinterpret_cast(userdata); - pthread_mutex_lock(&self->mAsyncMutex); - struct pdraw_media_info info = self->mMediaAddedInfoArgs.front(); - self->mMediaAddedInfoArgs.pop(); - pthread_mutex_unlock(&self->mAsyncMutex); - self->mListener->onMediaAdded(self, &info); - Media::cleanupMediaInfo(&info); -} - - -void Session::callOnMediaRemoved(void *userdata) -{ - Session *self = reinterpret_cast(userdata); - pthread_mutex_lock(&self->mAsyncMutex); - struct pdraw_media_info info = self->mMediaRemovedInfoArgs.front(); - self->mMediaRemovedInfoArgs.pop(); - pthread_mutex_unlock(&self->mAsyncMutex); - self->mListener->onMediaRemoved(self, &info); - Media::cleanupMediaInfo(&info); -} - - -/* Must be called on the loop thread */ -void Session::onElementStateChanged(Element *element, Element::State state) -{ - mFactory.onElementStateChanged(element, state); - - if (state == Element::State::STOPPED) { - bool stopped = true; - State curState; - - pthread_mutex_lock(&mMutex); - - curState = mState; - - std::vector::iterator e = mElements.begin(); - while (e != mElements.end()) { - if ((*e)->getState() != Element::State::STOPPED) { - stopped = false; - break; - } - e++; - } - - pthread_mutex_unlock(&mMutex); - - asyncElementDelete(element); - - if (stopped && curState == STOPPING) { - setState(STOPPED); - - if (mListener != nullptr) - stopResp(0); - } - } -} - - -/* Must be called on the loop thread */ -void Session::onOutputMediaAdded(CodedSource *source, CodedVideoMedia *media) -{ - ULOGD("onOutputMediaAdded name=%s", media->getName().c_str()); - - mFactory.onOutputMediaAdded(source, media); - - if (mListener != nullptr) { - struct pdraw_media_info info; - media->fillMediaInfo(&info); - if (pthread_self() == mLoopThread) { - mListener->onMediaAdded(this, &info); - Media::cleanupMediaInfo(&info); - } else { - pthread_mutex_lock(&mAsyncMutex); - mMediaAddedInfoArgs.push(info); - pomp_loop_idle_add(mLoop, callOnMediaAdded, this); - pthread_mutex_unlock(&mAsyncMutex); - } - } -} - - -/* Must be called on the loop thread */ -void Session::onOutputMediaRemoved(CodedSource *source, CodedVideoMedia *media) -{ - ULOGD("onOutputMediaRemoved(coded) name=%s", media->getName().c_str()); - - mFactory.onOutputMediaRemoved(source, media); - - if (mListener != nullptr) { - struct pdraw_media_info info; - media->fillMediaInfo(&info); - pthread_mutex_lock(&mAsyncMutex); - mMediaRemovedInfoArgs.push(info); - pomp_loop_idle_add(mLoop, callOnMediaRemoved, this); - pthread_mutex_unlock(&mAsyncMutex); - } -} - - -/* Must be called on the loop thread */ -void Session::onOutputMediaAdded(RawSource *source, RawVideoMedia *media) -{ - ULOGD("onOutputMediaAdded(raw) name=%s", media->getName().c_str()); - - mFactory.onOutputMediaAdded(source, media); - - if (mListener != nullptr) { - struct pdraw_media_info info; - media->fillMediaInfo(&info); - if (pthread_self() == mLoopThread) { - mListener->onMediaAdded(this, &info); - Media::cleanupMediaInfo(&info); - } else { - pthread_mutex_lock(&mAsyncMutex); - mMediaAddedInfoArgs.push(info); - pomp_loop_idle_add(mLoop, callOnMediaAdded, this); - pthread_mutex_unlock(&mAsyncMutex); - } - } -} - - -/* Must be called on the loop thread */ -void Session::onOutputMediaRemoved(RawSource *source, RawVideoMedia *media) -{ - ULOGD("onOutputMediaRemoved(raw) name=%s", media->getName().c_str()); - - mFactory.onOutputMediaRemoved(source, media); - - if (mListener != nullptr) { - struct pdraw_media_info info; - media->fillMediaInfo(&info); - pthread_mutex_lock(&mAsyncMutex); - mMediaRemovedInfoArgs.push(info); - pomp_loop_idle_add(mLoop, callOnMediaRemoved, this); - pthread_mutex_unlock(&mAsyncMutex); - } -} - - -void Session::stopResp(int status) -{ - pthread_mutex_lock(&mAsyncMutex); - mStopRespStatusArgs.push(status); - pomp_loop_idle_add(mLoop, callStopResponse, this); - pthread_mutex_unlock(&mAsyncMutex); -} - - -const char *Session::stateStr(enum State val) -{ - switch (val) { - case State::STOPPED: - return "STOPPED"; - case State::READY: - return "READY"; - case State::STOPPING: - return "STOPPING"; - default: - return nullptr; - } -} - - -Session::PipelineFactory::PipelineFactory(Session *session) : mSession(session) -{ - return; -} - - -Session::PipelineFactory::~PipelineFactory(void) -{ - return; -} - - -void Session::PipelineFactory::onElementStateChanged(Element *element, - Element::State state) -{ - if (state == Element::State::STARTED) { - Renderer *r = dynamic_cast(element); - if (r != nullptr) { - int ret = addAllMediaToRenderer(r); - if (ret < 0) - ULOG_ERRNO("addAllMediaToRenderer", -ret); - } - } -} - - -void Session::PipelineFactory::onOutputMediaAdded(CodedSource *source, - CodedVideoMedia *media) -{ - Pdraw::Demuxer *demuxer = dynamic_cast(source); - if (demuxer != nullptr) { - if (mSession->mSettings.getPipelineMode() == - PDRAW_PIPELINE_MODE_DECODE_ALL) { - int ret = addDecoderForMedia(source, media); - if (ret < 0) - ULOG_ERRNO("addDecoderForMedia", -ret); - } - } -} - - -void Session::PipelineFactory::onOutputMediaAdded(RawSource *source, - RawVideoMedia *media) -{ - VideoDecoder *decoder = dynamic_cast(source); - if ((decoder != nullptr)) { - int ret = addMediaToAllRenderers(source, media); - if (ret < 0) - ULOG_ERRNO("addMediaToAllRenderers", -ret); - } -} - - -void Session::PipelineFactory::onOutputMediaRemoved(CodedSource *source, - CodedVideoMedia *media) -{ - return; -} - - -void Session::PipelineFactory::onOutputMediaRemoved(RawSource *source, - RawVideoMedia *media) -{ - return; -} - - -int Session::PipelineFactory::dumpPipeline(const std::string &fileName) -{ - int ret; - FILE *f; - - f = fopen(fileName.c_str(), "w"); - if (f == nullptr) { - ret = -errno; - ULOG_ERRNO("fopen", -ret); - return ret; - } - - fprintf(f, "digraph {\n"); - fprintf(f, "\tnode [margin=0.2,fontsize=12];\n"); - - pthread_mutex_lock(&mSession->mMutex); - - /* First pass: list the elements with their sink and source medias */ - std::vector::iterator e = mSession->mElements.begin(); - while (e != mSession->mElements.end()) { - unsigned int elmId = (*e)->getId(); - const char *elmName = (*e)->getName().c_str(); - fprintf(f, "\te%u [shape=record,label=\"", elmId); - - /* Element input medias */ - CodedSink *csink = dynamic_cast(*e); - if (csink != nullptr) { - unsigned int count = csink->getInputMediaCount(); - if (count > 0) - fprintf(f, "{ "); - for (unsigned int i = 0; i < count; i++) { - CodedVideoMedia *media = - csink->getInputMedia(i); - if (media == nullptr) - continue; - fprintf(f, - "%s %s", - (i > 0) ? " | " : "", - elmId, - media->id, - media->getName().c_str()); - } - if (count > 0) - fprintf(f, " } | "); - } - RawSink *rsink = dynamic_cast(*e); - if (rsink != nullptr) { - unsigned int count = rsink->getInputMediaCount(); - if (count > 0) - fprintf(f, "{ "); - for (unsigned int i = 0; i < count; i++) { - RawVideoMedia *media = rsink->getInputMedia(i); - if (media == nullptr) - continue; - fprintf(f, - "%s %s", - (i > 0) ? " | " : "", - elmId, - media->id, - media->getName().c_str()); - } - if (count > 0) - fprintf(f, " } | "); - } - - /* Element name */ - fprintf(f, " %s", elmId, elmName); - - /* Element output medias */ - CodedSource *csource = dynamic_cast(*e); - if (csource != nullptr) { - unsigned int count = csource->getOutputMediaCount(); - if (count > 0) - fprintf(f, " | { "); - for (unsigned int i = 0; i < count; i++) { - CodedVideoMedia *media = - csource->getOutputMedia(i); - if (media == nullptr) - continue; - fprintf(f, - "%s %s", - (i > 0) ? " | " : "", - elmId, - media->id, - media->getName().c_str()); - } - if (count > 0) - fprintf(f, " }"); - } - RawSource *rsource = dynamic_cast(*e); - if (rsource != nullptr) { - unsigned int count = rsource->getOutputMediaCount(); - if (count > 0) - fprintf(f, " | { "); - for (unsigned int i = 0; i < count; i++) { - RawVideoMedia *media = - rsource->getOutputMedia(i); - if (media == nullptr) - continue; - fprintf(f, - "%s %s", - (i > 0) ? " | " : "", - elmId, - media->id, - media->getName().c_str()); - } - if (count > 0) - fprintf(f, " }"); - } - - fprintf(f, "\"];\n"); - e++; - } - - /* Second pass: list the links between sources and sinks */ - e = mSession->mElements.begin(); - while (e != mSession->mElements.end()) { - unsigned int dstElmId = (*e)->getId(); - - /* Element input medias */ - CodedSink *csink = dynamic_cast(*e); - if (csink != nullptr) { - unsigned int count = csink->getInputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - CodedVideoMedia *media = - csink->getInputMedia(i); - std::vector::iterator e2 = - mSession->mElements.begin(); - while (e2 != mSession->mElements.end()) { - CodedSource *source = - dynamic_cast( - *e2); - if (source != nullptr) { - if (source->findOutputMedia( - media) == nullptr) { - e2++; - continue; - } - unsigned int srcElmId = - (*e2)->getId(); - fprintf(f, - "\te%u:e%um%u -> " - "e%u:e%um%u;\n", - srcElmId, - srcElmId, - media->id, - dstElmId, - dstElmId, - media->id); - break; - } - e2++; - } - } - } - RawSink *rsink = dynamic_cast(*e); - if (rsink != nullptr) { - unsigned int count = rsink->getInputMediaCount(); - for (unsigned int i = 0; i < count; i++) { - RawVideoMedia *media = rsink->getInputMedia(i); - std::vector::iterator e2 = - mSession->mElements.begin(); - while (e2 != mSession->mElements.end()) { - RawSource *source = - dynamic_cast(*e2); - if (source != nullptr) { - if (source->findOutputMedia( - media) == nullptr) { - e2++; - continue; - } - unsigned int srcElmId = - (*e2)->getId(); - fprintf(f, - "\te%u:e%um%u -> " - "e%u:e%um%u;\n", - srcElmId, - srcElmId, - media->id, - dstElmId, - dstElmId, - media->id); - break; - } - e2++; - } - } - } - - e++; - } - - pthread_mutex_unlock(&mSession->mMutex); - - fprintf(f, "}"); - fclose(f); - - ULOGI("pipeline dumped to file '%s'", fileName.c_str()); - - return 0; -} - - -int Session::PipelineFactory::addDecoderForMedia(CodedSource *source, - CodedVideoMedia *media) -{ - int ret; - - VideoDecoder *decoder = new VideoDecoder(mSession, mSession, mSession); - if (decoder == nullptr) { - ULOGE("decoder creation failed"); - return -ENOMEM; - } - ret = decoder->addInputMedia(media); - if (ret < 0) { - if (ret == -ENOSYS) - ret = 0; - else - ULOG_ERRNO("decoder->addInputMedia", -ret); - delete decoder; - return ret; - } - pthread_mutex_lock(&mSession->mMutex); - mSession->mElements.push_back(decoder); - pthread_mutex_unlock(&mSession->mMutex); - ret = decoder->start(); - if (ret < 0) { - ULOG_ERRNO("decoder->start", -ret); - return ret; - } - CodedChannel *channel = decoder->getInputChannel(media); - if (channel == nullptr) { - ULOGE("failed to get decoder input channel"); - return -EPROTO; - } - ret = source->addOutputChannel(media, channel); - if (ret < 0) { - ULOG_ERRNO("source->addOutputChannel", -ret); - return ret; - } - /* Force a resync after linking the elements; this allows a H.264 - * decoder to start on an IDR frame for example */ - decoder->resync(); - - return 0; -} - - -int Session::PipelineFactory::addEncoderForMedia(RawSource *source, - RawVideoMedia *media) -{ - int ret; - VideoEncoder *encoder = new VideoEncoder(mSession, mSession, mSession); - if (encoder == nullptr) { - ULOGE("encoder creation failed"); - return -ENOMEM; - } - - pthread_mutex_lock(&mSession->mMutex); - mSession->mElements.push_back(encoder); - pthread_mutex_unlock(&mSession->mMutex); - ret = encoder->addInputMedia(media); - if (ret < 0) { - ULOG_ERRNO("encoder->addInputMedia", -ret); - return ret; - } - ret = encoder->start(); - if (ret < 0) { - ULOG_ERRNO("encoder->start", -ret); - return ret; - } - RawChannel *channel = encoder->getInputChannel(media); - if (channel == nullptr) { - ULOGE("failed to get encoder input channel"); - return -EPROTO; - } - ret = source->addOutputChannel(media, channel); - if (ret < 0) { - ULOG_ERRNO("source->addOutputChannel", -ret); - return ret; - } - - return 0; -} - -int Session::PipelineFactory::addScalerForMedia(RawSource *source, - RawVideoMedia *media) -{ - int ret; - VideoScaler *scaler = new VideoScaler(mSession, mSession, mSession); - if (scaler == nullptr) { - ULOGE("scaler creation failed"); - return -ENOMEM; - } - - pthread_mutex_lock(&mSession->mMutex); - mSession->mElements.push_back(scaler); - pthread_mutex_unlock(&mSession->mMutex); - ret = scaler->addInputMedia(media); - if (ret < 0) { - ULOG_ERRNO("scaler->addInputMedia", -ret); - return ret; - } - ret = scaler->start(); - if (ret < 0) { - ULOG_ERRNO("scaler->start", -ret); - return ret; - } - RawChannel *channel = scaler->getInputChannel(media); - if (channel == nullptr) { - ULOGE("failed to get scaler input channel"); - return -EPROTO; - } - ret = source->addOutputChannel(media, channel); - if (ret < 0) { - ULOG_ERRNO("source->addOutputChannel", -ret); - return ret; - } - - return 0; -} - - -int Session::PipelineFactory::addMediaToRenderer(RawSource *source, - RawVideoMedia *media, - Renderer *renderer) -{ - int ret; - - ret = renderer->addInputMedia(media); - if ((ret == -EEXIST) || (ret == -EPERM)) { - return 0; - } else if (ret < 0) { - ULOG_ERRNO("renderer->addInputMedia", -ret); - return ret; - } - RawChannel *channel = renderer->getInputChannel(media); - if (channel == nullptr) { - ULOGE("failed to get renderer input channel"); - return -EPROTO; - } - ret = source->addOutputChannel(media, channel); - if (ret < 0) { - ULOG_ERRNO("source->addOutputChannel", -ret); - return ret; - } - return 0; -} - - -int Session::PipelineFactory::addMediaToRenderer(unsigned int mediaId, - Renderer *renderer) -{ - int ret; - - pthread_mutex_lock(&mSession->mMutex); - std::vector::iterator e = mSession->mElements.begin(); - while (e != mSession->mElements.end()) { - VideoDecoder *decoder = dynamic_cast(*e); - if (decoder == nullptr) { - e++; - continue; - } - RawVideoMedia *media = decoder->getOutputMedia(0); - if (media == nullptr) { - ULOGE("invalid media"); - e++; - continue; - } - if (media->id != mediaId) { - e++; - continue; - } - ret = addMediaToRenderer(decoder, media, renderer); - if (ret < 0) - ULOG_ERRNO("addMediaToRenderer", -ret); - break; - } - pthread_mutex_unlock(&mSession->mMutex); - - return 0; -} - - -int Session::PipelineFactory::addMediaToAllRenderers(RawSource *source, - RawVideoMedia *media) -{ - int ret = 0; - - pthread_mutex_lock(&mSession->mMutex); - std::vector::iterator e = mSession->mElements.begin(); - while (e != mSession->mElements.end() && ret == 0) { - Renderer *r = dynamic_cast(*e); - e++; - if (r == nullptr) - continue; - ret = addMediaToRenderer(source, media, r); - } - pthread_mutex_unlock(&mSession->mMutex); - - return ret; -} - - -int Session::PipelineFactory::addAllMediaToRenderer(Renderer *renderer) -{ - int ret; - - pthread_mutex_lock(&mSession->mMutex); - std::vector::iterator e = mSession->mElements.begin(); - while (e != mSession->mElements.end()) { - VideoDecoder *decoder = dynamic_cast(*e); - if (decoder == nullptr) { - e++; - continue; - } - RawVideoMedia *media = decoder->getOutputMedia(0); - if (media == nullptr) { - ULOGE("invalid media"); - e++; - continue; - } - ret = addMediaToRenderer(decoder, media, renderer); - if (ret < 0) - ULOG_ERRNO("addMediaToRenderer", -ret); - e++; - } - pthread_mutex_unlock(&mSession->mMutex); - - return 0; -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Session + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_session +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_decoder_video.hpp" +#include "pdraw_demuxer_record.hpp" +#include "pdraw_demuxer_stream_mux.hpp" +#include "pdraw_demuxer_stream_net.hpp" +#include "pdraw_encoder_video.hpp" +#include "pdraw_muxer_record.hpp" +#include "pdraw_muxer_stream_rtmp.hpp" +#include "pdraw_scaler_video.hpp" +#include "pdraw_session.hpp" +#include "pdraw_utils.hpp" + +#include +#include + +#include +#include +#include + +namespace Pdraw { + + +int createPdraw(struct pomp_loop *loop, + IPdraw::Listener *listener, + IPdraw **retObj) +{ + IPdraw *pdraw = nullptr; + if (loop == nullptr) { + ULOGE("invalid loop"); + return -EINVAL; + } + if (retObj == nullptr) { + ULOGE("invalid pointer"); + return -EINVAL; + } + pdraw = new Session(loop, listener); + if (pdraw == nullptr) { + ULOGE("failed to create pdraw instance"); + return -EPROTO; + } + *retObj = pdraw; + return 0; +} + + +const char *pdrawHmdModelStr(enum pdraw_hmd_model val) +{ + return pdraw_hmdModelStr(val); +} + + +const char *pdrawPipelineModeStr(enum pdraw_pipeline_mode val) +{ + return pdraw_pipelineModeStr(val); +} + + +const char *pdrawPlaybackTypeStr(enum pdraw_playback_type val) +{ + return pdraw_playbackTypeStr(val); +} + + +const char *pdrawMediaTypeStr(enum pdraw_media_type val) +{ + return pdraw_mediaTypeStr(val); +} + + +const char *pdrawVideoTypeStr(enum pdraw_video_type val) +{ + return pdraw_videoTypeStr(val); +} + + +const char *pdrawHistogramChannelStr(enum pdraw_histogram_channel val) +{ + return pdraw_histogramChannelStr(val); +} + + +const char *pdrawVideoRendererSchedulingModeStr( + enum pdraw_video_renderer_scheduling_mode val) +{ + return pdraw_videoRendererSchedulingModeStr(val); +} + + +const char * +pdrawVideoRendererFillModeStr(enum pdraw_video_renderer_fill_mode val) +{ + return pdraw_videoRendererFillModeStr(val); +} + + +const char *pdrawVideoRendererTransitionFlagStr( + enum pdraw_video_renderer_transition_flag val) +{ + return pdraw_videoRendererTransitionFlagStr(val); +} + + +int pdrawVideoFrameToJsonStr(const struct pdraw_video_frame *frame, + struct vmeta_frame *metadata, + char *str, + unsigned int len) +{ + return pdraw_frameMetadataToJsonStr(frame, metadata, str, len); +} + + +int pdrawVideoFrameToJson(const struct pdraw_video_frame *frame, + struct vmeta_frame *metadata, + struct json_object *jobj) +{ + return pdraw_frameMetadataToJson(frame, metadata, jobj); +} + + +struct pdraw_media_info *pdrawMediaInfoDup(const struct pdraw_media_info *src) +{ + return pdraw_mediaInfoDup(src); +} + + +void pdrawMediaInfoFree(struct pdraw_media_info *media_info) +{ + return pdraw_mediaInfoFree(media_info); +} + + +Session::Session(struct pomp_loop *loop, IPdraw::Listener *listener) : + mFactory(this), mListener(listener), mState(STOPPED), + mLoop(loop), mAndroidJvm(nullptr) + +{ + int res; + pthread_mutexattr_t attr; + bool attr_created = false; + bool mutex_created = false; + + mLoopThread = pthread_self(); + + res = pthread_mutexattr_init(&attr); + if (res != 0) { + ULOG_ERRNO("pthread_mutexattr_init", res); + goto error; + } + attr_created = true; + + res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + if (res != 0) { + ULOG_ERRNO("pthread_mutexattr_settype", res); + goto error; + } + + res = pthread_mutex_init(&mMutex, &attr); + if (res != 0) { + ULOG_ERRNO("pthread_mutex_init(mMutex)", res); + goto error; + } + pthread_mutexattr_destroy(&attr); + mutex_created = true; + + res = pthread_mutex_init(&mAsyncMutex, NULL); + if (res != 0) { + ULOG_ERRNO("pthread_mutex_init(mAsyncMutex)", res); + goto error; + } + + setState(READY); + return; + +error: + if (mutex_created) + pthread_mutex_destroy(&mMutex); + else if (attr_created) + pthread_mutexattr_destroy(&attr); +} + + +Session::~Session(void) +{ + if (mState != STOPPED) + ULOGW("destroying while instance is still running"); + + /* Remove any leftover idle callbacks */ + if (mLoop != nullptr) { + pomp_loop_idle_remove(mLoop, idleElementStateChange, this); + pomp_loop_idle_remove(mLoop, idleElementDelete, this); + pomp_loop_idle_remove(mLoop, idleRendererCompleteStop, this); + pomp_loop_idle_remove(mLoop, callStopResponse, this); + pomp_loop_idle_remove(mLoop, callOnMediaAdded, this); + pomp_loop_idle_remove(mLoop, callOnMediaRemoved, this); + } + + pthread_mutex_lock(&mMutex); + std::vector::iterator e = mElements.begin(); + while (e != mElements.end()) { + delete *e; + e++; + } + mElements.clear(); + pthread_mutex_unlock(&mMutex); + + pthread_mutex_lock(&mAsyncMutex); + while (!mMediaAddedInfoArgs.empty()) { + struct pdraw_media_info info = mMediaAddedInfoArgs.front(); + mMediaAddedInfoArgs.pop(); + Media::cleanupMediaInfo(&info); + } + while (!mMediaRemovedInfoArgs.empty()) { + struct pdraw_media_info info = mMediaRemovedInfoArgs.front(); + mMediaRemovedInfoArgs.pop(); + Media::cleanupMediaInfo(&info); + } + pthread_mutex_unlock(&mAsyncMutex); + + pthread_mutex_destroy(&mMutex); + pthread_mutex_destroy(&mAsyncMutex); +} + + +/* + * API methods + */ + +int Session::stop(void) +{ + int ret; + bool stopped = true; + std::vector::iterator e; + + if (mState == STOPPING) { + /* Return without calling the stopResponse() function */ + ULOGI("%s: already in %s state, nothing to do", + __func__, + stateStr(mState)); + return 0; + } + + if (mState == STOPPED) { + /* Call the stopResponse() function with OK status */ + ULOGI("%s: state is %s, nothing to do", + __func__, + stateStr(mState)); + ret = 0; + goto already_stopped; + } + + if (mState != READY) { + ULOGE("%s: invalid state (%s)", __func__, stateStr(mState)); + return -EPROTO; + } + + setState(STOPPING); + + pthread_mutex_lock(&mMutex); + e = mElements.begin(); + while (e != mElements.end()) { + if ((*e)->getState() != Element::State::STOPPED) { + stopped = false; + } else { + int err = (*e)->stop(); + if (err < 0) + ULOG_ERRNO("element->stop", -err); + } + e++; + } + pthread_mutex_unlock(&mMutex); + + if (stopped) { + /* Call the stopResponse() function with OK status */ + ULOGI("%s: all elements are stopped, closing", __func__); + setState(STOPPED); + ret = 0; + goto already_stopped; + } + + /* Waiting for the asynchronous stop; stopResponse() + * will be called when it's done */ + return 0; + +already_stopped: + if (mListener != nullptr && ret == 0) + stopResp(ret); + return ret; +} + + +/* Called on the rendering thread */ +int Session::createVideoRenderer( + unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + IPdraw::IVideoRenderer::Listener *listener, + IPdraw::IVideoRenderer **retObj, + struct egl_display *eglDisplay) +{ + int res; + Session::VideoRenderer *renderer = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(renderPos == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(params == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(listener == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); + + pthread_mutex_lock(&mMutex); + if (mState == STOPPING || mState == STOPPED) { + ULOGE("renderer creation refused in %s state", + stateStr(mState)); + pthread_mutex_unlock(&mMutex); + return -EPROTO; + } + + renderer = new Session::VideoRenderer( + this, mediaId, renderPos, params, listener, eglDisplay); + if (renderer == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOGE("%s: failed to create the video renderer", __func__); + return -ENOMEM; + } + + mElements.push_back(renderer->getElement()); + pthread_mutex_unlock(&mMutex); + + res = renderer->getElement()->start(); + if (res < 0) { + ULOG_ERRNO("renderer->start", -res); + return res; + } + + *retObj = renderer; + + return 0; +} + + +/* Called on the rendering thread */ +Session::VideoRenderer::VideoRenderer( + Session *session, + unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + IPdraw::IVideoRenderer::Listener *listener, + struct egl_display *eglDisplay) +{ + mRenderer = Renderer::create(session, + session, + this, + listener, + mediaId, + renderPos, + params, + eglDisplay); + if (mRenderer == nullptr) { + ULOGE("%s: failed to create the video renderer", __func__); + return; + } +} + + +/* Called on the rendering thread */ +Session::VideoRenderer::~VideoRenderer(void) +{ + if (mRenderer == nullptr) + return; + + int ret = mRenderer->stop(); + if (ret < 0) + ULOG_ERRNO("renderer->stop", -ret); +} + + +/* Called on the rendering thread */ +int Session::VideoRenderer::resize(const struct pdraw_rect *renderPos) +{ + if (mRenderer == nullptr) + return -EPROTO; + + return mRenderer->resize(renderPos); +} + + +/* Called on the rendering thread */ +int Session::VideoRenderer::setMediaId(unsigned int mediaId) +{ + if (mRenderer == nullptr) + return -EPROTO; + + return mRenderer->setMediaId(mediaId); +} + + +/* Called on the rendering thread */ +unsigned int Session::VideoRenderer::getMediaId(void) +{ + if (mRenderer == nullptr) + return -EPROTO; + + return mRenderer->getMediaId(); +} + + +/* Called on the rendering thread */ +int Session::VideoRenderer::setParams( + const struct pdraw_video_renderer_params *params) +{ + if (mRenderer == nullptr) + return -EPROTO; + + return mRenderer->setParams(params); +} + + +/* Called on the rendering thread */ +int Session::VideoRenderer::getParams( + struct pdraw_video_renderer_params *params) +{ + if (mRenderer == nullptr) + return -EPROTO; + + return mRenderer->getParams(params); +} + + +/* Called on the rendering thread */ +int Session::VideoRenderer::render(struct pdraw_rect *contentPos, + const float *viewMat, + const float *projMat) +{ + if (mRenderer == nullptr) + return -EPROTO; + + return mRenderer->render(contentPos, viewMat, projMat); +} + + +int Session::createDemuxer(const std::string &url, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj) +{ + return createDemuxer(url, nullptr, listener, retObj); +} + + +int Session::createDemuxer(const std::string &localAddr, + uint16_t localStreamPort, + uint16_t localControlPort, + const std::string &remoteAddr, + uint16_t remoteStreamPort, + uint16_t remoteControlPort, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj) +{ + int res; + Session::Demuxer *demuxer = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(listener == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); + + pthread_mutex_lock(&mMutex); + if (mState == STOPPING || mState == STOPPED) { + ULOGE("demuxer creation refused in %s state", stateStr(mState)); + pthread_mutex_unlock(&mMutex); + return -EPROTO; + } + + demuxer = new Session::Demuxer(this, + localAddr, + localStreamPort, + localControlPort, + remoteAddr, + remoteStreamPort, + remoteControlPort, + listener); + if (demuxer == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOGE("%s: failed to create the demuxer", __func__); + return -ENOMEM; + } + if (demuxer->getElement() == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOGE("%s: failed to create the demuxer", __func__); + delete demuxer; + return -EINVAL; + } + + mElements.push_back(demuxer->getElement()); + pthread_mutex_unlock(&mMutex); + + res = demuxer->getElement()->start(); + if (res < 0) { + ULOG_ERRNO("demuxer->start", -res); + goto error; + } + + *retObj = demuxer; + + /* Waiting for the asynchronous open; openResponse() + * will be called when it's done */ + return 0; + +error: + if (demuxer != nullptr) { + pthread_mutex_lock(&mMutex); + std::vector::iterator e = mElements.begin(); + while (e != mElements.end()) { + if (*e != demuxer->getElement()) { + e++; + continue; + } + mElements.erase(e); + break; + } + pthread_mutex_unlock(&mMutex); + delete demuxer; + } + return res; +} + + +int Session::createDemuxer(const std::string &url, + struct mux_ctx *mux, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj) +{ + int res; + Session::Demuxer *demuxer = nullptr; + + ULOG_ERRNO_RETURN_ERR_IF(url.length() == 0, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(listener == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); + + pthread_mutex_lock(&mMutex); + if (mState == STOPPING || mState == STOPPED) { + ULOGE("demuxer creation refused in %s state", stateStr(mState)); + pthread_mutex_unlock(&mMutex); + return -EPROTO; + } + + demuxer = new Session::Demuxer(this, url, mux, listener); + if (demuxer == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOGE("%s: failed to create the demuxer", __func__); + return -ENOMEM; + } + if (demuxer->getElement() == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOGE("%s: failed to create the demuxer", __func__); + delete demuxer; + return -EINVAL; + } + + mElements.push_back(demuxer->getElement()); + pthread_mutex_unlock(&mMutex); + + res = demuxer->getElement()->start(); + if (res < 0) { + ULOG_ERRNO("demuxer->start", -res); + goto error; + } + + *retObj = demuxer; + + /* Waiting for the asynchronous open; openResponse() + * will be called when it's done */ + return 0; + +error: + if (demuxer != nullptr) { + pthread_mutex_lock(&mMutex); + std::vector::iterator e = mElements.begin(); + while (e != mElements.end()) { + if (*e != demuxer->getElement()) { + e++; + continue; + } + mElements.erase(e); + break; + } + pthread_mutex_unlock(&mMutex); + delete demuxer; + } + return res; +} + + +Session::Demuxer::Demuxer(Session *session, + const std::string &url, + struct mux_ctx *mux, + IPdraw::IDemuxer::Listener *listener) : + mSession(session), + mDemuxer(nullptr) +{ + std::string ext; + + if (url.length() < 4) { + ULOGE("%s: invalid URL length", __func__); + return; + } + ext = url.substr(url.length() - 4, 4); + std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); + + if ((mux != nullptr) && (url.substr(0, 7) == "rtsp://")) { +#ifdef BUILD_LIBMUX + StreamDemuxerMux *demuxer; + demuxer = new StreamDemuxerMux( + mSession, mSession, mSession, this, listener, url, mux); + if (demuxer == nullptr) { + ULOGE("%s: failed to create the stream demuxer", + __func__); + return; + } + mDemuxer = demuxer; +#else /* BUILD_LIBMUX */ + ULOGE("%s: libmux is not supported", __func__); +#endif /* BUILD_LIBMUX */ + } else if (url.substr(0, 7) == "rtsp://") { + StreamDemuxerNet *demuxer; + demuxer = new StreamDemuxerNet( + mSession, mSession, mSession, this, listener, url); + if (demuxer == nullptr) { + ULOGE("%s: failed to create the stream demuxer", + __func__); + return; + } + mDemuxer = demuxer; + } else if (ext == ".mp4") { + RecordDemuxer *demuxer; + demuxer = new RecordDemuxer( + mSession, mSession, mSession, this, listener, url); + if (demuxer == nullptr) { + ULOGE("%s: failed to create the record demuxer", + __func__); + return; + } + mDemuxer = demuxer; + } else { + ULOGE("%s: unsupported URL", __func__); + } +} + + +Session::Demuxer::Demuxer(Session *session, + const std::string &localAddr, + uint16_t localStreamPort, + uint16_t localControlPort, + const std::string &remoteAddr, + uint16_t remoteStreamPort, + uint16_t remoteControlPort, + IPdraw::IDemuxer::Listener *listener) : + mSession(session), + mDemuxer(nullptr) +{ + StreamDemuxerNet *demuxer; + demuxer = new StreamDemuxerNet(mSession, + mSession, + mSession, + this, + listener, + localAddr, + localStreamPort, + localControlPort, + remoteAddr, + remoteStreamPort, + remoteControlPort); + if (demuxer == nullptr) { + ULOGE("%s: failed to create the stream demuxer", __func__); + return; + } + mDemuxer = demuxer; +} + + +Session::Demuxer::~Demuxer(void) +{ + if (mDemuxer == nullptr) + return; + + int res = mDemuxer->stop(); + if (res < 0) + ULOG_ERRNO("Demuxer::stop", -res); +} + + +int Session::Demuxer::close(void) +{ + int res; + + if (mDemuxer == nullptr) + return -EPROTO; + + res = mDemuxer->stop(); + if (res < 0) { + ULOG_ERRNO("Demuxer::stop", -res); + return res; + } + + /* Waiting for the asynchronous stop; closeResponse() + * will be called when it's done */ + mDemuxer = nullptr; + return 0; +} + + +uint16_t Session::Demuxer::getSingleStreamLocalStreamPort(void) +{ + if (mDemuxer == nullptr) + return 0; + + StreamDemuxerNet *demuxer = dynamic_cast(mDemuxer); + if (demuxer == nullptr) { + ULOGE("%s: invalid demuxer", __func__); + return 0; + } + + return demuxer->getSingleStreamLocalStreamPort(); +} + + +uint16_t Session::Demuxer::getSingleStreamLocalControlPort(void) +{ + if (mDemuxer == nullptr) + return 0; + + StreamDemuxerNet *demuxer = dynamic_cast(mDemuxer); + if (demuxer == nullptr) { + ULOGE("%s: invalid demuxer", __func__); + return 0; + } + + return demuxer->getSingleStreamLocalControlPort(); +} + + +bool Session::Demuxer::isReadyToPlay(void) +{ + if (mDemuxer == nullptr) + return false; + + return mDemuxer->isReadyToPlay(); +} + + +bool Session::Demuxer::isPaused(void) +{ + if (mDemuxer == nullptr) + return false; + + return mDemuxer->isPaused(); +} + + +int Session::Demuxer::play(float speed) +{ + if (mDemuxer == nullptr) + return -EPROTO; + + return mDemuxer->play(speed); +} + + +int Session::Demuxer::pause(void) +{ + return play(0.); +} + + +int Session::Demuxer::previousFrame(void) +{ + if (mDemuxer == nullptr) + return -EPROTO; + return mDemuxer->previous(); +} + + +int Session::Demuxer::nextFrame(void) +{ + if (mDemuxer == nullptr) + return -EPROTO; + return mDemuxer->next(); +} + + +int Session::Demuxer::seek(int64_t delta, bool exact) +{ + if (mDemuxer == nullptr) + return -EPROTO; + + return mDemuxer->seek(delta, exact); +} + + +int Session::Demuxer::seekForward(uint64_t delta, bool exact) +{ + return seek((int64_t)delta); +} + + +int Session::Demuxer::seekBack(uint64_t delta, bool exact) +{ + return seek(-((int64_t)delta)); +} + + +int Session::Demuxer::seekTo(uint64_t timestamp, bool exact) +{ + if (mDemuxer == nullptr) + return -EPROTO; + + return mDemuxer->seekTo(timestamp, exact); +} + + +uint64_t Session::Demuxer::getDuration(void) +{ + if (mDemuxer == nullptr) + return 0; + + return mDemuxer->getDuration(); +} + + +uint64_t Session::Demuxer::getCurrentTime(void) +{ + if (mDemuxer == nullptr) + return 0; + + return mDemuxer->getCurrentTime(); +} + + +int Session::createMuxer(const std::string &url, IPdraw::IMuxer **retObj) +{ + ULOG_ERRNO_RETURN_ERR_IF(url.length() == 0, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(retObj == nullptr, EINVAL); + +#if MUXER_TEST + int res; + Session::Muxer *muxer = nullptr; + + pthread_mutex_lock(&mMutex); + if (mState == STOPPING || mState == STOPPED) { + ULOGE("muxer creation refused in %s state", stateStr(mState)); + pthread_mutex_unlock(&mMutex); + return -EPROTO; + } + + muxer = new Session::Muxer(this, url); + if (muxer == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOGE("%s: failed to create the muxer", __func__); + return -ENOMEM; + } + + mElements.push_back(muxer->getElement()); + pthread_mutex_unlock(&mMutex); + + res = muxer->getElement()->start(); + if (res < 0) { + ULOG_ERRNO("muxer->start", -res); + goto error; + } + + *retObj = muxer; + + return 0; + +error: + if (muxer != nullptr) { + pthread_mutex_lock(&mMutex); + std::vector::iterator e = mElements.begin(); + while (e != mElements.end()) { + if (*e != muxer->getElement()) { + e++; + continue; + } + mElements.erase(e); + break; + } + pthread_mutex_unlock(&mMutex); + delete muxer; + } + return res; + +#else + /* Not supported yet */ + return -ENOSYS; +#endif +} + + +Session::Muxer::Muxer(Session *session, const std::string &url) +{ + std::string ext; + + mSession = session; + mMuxer = nullptr; + + if (url.length() < 4) { + ULOGE("%s: invalid URL length", __func__); + return; + } + ext = url.substr(url.length() - 4, 4); + std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); + + if (url.substr(0, 7) == "rtmp://") { +#ifdef BUILD_LIBRTMP + RtmpStreamMuxer *muxer; + muxer = new Pdraw::RtmpStreamMuxer(session, session, url); + if (muxer == nullptr) { + ULOGE("%s: failed to create the RTMP stream muxer", + __func__); + return; + } + mMuxer = muxer; +#else + ULOGE("%s: librtmp is not supported", __func__); +#endif + } else if (ext == ".mp4") { + RecordMuxer *muxer; + muxer = new Pdraw::RecordMuxer(session, session, url); + if (muxer == nullptr) { + ULOGE("%s: failed to create the record muxer", + __func__); + return; + } + mMuxer = muxer; + } else { + ULOGE("%s: unsupported URL", __func__); + } +} + + +Session::Muxer::~Muxer(void) +{ + if (mMuxer == nullptr) + return; + + int res = mMuxer->stop(); + if (res < 0) + ULOG_ERRNO("Muxer::stop", -res); +} + + +int Session::Muxer::addMedia( + unsigned int mediaId, + const struct pdraw_muxer_video_media_params *params) +{ + int res = 0; + CodedSource *source = nullptr; + CodedVideoMedia *media = nullptr; + CodedChannel *channel = nullptr; + bool found = false; + + if (mMuxer == nullptr) + return -EPROTO; + + pthread_mutex_lock(&mSession->mMutex); + + std::vector::iterator e = mSession->mElements.begin(); + while (e != mSession->mElements.end()) { + source = dynamic_cast(*e); + if (source == nullptr) { + e++; + continue; + } + unsigned int mediaCount = source->getOutputMediaCount(); + for (unsigned int i = 0; i < mediaCount; i++) { + media = source->getOutputMedia(i); + if ((media != nullptr) && (media->id == mediaId)) { + found = true; + break; + } + } + if (found) + break; + e++; + } + if ((!found) || (source == nullptr) || (media == nullptr)) { + pthread_mutex_unlock(&mSession->mMutex); + return -ENOENT; + } + + res = mMuxer->addInputMedia(media); + if (res < 0) { + ULOG_ERRNO("RecordMuxer::addInputMedia", -res); + goto out; + } + channel = mMuxer->getInputChannel(media); + if (channel == nullptr) { + ULOGE("failed to get muxer input channel"); + res = -EPROTO; + goto out; + } + res = source->addOutputChannel(media, channel); + if (res < 0) { + ULOG_ERRNO("Source::addOutputChannel", -res); + goto out; + } + +out: + pthread_mutex_unlock(&mSession->mMutex); + return res; +} + + +Session::CodedVideoSink::CodedVideoSink( + Session *session, + const struct pdraw_video_sink_params *params, + IPdraw::ICodedVideoSink::Listener *listener) +{ + mSink = new Pdraw::ExternalCodedVideoSink( + session, + ¶ms->required_coded_format, + session, + listener, + this, + params); + if (mSink == nullptr) { + ULOGE("%s: failed to create the video sink", __func__); + return; + } +} + + +Session::CodedVideoSink::~CodedVideoSink(void) +{ + if (mSink == nullptr) + return; + int ret = mSink->stop(); + if (ret < 0) + ULOG_ERRNO("sink->stop", -ret); +} + + +int Session::CodedVideoSink::resync(void) +{ + if (mSink == nullptr) + return -EPROTO; + return mSink->resync(); +} + + +struct mbuf_coded_video_frame_queue *Session::CodedVideoSink::getQueue(void) +{ + if (mSink == nullptr) + return nullptr; + return mSink->getQueue(); +} + + +int Session::CodedVideoSink::queueFlushed(void) +{ + if (mSink == nullptr) + return -EPROTO; + return mSink->flushDone(); +} + + +Session::RawVideoSink::RawVideoSink( + Session *session, + const struct pdraw_video_sink_params *params, + IPdraw::IRawVideoSink::Listener *listener) +{ + mSink = new Pdraw::ExternalRawVideoSink( + session, session, listener, this, params); + if (mSink == nullptr) { + ULOGE("%s: failed to create the video sink", __func__); + return; + } +} + + +Session::RawVideoSink::~RawVideoSink(void) +{ + if (mSink == nullptr) + return; + int ret = mSink->stop(); + if (ret < 0) + ULOG_ERRNO("sink->stop", -ret); +} + + +struct mbuf_raw_video_frame_queue *Session::RawVideoSink::getQueue(void) +{ + if (mSink == nullptr) + return nullptr; + return mSink->getQueue(); +} + + +int Session::RawVideoSink::queueFlushed(void) +{ + if (mSink == nullptr) + return -EPROTO; + return mSink->flushDone(); +} + + +int Session::internalCreateCodedVideoSink( + CodedSource *source, + CodedVideoMedia *media, + const struct pdraw_video_sink_params *params, + IPdraw::ICodedVideoSink::Listener *listener, + IPdraw::ICodedVideoSink **retObj) +{ + /* Note: mMutex is held while this function is called */ + int res; + Session::CodedVideoSink *sink = nullptr; + CodedChannel *channel = nullptr; + + sink = new CodedVideoSink(this, params, listener); + if (sink == nullptr) { + ULOGE("coded video sink creation failed"); + return -ENOMEM; + } + + mElements.push_back(sink->getElement()); + + res = sink->getSink()->addInputMedia(media); + if (res < 0) { + ULOG_ERRNO("codedVideoSink->addInputMedia", -res); + goto error; + } + + res = sink->getElement()->start(); + if (res < 0) { + ULOG_ERRNO("codedVideoSink->start", -res); + goto error; + } + + channel = sink->getSink()->getInputChannel(media); + if (channel == nullptr) { + ULOGE("failed to get coded video sink input channel"); + res = -EPROTO; + goto error; + } + + res = source->addOutputChannel(media, channel); + if (res < 0) { + ULOG_ERRNO("source->addOutputChannel", -res); + goto error; + } + + /* Force a resync after linking the elements; this allows a coded + * video sink to start on an IDR frame for example */ + res = sink->getCodedVideoSink()->resync(); + if (res < 0) { + ULOG_ERRNO("codedVideoSink->resync", -res); + goto error; + } + + *retObj = sink; + + return 0; + +error: + if (sink != nullptr) { + if (channel != nullptr) { + /* removeOutputChannel must be called without mMutex + * being held, so release it here */ + pthread_mutex_unlock(&mMutex); + source->removeOutputChannel(media, channel->getKey()); + pthread_mutex_lock(&mMutex); + } + std::vector::iterator e = mElements.begin(); + while (e != mElements.end()) { + if (*e != sink->getElement()) { + e++; + continue; + } + mElements.erase(e); + break; + } + delete sink; + } + return res; +} + + +int Session::internalCreateRawVideoSink( + RawSource *source, + RawVideoMedia *media, + const struct pdraw_video_sink_params *params, + IPdraw::IRawVideoSink::Listener *listener, + IPdraw::IRawVideoSink **retObj) +{ + /* Note: mMutex is held while this function is called */ + int res; + Session::RawVideoSink *sink = nullptr; + RawChannel *channel = nullptr; + + sink = new RawVideoSink(this, params, listener); + if (sink == nullptr) { + ULOGE("raw video sink creation failed"); + return -ENOMEM; + } + + mElements.push_back(sink->getElement()); + + res = sink->getSink()->addInputMedia(media); + if (res < 0) { + ULOG_ERRNO("rawVideoSink->addInputMedia", -res); + goto error; + } + + res = sink->getElement()->start(); + if (res < 0) { + ULOG_ERRNO("rawVideoSink->start", -res); + goto error; + } + + channel = sink->getSink()->getInputChannel(media); + if (channel == nullptr) { + ULOGE("failed to get raw video sink input channel"); + res = -EPROTO; + goto error; + } + + res = source->addOutputChannel(media, channel); + if (res < 0) { + ULOG_ERRNO("source->addOutputChannel", -res); + goto error; + } + + *retObj = sink; + + return 0; + +error: + if (sink != nullptr) { + if (channel != nullptr) { + /* removeOutputChannel must be called without mMutex + * being held, so release it here */ + pthread_mutex_unlock(&mMutex); + source->removeOutputChannel(media, channel->getKey()); + pthread_mutex_lock(&mMutex); + } + std::vector::iterator e = mElements.begin(); + while (e != mElements.end()) { + if (*e != sink->getElement()) { + e++; + continue; + } + mElements.erase(e); + break; + } + delete sink; + } + return res; +} + + +int Session::createCodedVideoSink(unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::ICodedVideoSink::Listener *listener, + IPdraw::ICodedVideoSink **retObj) +{ + int ret = 0; + bool found = false; + + if (params == nullptr) + return -EINVAL; + if (listener == nullptr) + return -EINVAL; + if (retObj == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + + std::vector::iterator e = mElements.begin(); + while (e != mElements.end()) { + CodedVideoMedia *media; + CodedSource *source = dynamic_cast(*e); + if (source == nullptr) { + e++; + continue; + } + unsigned int mediaCount = source->getOutputMediaCount(); + for (unsigned int i = 0; i < mediaCount; i++) { + media = source->getOutputMedia(i); + if ((media != nullptr) && (media->id == mediaId)) { + found = true; + break; + } + } + if (found) { + ret = internalCreateCodedVideoSink( + source, media, params, listener, retObj); + goto exit; + } + e++; + } + ret = -ENOENT; +exit: + pthread_mutex_unlock(&mMutex); + return ret; +} + + +int Session::createRawVideoSink(unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::IRawVideoSink::Listener *listener, + IPdraw::IRawVideoSink **retObj) +{ + int ret = 0; + bool found = false; + + if (params == nullptr) + return -EINVAL; + if (listener == nullptr) + return -EINVAL; + if (retObj == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + + std::vector::iterator e = mElements.begin(); + while (e != mElements.end()) { + RawVideoMedia *media; + RawSource *source = dynamic_cast(*e); + if (source == nullptr) { + e++; + continue; + } + unsigned int mediaCount = source->getOutputMediaCount(); + for (unsigned int i = 0; i < mediaCount; i++) { + media = source->getOutputMedia(i); + if ((media != nullptr) && (media->id == mediaId)) { + found = true; + break; + } + } + if (found) { + ret = internalCreateRawVideoSink( + source, media, params, listener, retObj); + goto exit; + } + e++; + } + ret = -ENOENT; +exit: + pthread_mutex_unlock(&mMutex); + return ret; +} + + +void Session::getFriendlyNameSetting(std::string *friendlyName) +{ + mSettings.getFriendlyName(friendlyName); +} + + +void Session::setFriendlyNameSetting(const std::string &friendlyName) +{ + mSettings.setFriendlyName(friendlyName); +} + + +void Session::getSerialNumberSetting(std::string *serialNumber) +{ + mSettings.getSerialNumber(serialNumber); +} + + +void Session::setSerialNumberSetting(const std::string &serialNumber) +{ + mSettings.setSerialNumber(serialNumber); +} + + +void Session::getSoftwareVersionSetting(std::string *softwareVersion) +{ + mSettings.getSoftwareVersion(softwareVersion); +} + + +void Session::setSoftwareVersionSetting(const std::string &softwareVersion) +{ + mSettings.setSoftwareVersion(softwareVersion); +} + + +enum pdraw_pipeline_mode Session::getPipelineModeSetting(void) +{ + return mSettings.getPipelineMode(); +} + + +void Session::setPipelineModeSetting(enum pdraw_pipeline_mode mode) +{ + if (mState != READY) { + ULOGE("%s: invalid state", __func__); + return; + } + + mSettings.setPipelineMode(mode); +} + + +void Session::getDisplayScreenSettings(float *xdpi, + float *ydpi, + float *deviceMarginTop, + float *deviceMarginBottom, + float *deviceMarginLeft, + float *deviceMarginRight) +{ + mSettings.getDisplayScreenSettings(xdpi, + ydpi, + deviceMarginTop, + deviceMarginBottom, + deviceMarginLeft, + deviceMarginRight); +} + + +void Session::setDisplayScreenSettings(float xdpi, + float ydpi, + float deviceMarginTop, + float deviceMarginBottom, + float deviceMarginLeft, + float deviceMarginRight) +{ + mSettings.setDisplayScreenSettings(xdpi, + ydpi, + deviceMarginTop, + deviceMarginBottom, + deviceMarginLeft, + deviceMarginRight); +} + + +enum pdraw_hmd_model Session::getHmdModelSetting(void) +{ + return mSettings.getHmdModelSetting(); +} + + +void Session::setHmdModelSetting(enum pdraw_hmd_model hmdModel) +{ + mSettings.setHmdModelSetting(hmdModel); +} + + +int Session::dumpPipeline(const std::string &fileName) +{ + return mFactory.dumpPipeline(fileName); +} + + +/* + * Internal methods + */ + +void Session::asyncElementStateChange(Element *element, Element::State state) +{ + pthread_mutex_lock(&mAsyncMutex); + mElementStateChangeElementArgs.push(element); + mElementStateChangeStateArgs.push(state); + pomp_loop_idle_add(mLoop, idleElementStateChange, this); + pthread_mutex_unlock(&mAsyncMutex); +} + + +int Session::addMediaToRenderer(unsigned int mediaId, Renderer *renderer) +{ + return mFactory.addMediaToRenderer(mediaId, renderer); +} + + +void Session::asyncElementDelete(Element *element) +{ + pthread_mutex_lock(&mAsyncMutex); + mElementDeleteElementArgs.push(element); + pomp_loop_idle_add(mLoop, idleElementDelete, this); + pthread_mutex_unlock(&mAsyncMutex); +} + + +void Session::setState(enum State state) +{ + pthread_mutex_lock(&mMutex); + if (state == mState) { + pthread_mutex_unlock(&mMutex); + return; + } + + mState = state; + pthread_mutex_unlock(&mMutex); + ULOGI("state change to %s", stateStr(state)); +} + + +void Session::socketCreated(int fd) +{ + if (mListener != nullptr) + mListener->onSocketCreated(this, fd); +} + + +/** + * Calls from idle functions + */ + +void Session::idleElementStateChange(void *userdata) +{ + Session *self = reinterpret_cast(userdata); + pthread_mutex_lock(&self->mAsyncMutex); + Element *element = self->mElementStateChangeElementArgs.front(); + Element::State state = self->mElementStateChangeStateArgs.front(); + self->mElementStateChangeElementArgs.pop(); + self->mElementStateChangeStateArgs.pop(); + pthread_mutex_unlock(&self->mAsyncMutex); + ULOG_ERRNO_RETURN_IF(element == nullptr, EINVAL); + self->onElementStateChanged(element, state); +} + + +void Session::idleElementDelete(void *userdata) +{ + Session *self = reinterpret_cast(userdata); + pthread_mutex_lock(&self->mAsyncMutex); + Element *element = self->mElementDeleteElementArgs.front(); + self->mElementDeleteElementArgs.pop(); + pthread_mutex_unlock(&self->mAsyncMutex); + ULOG_ERRNO_RETURN_IF(element == nullptr, EINVAL); + + pthread_mutex_lock(&self->mMutex); + std::vector::iterator e = self->mElements.begin(); + while (e != self->mElements.end()) { + if (*e != element) { + e++; + continue; + } + self->mElements.erase(e); + delete element; + break; + } + pthread_mutex_unlock(&self->mMutex); +} + + +void Session::idleRendererCompleteStop(void *userdata) +{ + Session *self = reinterpret_cast(userdata); + pthread_mutex_lock(&self->mAsyncMutex); + Renderer *renderer = self->mRendererCompleteStopRendererArgs.front(); + self->mRendererCompleteStopRendererArgs.pop(); + pthread_mutex_unlock(&self->mAsyncMutex); + ULOG_ERRNO_RETURN_IF(renderer == nullptr, EINVAL); + renderer->completeStop(); +} + + +void Session::callStopResponse(void *userdata) +{ + Session *self = reinterpret_cast(userdata); + pthread_mutex_lock(&self->mAsyncMutex); + int status = self->mStopRespStatusArgs.front(); + self->mStopRespStatusArgs.pop(); + pthread_mutex_unlock(&self->mAsyncMutex); + if (self->mListener == nullptr) + return; + self->mListener->stopResponse(self, status); +} + + +void Session::callOnMediaAdded(void *userdata) +{ + Session *self = reinterpret_cast(userdata); + pthread_mutex_lock(&self->mAsyncMutex); + struct pdraw_media_info info = self->mMediaAddedInfoArgs.front(); + self->mMediaAddedInfoArgs.pop(); + pthread_mutex_unlock(&self->mAsyncMutex); + self->mListener->onMediaAdded(self, &info); + Media::cleanupMediaInfo(&info); +} + + +void Session::callOnMediaRemoved(void *userdata) +{ + Session *self = reinterpret_cast(userdata); + pthread_mutex_lock(&self->mAsyncMutex); + struct pdraw_media_info info = self->mMediaRemovedInfoArgs.front(); + self->mMediaRemovedInfoArgs.pop(); + pthread_mutex_unlock(&self->mAsyncMutex); + self->mListener->onMediaRemoved(self, &info); + Media::cleanupMediaInfo(&info); +} + + +/* Must be called on the loop thread */ +void Session::onElementStateChanged(Element *element, Element::State state) +{ + mFactory.onElementStateChanged(element, state); + + if (state == Element::State::STOPPED) { + bool stopped = true; + State curState; + + pthread_mutex_lock(&mMutex); + + curState = mState; + + std::vector::iterator e = mElements.begin(); + while (e != mElements.end()) { + if ((*e)->getState() != Element::State::STOPPED) { + stopped = false; + break; + } + e++; + } + + pthread_mutex_unlock(&mMutex); + + asyncElementDelete(element); + + if (stopped && curState == STOPPING) { + setState(STOPPED); + + if (mListener != nullptr) + stopResp(0); + } + } +} + + +/* Must be called on the loop thread */ +void Session::onOutputMediaAdded(CodedSource *source, CodedVideoMedia *media) +{ + ULOGD("onOutputMediaAdded name=%s", media->getName().c_str()); + + mFactory.onOutputMediaAdded(source, media); + + if (mListener != nullptr) { + struct pdraw_media_info info; + media->fillMediaInfo(&info); + if (pthread_self() == mLoopThread) { + mListener->onMediaAdded(this, &info); + Media::cleanupMediaInfo(&info); + } else { + pthread_mutex_lock(&mAsyncMutex); + mMediaAddedInfoArgs.push(info); + pomp_loop_idle_add(mLoop, callOnMediaAdded, this); + pthread_mutex_unlock(&mAsyncMutex); + } + } +} + + +/* Must be called on the loop thread */ +void Session::onOutputMediaRemoved(CodedSource *source, CodedVideoMedia *media) +{ + ULOGD("onOutputMediaRemoved(coded) name=%s", media->getName().c_str()); + + mFactory.onOutputMediaRemoved(source, media); + + if (mListener != nullptr) { + struct pdraw_media_info info; + media->fillMediaInfo(&info); + pthread_mutex_lock(&mAsyncMutex); + mMediaRemovedInfoArgs.push(info); + pomp_loop_idle_add(mLoop, callOnMediaRemoved, this); + pthread_mutex_unlock(&mAsyncMutex); + } +} + + +/* Must be called on the loop thread */ +void Session::onOutputMediaAdded(RawSource *source, RawVideoMedia *media) +{ + ULOGD("onOutputMediaAdded(raw) name=%s", media->getName().c_str()); + + mFactory.onOutputMediaAdded(source, media); + + if (mListener != nullptr) { + struct pdraw_media_info info; + media->fillMediaInfo(&info); + if (pthread_self() == mLoopThread) { + mListener->onMediaAdded(this, &info); + Media::cleanupMediaInfo(&info); + } else { + pthread_mutex_lock(&mAsyncMutex); + mMediaAddedInfoArgs.push(info); + pomp_loop_idle_add(mLoop, callOnMediaAdded, this); + pthread_mutex_unlock(&mAsyncMutex); + } + } +} + + +/* Must be called on the loop thread */ +void Session::onOutputMediaRemoved(RawSource *source, RawVideoMedia *media) +{ + ULOGD("onOutputMediaRemoved(raw) name=%s", media->getName().c_str()); + + mFactory.onOutputMediaRemoved(source, media); + + if (mListener != nullptr) { + struct pdraw_media_info info; + media->fillMediaInfo(&info); + pthread_mutex_lock(&mAsyncMutex); + mMediaRemovedInfoArgs.push(info); + pomp_loop_idle_add(mLoop, callOnMediaRemoved, this); + pthread_mutex_unlock(&mAsyncMutex); + } +} + + +void Session::stopResp(int status) +{ + pthread_mutex_lock(&mAsyncMutex); + mStopRespStatusArgs.push(status); + pomp_loop_idle_add(mLoop, callStopResponse, this); + pthread_mutex_unlock(&mAsyncMutex); +} + + +const char *Session::stateStr(enum State val) +{ + switch (val) { + case State::STOPPED: + return "STOPPED"; + case State::READY: + return "READY"; + case State::STOPPING: + return "STOPPING"; + default: + return nullptr; + } +} + + +Session::PipelineFactory::PipelineFactory(Session *session) : mSession(session) +{ + return; +} + + +Session::PipelineFactory::~PipelineFactory(void) +{ + return; +} + + +void Session::PipelineFactory::onElementStateChanged(Element *element, + Element::State state) +{ + if (state == Element::State::STARTED) { + Renderer *r = dynamic_cast(element); + if (r != nullptr) { + int ret = addAllMediaToRenderer(r); + if (ret < 0) + ULOG_ERRNO("addAllMediaToRenderer", -ret); + } + } +} + + +void Session::PipelineFactory::onOutputMediaAdded(CodedSource *source, + CodedVideoMedia *media) +{ + Pdraw::Demuxer *demuxer = dynamic_cast(source); + if (demuxer != nullptr) { + if (mSession->mSettings.getPipelineMode() == + PDRAW_PIPELINE_MODE_DECODE_ALL) { + int ret = addDecoderForMedia(source, media); + if (ret < 0) + ULOG_ERRNO("addDecoderForMedia", -ret); + } + } +} + + +void Session::PipelineFactory::onOutputMediaAdded(RawSource *source, + RawVideoMedia *media) +{ + VideoDecoder *decoder = dynamic_cast(source); + if ((decoder != nullptr)) { + int ret = addMediaToAllRenderers(source, media); + if (ret < 0) + ULOG_ERRNO("addMediaToAllRenderers", -ret); + } +} + + +void Session::PipelineFactory::onOutputMediaRemoved(CodedSource *source, + CodedVideoMedia *media) +{ + return; +} + + +void Session::PipelineFactory::onOutputMediaRemoved(RawSource *source, + RawVideoMedia *media) +{ + return; +} + + +int Session::PipelineFactory::dumpPipeline(const std::string &fileName) +{ + int ret; + FILE *f; + + f = fopen(fileName.c_str(), "w"); + if (f == nullptr) { + ret = -errno; + ULOG_ERRNO("fopen", -ret); + return ret; + } + + fprintf(f, "digraph {\n"); + fprintf(f, "\tnode [margin=0.2,fontsize=12];\n"); + + pthread_mutex_lock(&mSession->mMutex); + + /* First pass: list the elements with their sink and source medias */ + std::vector::iterator e = mSession->mElements.begin(); + while (e != mSession->mElements.end()) { + unsigned int elmId = (*e)->getId(); + const char *elmName = (*e)->getName().c_str(); + fprintf(f, "\te%u [shape=record,label=\"", elmId); + + /* Element input medias */ + CodedSink *csink = dynamic_cast(*e); + if (csink != nullptr) { + unsigned int count = csink->getInputMediaCount(); + if (count > 0) + fprintf(f, "{ "); + for (unsigned int i = 0; i < count; i++) { + CodedVideoMedia *media = + csink->getInputMedia(i); + if (media == nullptr) + continue; + fprintf(f, + "%s %s", + (i > 0) ? " | " : "", + elmId, + media->id, + media->getName().c_str()); + } + if (count > 0) + fprintf(f, " } | "); + } + RawSink *rsink = dynamic_cast(*e); + if (rsink != nullptr) { + unsigned int count = rsink->getInputMediaCount(); + if (count > 0) + fprintf(f, "{ "); + for (unsigned int i = 0; i < count; i++) { + RawVideoMedia *media = rsink->getInputMedia(i); + if (media == nullptr) + continue; + fprintf(f, + "%s %s", + (i > 0) ? " | " : "", + elmId, + media->id, + media->getName().c_str()); + } + if (count > 0) + fprintf(f, " } | "); + } + + /* Element name */ + fprintf(f, " %s", elmId, elmName); + + /* Element output medias */ + CodedSource *csource = dynamic_cast(*e); + if (csource != nullptr) { + unsigned int count = csource->getOutputMediaCount(); + if (count > 0) + fprintf(f, " | { "); + for (unsigned int i = 0; i < count; i++) { + CodedVideoMedia *media = + csource->getOutputMedia(i); + if (media == nullptr) + continue; + fprintf(f, + "%s %s", + (i > 0) ? " | " : "", + elmId, + media->id, + media->getName().c_str()); + } + if (count > 0) + fprintf(f, " }"); + } + RawSource *rsource = dynamic_cast(*e); + if (rsource != nullptr) { + unsigned int count = rsource->getOutputMediaCount(); + if (count > 0) + fprintf(f, " | { "); + for (unsigned int i = 0; i < count; i++) { + RawVideoMedia *media = + rsource->getOutputMedia(i); + if (media == nullptr) + continue; + fprintf(f, + "%s %s", + (i > 0) ? " | " : "", + elmId, + media->id, + media->getName().c_str()); + } + if (count > 0) + fprintf(f, " }"); + } + + fprintf(f, "\"];\n"); + e++; + } + + /* Second pass: list the links between sources and sinks */ + e = mSession->mElements.begin(); + while (e != mSession->mElements.end()) { + unsigned int dstElmId = (*e)->getId(); + + /* Element input medias */ + CodedSink *csink = dynamic_cast(*e); + if (csink != nullptr) { + unsigned int count = csink->getInputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + CodedVideoMedia *media = + csink->getInputMedia(i); + std::vector::iterator e2 = + mSession->mElements.begin(); + while (e2 != mSession->mElements.end()) { + CodedSource *source = + dynamic_cast( + *e2); + if (source != nullptr) { + if (source->findOutputMedia( + media) == nullptr) { + e2++; + continue; + } + unsigned int srcElmId = + (*e2)->getId(); + fprintf(f, + "\te%u:e%um%u -> " + "e%u:e%um%u;\n", + srcElmId, + srcElmId, + media->id, + dstElmId, + dstElmId, + media->id); + break; + } + e2++; + } + } + } + RawSink *rsink = dynamic_cast(*e); + if (rsink != nullptr) { + unsigned int count = rsink->getInputMediaCount(); + for (unsigned int i = 0; i < count; i++) { + RawVideoMedia *media = rsink->getInputMedia(i); + std::vector::iterator e2 = + mSession->mElements.begin(); + while (e2 != mSession->mElements.end()) { + RawSource *source = + dynamic_cast(*e2); + if (source != nullptr) { + if (source->findOutputMedia( + media) == nullptr) { + e2++; + continue; + } + unsigned int srcElmId = + (*e2)->getId(); + fprintf(f, + "\te%u:e%um%u -> " + "e%u:e%um%u;\n", + srcElmId, + srcElmId, + media->id, + dstElmId, + dstElmId, + media->id); + break; + } + e2++; + } + } + } + + e++; + } + + pthread_mutex_unlock(&mSession->mMutex); + + fprintf(f, "}"); + fclose(f); + + ULOGI("pipeline dumped to file '%s'", fileName.c_str()); + + return 0; +} + + +int Session::PipelineFactory::addDecoderForMedia(CodedSource *source, + CodedVideoMedia *media) +{ + int ret; + + VideoDecoder *decoder = new VideoDecoder(mSession, mSession, mSession); + if (decoder == nullptr) { + ULOGE("decoder creation failed"); + return -ENOMEM; + } + ret = decoder->addInputMedia(media); + if (ret < 0) { + if (ret == -ENOSYS) + ret = 0; + else + ULOG_ERRNO("decoder->addInputMedia", -ret); + delete decoder; + return ret; + } + pthread_mutex_lock(&mSession->mMutex); + mSession->mElements.push_back(decoder); + pthread_mutex_unlock(&mSession->mMutex); + ret = decoder->start(); + if (ret < 0) { + ULOG_ERRNO("decoder->start", -ret); + return ret; + } + CodedChannel *channel = decoder->getInputChannel(media); + if (channel == nullptr) { + ULOGE("failed to get decoder input channel"); + return -EPROTO; + } + ret = source->addOutputChannel(media, channel); + if (ret < 0) { + ULOG_ERRNO("source->addOutputChannel", -ret); + return ret; + } + /* Force a resync after linking the elements; this allows a H.264 + * decoder to start on an IDR frame for example */ + decoder->resync(); + + return 0; +} + + +int Session::PipelineFactory::addEncoderForMedia(RawSource *source, + RawVideoMedia *media) +{ + int ret; + VideoEncoder *encoder = new VideoEncoder(mSession, mSession, mSession); + if (encoder == nullptr) { + ULOGE("encoder creation failed"); + return -ENOMEM; + } + + pthread_mutex_lock(&mSession->mMutex); + mSession->mElements.push_back(encoder); + pthread_mutex_unlock(&mSession->mMutex); + ret = encoder->addInputMedia(media); + if (ret < 0) { + ULOG_ERRNO("encoder->addInputMedia", -ret); + return ret; + } + ret = encoder->start(); + if (ret < 0) { + ULOG_ERRNO("encoder->start", -ret); + return ret; + } + RawChannel *channel = encoder->getInputChannel(media); + if (channel == nullptr) { + ULOGE("failed to get encoder input channel"); + return -EPROTO; + } + ret = source->addOutputChannel(media, channel); + if (ret < 0) { + ULOG_ERRNO("source->addOutputChannel", -ret); + return ret; + } + + return 0; +} + +int Session::PipelineFactory::addScalerForMedia(RawSource *source, + RawVideoMedia *media) +{ + int ret; + VideoScaler *scaler = new VideoScaler(mSession, mSession, mSession); + if (scaler == nullptr) { + ULOGE("scaler creation failed"); + return -ENOMEM; + } + + pthread_mutex_lock(&mSession->mMutex); + mSession->mElements.push_back(scaler); + pthread_mutex_unlock(&mSession->mMutex); + ret = scaler->addInputMedia(media); + if (ret < 0) { + ULOG_ERRNO("scaler->addInputMedia", -ret); + return ret; + } + ret = scaler->start(); + if (ret < 0) { + ULOG_ERRNO("scaler->start", -ret); + return ret; + } + RawChannel *channel = scaler->getInputChannel(media); + if (channel == nullptr) { + ULOGE("failed to get scaler input channel"); + return -EPROTO; + } + ret = source->addOutputChannel(media, channel); + if (ret < 0) { + ULOG_ERRNO("source->addOutputChannel", -ret); + return ret; + } + + return 0; +} + + +int Session::PipelineFactory::addMediaToRenderer(RawSource *source, + RawVideoMedia *media, + Renderer *renderer) +{ + int ret; + + ret = renderer->addInputMedia(media); + if ((ret == -EEXIST) || (ret == -EPERM)) { + return 0; + } else if (ret < 0) { + ULOG_ERRNO("renderer->addInputMedia", -ret); + return ret; + } + RawChannel *channel = renderer->getInputChannel(media); + if (channel == nullptr) { + ULOGE("failed to get renderer input channel"); + return -EPROTO; + } + ret = source->addOutputChannel(media, channel); + if (ret < 0) { + ULOG_ERRNO("source->addOutputChannel", -ret); + return ret; + } + return 0; +} + + +int Session::PipelineFactory::addMediaToRenderer(unsigned int mediaId, + Renderer *renderer) +{ + int ret; + + pthread_mutex_lock(&mSession->mMutex); + std::vector::iterator e = mSession->mElements.begin(); + while (e != mSession->mElements.end()) { + VideoDecoder *decoder = dynamic_cast(*e); + if (decoder == nullptr) { + e++; + continue; + } + RawVideoMedia *media = decoder->getOutputMedia(0); + if (media == nullptr) { + ULOGE("invalid media"); + e++; + continue; + } + if (media->id != mediaId) { + e++; + continue; + } + ret = addMediaToRenderer(decoder, media, renderer); + if (ret < 0) + ULOG_ERRNO("addMediaToRenderer", -ret); + break; + } + pthread_mutex_unlock(&mSession->mMutex); + + return 0; +} + + +int Session::PipelineFactory::addMediaToAllRenderers(RawSource *source, + RawVideoMedia *media) +{ + int ret = 0; + + pthread_mutex_lock(&mSession->mMutex); + std::vector::iterator e = mSession->mElements.begin(); + while (e != mSession->mElements.end() && ret == 0) { + Renderer *r = dynamic_cast(*e); + e++; + if (r == nullptr) + continue; + ret = addMediaToRenderer(source, media, r); + } + pthread_mutex_unlock(&mSession->mMutex); + + return ret; +} + + +int Session::PipelineFactory::addAllMediaToRenderer(Renderer *renderer) +{ + int ret; + + pthread_mutex_lock(&mSession->mMutex); + std::vector::iterator e = mSession->mElements.begin(); + while (e != mSession->mElements.end()) { + VideoDecoder *decoder = dynamic_cast(*e); + if (decoder == nullptr) { + e++; + continue; + } + RawVideoMedia *media = decoder->getOutputMedia(0); + if (media == nullptr) { + ULOGE("invalid media"); + e++; + continue; + } + ret = addMediaToRenderer(decoder, media, renderer); + if (ret < 0) + ULOG_ERRNO("addMediaToRenderer", -ret); + e++; + } + pthread_mutex_unlock(&mSession->mMutex); + + return 0; +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_session.hpp b/libpdraw/src/pdraw_session.hpp index 9dedb66..870cf33 100644 --- a/libpdraw/src/pdraw_session.hpp +++ b/libpdraw/src/pdraw_session.hpp @@ -1,518 +1,518 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Session - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_SESSION_HPP_ -#define _PDRAW_SESSION_HPP_ - -#include "pdraw_decoder_video.hpp" -#include "pdraw_demuxer.hpp" -#include "pdraw_external_coded_video_sink.hpp" -#include "pdraw_external_raw_video_sink.hpp" -#include "pdraw_media.hpp" -#include "pdraw_muxer.hpp" -#include "pdraw_renderer.hpp" -#include "pdraw_settings.hpp" - -#include - -#ifdef _WIN32 -# include -# undef OPAQUE -# undef near -# undef far -# define IPTOS_PREC_INTERNETCONTROL 0xc0 -# define IPTOS_PREC_FLASHOVERRIDE 0x80 -#else /* !_WIN32 */ -# include -# include -#endif /* !_WIN32 */ - -#include -#include -#include - -#include -#include -#include - -#ifdef _WIN32 -# define PIPE_BUF 4096 -#endif /* _WIN32 */ - -namespace Pdraw { - - -class Settings; - - -class Session : public IPdraw, - public Element::Listener, - public CodedSource::Listener, - public RawSource::Listener { -public: - enum State { - STOPPED = 0, - READY, - STOPPING, - }; - - - class PipelineFactory { - public: - PipelineFactory(Session *session); - - ~PipelineFactory(void); - - void onElementStateChanged(Element *element, - Element::State state); - - void onOutputMediaAdded(CodedSource *source, - CodedVideoMedia *media); - - void onOutputMediaAdded(RawSource *source, - RawVideoMedia *media); - - void onOutputMediaRemoved(CodedSource *source, - CodedVideoMedia *media); - - void onOutputMediaRemoved(RawSource *source, - RawVideoMedia *media); - - int dumpPipeline(const std::string &fileName); - - int addMediaToRenderer(unsigned int mediaId, - Renderer *renderer); - - private: - int addDecoderForMedia(CodedSource *source, - CodedVideoMedia *media); - - int addEncoderForMedia(RawSource *source, RawVideoMedia *media); - - int addScalerForMedia(RawSource *source, RawVideoMedia *media); - - int addMediaToRenderer(RawSource *source, - RawVideoMedia *media, - Renderer *renderer); - - int addMediaToAllRenderers(RawSource *source, - RawVideoMedia *media); - - int addAllMediaToRenderer(Renderer *renderer); - - Session *mSession; - }; - - - class Demuxer : public IPdraw::IDemuxer { - public: - Demuxer(Session *session, - const std::string &url, - struct mux_ctx *mux, - IPdraw::IDemuxer::Listener *listener); - - Demuxer(Session *session, - const std::string &localAddr, - uint16_t localStreamPort, - uint16_t localControlPort, - const std::string &remoteAddr, - uint16_t remoteStreamPort, - uint16_t remoteControlPort, - IPdraw::IDemuxer::Listener *listener); - - ~Demuxer(void); - - int close(void); - - uint16_t getSingleStreamLocalStreamPort(void); - - uint16_t getSingleStreamLocalControlPort(void); - - bool isReadyToPlay(void); - - bool isPaused(void); - - int play(float speed = 1.0f); - - int pause(void); - - int previousFrame(void); - - int nextFrame(void); - - int seek(int64_t delta, bool exact = false); - - int seekForward(uint64_t delta, bool exact = false); - - int seekBack(uint64_t delta, bool exact = false); - - int seekTo(uint64_t timestamp, bool exact = false); - - uint64_t getDuration(void); - - uint64_t getCurrentTime(void); - - Element *getElement() - { - return mDemuxer; - } - - Pdraw::Demuxer *getDemuxer() - { - return mDemuxer; - } - - private: - Session *mSession; - Pdraw::Demuxer *mDemuxer; - }; - - - class Muxer : public IPdraw::IMuxer { - public: - Muxer(Session *session, const std::string &url); - - ~Muxer(void); - - int - addMedia(unsigned int mediaId, - const struct pdraw_muxer_video_media_params *params); - - Element *getElement() - { - return mMuxer; - } - - Pdraw::Muxer *getMuxer() - { - return mMuxer; - } - - private: - Session *mSession; - Pdraw::Muxer *mMuxer; - }; - - - class VideoRenderer : public IPdraw::IVideoRenderer { - public: - /* Called on the rendering thread */ - VideoRenderer(Session *session, - unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - IPdraw::IVideoRenderer::Listener *listener, - struct egl_display *eglDisplay = nullptr); - - /* Called on the rendering thread */ - ~VideoRenderer(void); - - /* Called on the rendering thread */ - int resize(const struct pdraw_rect *renderPos); - - /* Called on the rendering thread */ - int setMediaId(unsigned int mediaId); - - /* Called on the rendering thread */ - unsigned int getMediaId(void); - - /* Called on the rendering thread */ - int setParams(const struct pdraw_video_renderer_params *params); - - /* Called on the rendering thread */ - int getParams(struct pdraw_video_renderer_params *params); - - /* Called on the rendering thread */ - int render(struct pdraw_rect *contentPos, - const float *viewMat = nullptr, - const float *projMat = nullptr); - - Element *getElement() - { - return mRenderer; - } - - private: - Pdraw::Renderer *mRenderer; - }; - - - class CodedVideoSink : public IPdraw::ICodedVideoSink { - public: - CodedVideoSink(Session *session, - const struct pdraw_video_sink_params *params, - IPdraw::ICodedVideoSink::Listener *listener); - - ~CodedVideoSink(void); - - int resync(void); - - struct mbuf_coded_video_frame_queue *getQueue(void); - - int queueFlushed(void); - - Element *getElement() - { - return mSink; - } - - CodedSink *getSink() - { - return mSink; - } - - Pdraw::ExternalCodedVideoSink *getCodedVideoSink() - { - return mSink; - } - - private: - Pdraw::ExternalCodedVideoSink *mSink; - }; - - - class RawVideoSink : public IPdraw::IRawVideoSink { - public: - RawVideoSink(Session *session, - const struct pdraw_video_sink_params *params, - IPdraw::IRawVideoSink::Listener *listener); - - ~RawVideoSink(void); - - int resync(void); - - struct mbuf_raw_video_frame_queue *getQueue(void); - - int queueFlushed(void); - - Element *getElement() - { - return mSink; - } - - RawSink *getSink() - { - return mSink; - } - - Pdraw::ExternalRawVideoSink *getRawVideoSink() - { - return mSink; - } - - private: - Pdraw::ExternalRawVideoSink *mSink; - }; - - - Session(struct pomp_loop *loop, IPdraw::Listener *listener); - - ~Session(void); - - - /* - * API methods - */ - - int stop(void); - - int createDemuxer(const std::string &url, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj); - - int createDemuxer(const std::string &localAddr, - uint16_t localStreamPort, - uint16_t localControlPort, - const std::string &remoteAddr, - uint16_t remoteStreamPort, - uint16_t remoteControlPort, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj); - - int createDemuxer(const std::string &url, - struct mux_ctx *mux, - IPdraw::IDemuxer::Listener *listener, - IPdraw::IDemuxer **retObj); - - int createMuxer(const std::string &url, IPdraw::IMuxer **retObj); - - /* Called on the rendering thread */ - int - createVideoRenderer(unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params, - IPdraw::IVideoRenderer::Listener *listener, - IPdraw::IVideoRenderer **retObj, - struct egl_display *eglDisplay = nullptr); - - int createCodedVideoSink(unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::ICodedVideoSink::Listener *listener, - IPdraw::ICodedVideoSink **retObj); - - int createRawVideoSink(unsigned int mediaId, - const struct pdraw_video_sink_params *params, - IPdraw::IRawVideoSink::Listener *listener, - IPdraw::IRawVideoSink **retObj); - - void getFriendlyNameSetting(std::string *friendlyName); - - void setFriendlyNameSetting(const std::string &friendlyName); - - void getSerialNumberSetting(std::string *serialNumber); - - void setSerialNumberSetting(const std::string &serialNumber); - - void getSoftwareVersionSetting(std::string *softwareVersion); - - void setSoftwareVersionSetting(const std::string &softwareVersion); - - enum pdraw_pipeline_mode getPipelineModeSetting(void); - - void setPipelineModeSetting(enum pdraw_pipeline_mode mode); - - void getDisplayScreenSettings(float *xdpi, - float *ydpi, - float *deviceMarginTop, - float *deviceMarginBottom, - float *deviceMarginLeft, - float *deviceMarginRight); - - void setDisplayScreenSettings(float xdpi, - float ydpi, - float deviceMarginTop, - float deviceMarginBottom, - float deviceMarginLeft, - float deviceMarginRight); - - enum pdraw_hmd_model getHmdModelSetting(void); - - void setHmdModelSetting(enum pdraw_hmd_model hmdModel); - - void *getAndroidJvm(void) - { - return mAndroidJvm; - } - - void setAndroidJvm(void *jvm) - { - mAndroidJvm = jvm; - } - - int dumpPipeline(const std::string &fileName); - - - /* - * Internal methods - */ - - Settings *getSettings(void) - { - return &mSettings; - } - - struct pomp_loop *getLoop() - { - return mLoop; - } - - int addMediaToRenderer(unsigned int mediaId, Renderer *renderer); - - void asyncElementDelete(Element *element); - - /* Called on the loop thread */ - void socketCreated(int fd); - -private: - int internalCreateCodedVideoSink( - CodedSource *source, - CodedVideoMedia *media, - const struct pdraw_video_sink_params *params, - ICodedVideoSink::Listener *listener, - ICodedVideoSink **retObj); - - int - internalCreateRawVideoSink(RawSource *source, - RawVideoMedia *media, - const struct pdraw_video_sink_params *params, - IRawVideoSink::Listener *listener, - IRawVideoSink **retObj); - - void setState(enum State state); - - static void *runLoopThread(void *ptr); - - void onElementStateChanged(Element *element, Element::State state); - - void asyncElementStateChange(Element *element, Element::State state); - - void onOutputMediaAdded(CodedSource *source, CodedVideoMedia *media); - - void onOutputMediaAdded(RawSource *source, RawVideoMedia *media); - - void onOutputMediaRemoved(CodedSource *source, CodedVideoMedia *media); - - void onOutputMediaRemoved(RawSource *source, RawVideoMedia *media); - - void stopResp(int status); - - static const char *stateStr(enum State val); - - PipelineFactory mFactory; - IPdraw::Listener *mListener; - enum State mState; - struct pomp_loop *mLoop; - pthread_t mLoopThread; - pthread_mutex_t mMutex; - Settings mSettings; - std::vector mElements; - void *mAndroidJvm; - - /* Calls from idle functions */ - pthread_mutex_t mAsyncMutex; - static void idleElementStateChange(void *userdata); - std::queue mElementStateChangeElementArgs; - std::queue mElementStateChangeStateArgs; - static void idleElementDelete(void *userdata); - std::queue mElementDeleteElementArgs; - static void idleRendererCompleteStop(void *userdata); - std::queue mRendererCompleteStopRendererArgs; - static void callStopResponse(void *userdata); - std::queue mStopRespStatusArgs; - static void callOnMediaAdded(void *userdata); - std::queue mMediaAddedInfoArgs; - static void callOnMediaRemoved(void *userdata); - std::queue mMediaRemovedInfoArgs; - /* callOnSocketCreated omitted. Function has to be synchronous */ -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_SESSION_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Session + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_SESSION_HPP_ +#define _PDRAW_SESSION_HPP_ + +#include "pdraw_decoder_video.hpp" +#include "pdraw_demuxer.hpp" +#include "pdraw_external_coded_video_sink.hpp" +#include "pdraw_external_raw_video_sink.hpp" +#include "pdraw_media.hpp" +#include "pdraw_muxer.hpp" +#include "pdraw_renderer.hpp" +#include "pdraw_settings.hpp" + +#include + +#ifdef _WIN32 +# include +# undef OPAQUE +# undef near +# undef far +# define IPTOS_PREC_INTERNETCONTROL 0xc0 +# define IPTOS_PREC_FLASHOVERRIDE 0x80 +#else /* !_WIN32 */ +# include +# include +#endif /* !_WIN32 */ + +#include +#include +#include + +#include +#include +#include + +#ifdef _WIN32 +# define PIPE_BUF 4096 +#endif /* _WIN32 */ + +namespace Pdraw { + + +class Settings; + + +class Session : public IPdraw, + public Element::Listener, + public CodedSource::Listener, + public RawSource::Listener { +public: + enum State { + STOPPED = 0, + READY, + STOPPING, + }; + + + class PipelineFactory { + public: + PipelineFactory(Session *session); + + ~PipelineFactory(void); + + void onElementStateChanged(Element *element, + Element::State state); + + void onOutputMediaAdded(CodedSource *source, + CodedVideoMedia *media); + + void onOutputMediaAdded(RawSource *source, + RawVideoMedia *media); + + void onOutputMediaRemoved(CodedSource *source, + CodedVideoMedia *media); + + void onOutputMediaRemoved(RawSource *source, + RawVideoMedia *media); + + int dumpPipeline(const std::string &fileName); + + int addMediaToRenderer(unsigned int mediaId, + Renderer *renderer); + + private: + int addDecoderForMedia(CodedSource *source, + CodedVideoMedia *media); + + int addEncoderForMedia(RawSource *source, RawVideoMedia *media); + + int addScalerForMedia(RawSource *source, RawVideoMedia *media); + + int addMediaToRenderer(RawSource *source, + RawVideoMedia *media, + Renderer *renderer); + + int addMediaToAllRenderers(RawSource *source, + RawVideoMedia *media); + + int addAllMediaToRenderer(Renderer *renderer); + + Session *mSession; + }; + + + class Demuxer : public IPdraw::IDemuxer { + public: + Demuxer(Session *session, + const std::string &url, + struct mux_ctx *mux, + IPdraw::IDemuxer::Listener *listener); + + Demuxer(Session *session, + const std::string &localAddr, + uint16_t localStreamPort, + uint16_t localControlPort, + const std::string &remoteAddr, + uint16_t remoteStreamPort, + uint16_t remoteControlPort, + IPdraw::IDemuxer::Listener *listener); + + ~Demuxer(void); + + int close(void); + + uint16_t getSingleStreamLocalStreamPort(void); + + uint16_t getSingleStreamLocalControlPort(void); + + bool isReadyToPlay(void); + + bool isPaused(void); + + int play(float speed = 1.0f); + + int pause(void); + + int previousFrame(void); + + int nextFrame(void); + + int seek(int64_t delta, bool exact = false); + + int seekForward(uint64_t delta, bool exact = false); + + int seekBack(uint64_t delta, bool exact = false); + + int seekTo(uint64_t timestamp, bool exact = false); + + uint64_t getDuration(void); + + uint64_t getCurrentTime(void); + + Element *getElement() + { + return mDemuxer; + } + + Pdraw::Demuxer *getDemuxer() + { + return mDemuxer; + } + + private: + Session *mSession; + Pdraw::Demuxer *mDemuxer; + }; + + + class Muxer : public IPdraw::IMuxer { + public: + Muxer(Session *session, const std::string &url); + + ~Muxer(void); + + int + addMedia(unsigned int mediaId, + const struct pdraw_muxer_video_media_params *params); + + Element *getElement() + { + return mMuxer; + } + + Pdraw::Muxer *getMuxer() + { + return mMuxer; + } + + private: + Session *mSession; + Pdraw::Muxer *mMuxer; + }; + + + class VideoRenderer : public IPdraw::IVideoRenderer { + public: + /* Called on the rendering thread */ + VideoRenderer(Session *session, + unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + IPdraw::IVideoRenderer::Listener *listener, + struct egl_display *eglDisplay = nullptr); + + /* Called on the rendering thread */ + ~VideoRenderer(void); + + /* Called on the rendering thread */ + int resize(const struct pdraw_rect *renderPos); + + /* Called on the rendering thread */ + int setMediaId(unsigned int mediaId); + + /* Called on the rendering thread */ + unsigned int getMediaId(void); + + /* Called on the rendering thread */ + int setParams(const struct pdraw_video_renderer_params *params); + + /* Called on the rendering thread */ + int getParams(struct pdraw_video_renderer_params *params); + + /* Called on the rendering thread */ + int render(struct pdraw_rect *contentPos, + const float *viewMat = nullptr, + const float *projMat = nullptr); + + Element *getElement() + { + return mRenderer; + } + + private: + Pdraw::Renderer *mRenderer; + }; + + + class CodedVideoSink : public IPdraw::ICodedVideoSink { + public: + CodedVideoSink(Session *session, + const struct pdraw_video_sink_params *params, + IPdraw::ICodedVideoSink::Listener *listener); + + ~CodedVideoSink(void); + + int resync(void); + + struct mbuf_coded_video_frame_queue *getQueue(void); + + int queueFlushed(void); + + Element *getElement() + { + return mSink; + } + + CodedSink *getSink() + { + return mSink; + } + + Pdraw::ExternalCodedVideoSink *getCodedVideoSink() + { + return mSink; + } + + private: + Pdraw::ExternalCodedVideoSink *mSink; + }; + + + class RawVideoSink : public IPdraw::IRawVideoSink { + public: + RawVideoSink(Session *session, + const struct pdraw_video_sink_params *params, + IPdraw::IRawVideoSink::Listener *listener); + + ~RawVideoSink(void); + + int resync(void); + + struct mbuf_raw_video_frame_queue *getQueue(void); + + int queueFlushed(void); + + Element *getElement() + { + return mSink; + } + + RawSink *getSink() + { + return mSink; + } + + Pdraw::ExternalRawVideoSink *getRawVideoSink() + { + return mSink; + } + + private: + Pdraw::ExternalRawVideoSink *mSink; + }; + + + Session(struct pomp_loop *loop, IPdraw::Listener *listener); + + ~Session(void); + + + /* + * API methods + */ + + int stop(void); + + int createDemuxer(const std::string &url, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj); + + int createDemuxer(const std::string &localAddr, + uint16_t localStreamPort, + uint16_t localControlPort, + const std::string &remoteAddr, + uint16_t remoteStreamPort, + uint16_t remoteControlPort, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj); + + int createDemuxer(const std::string &url, + struct mux_ctx *mux, + IPdraw::IDemuxer::Listener *listener, + IPdraw::IDemuxer **retObj); + + int createMuxer(const std::string &url, IPdraw::IMuxer **retObj); + + /* Called on the rendering thread */ + int + createVideoRenderer(unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params, + IPdraw::IVideoRenderer::Listener *listener, + IPdraw::IVideoRenderer **retObj, + struct egl_display *eglDisplay = nullptr); + + int createCodedVideoSink(unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::ICodedVideoSink::Listener *listener, + IPdraw::ICodedVideoSink **retObj); + + int createRawVideoSink(unsigned int mediaId, + const struct pdraw_video_sink_params *params, + IPdraw::IRawVideoSink::Listener *listener, + IPdraw::IRawVideoSink **retObj); + + void getFriendlyNameSetting(std::string *friendlyName); + + void setFriendlyNameSetting(const std::string &friendlyName); + + void getSerialNumberSetting(std::string *serialNumber); + + void setSerialNumberSetting(const std::string &serialNumber); + + void getSoftwareVersionSetting(std::string *softwareVersion); + + void setSoftwareVersionSetting(const std::string &softwareVersion); + + enum pdraw_pipeline_mode getPipelineModeSetting(void); + + void setPipelineModeSetting(enum pdraw_pipeline_mode mode); + + void getDisplayScreenSettings(float *xdpi, + float *ydpi, + float *deviceMarginTop, + float *deviceMarginBottom, + float *deviceMarginLeft, + float *deviceMarginRight); + + void setDisplayScreenSettings(float xdpi, + float ydpi, + float deviceMarginTop, + float deviceMarginBottom, + float deviceMarginLeft, + float deviceMarginRight); + + enum pdraw_hmd_model getHmdModelSetting(void); + + void setHmdModelSetting(enum pdraw_hmd_model hmdModel); + + void *getAndroidJvm(void) + { + return mAndroidJvm; + } + + void setAndroidJvm(void *jvm) + { + mAndroidJvm = jvm; + } + + int dumpPipeline(const std::string &fileName); + + + /* + * Internal methods + */ + + Settings *getSettings(void) + { + return &mSettings; + } + + struct pomp_loop *getLoop() + { + return mLoop; + } + + int addMediaToRenderer(unsigned int mediaId, Renderer *renderer); + + void asyncElementDelete(Element *element); + + /* Called on the loop thread */ + void socketCreated(int fd); + +private: + int internalCreateCodedVideoSink( + CodedSource *source, + CodedVideoMedia *media, + const struct pdraw_video_sink_params *params, + ICodedVideoSink::Listener *listener, + ICodedVideoSink **retObj); + + int + internalCreateRawVideoSink(RawSource *source, + RawVideoMedia *media, + const struct pdraw_video_sink_params *params, + IRawVideoSink::Listener *listener, + IRawVideoSink **retObj); + + void setState(enum State state); + + static void *runLoopThread(void *ptr); + + void onElementStateChanged(Element *element, Element::State state); + + void asyncElementStateChange(Element *element, Element::State state); + + void onOutputMediaAdded(CodedSource *source, CodedVideoMedia *media); + + void onOutputMediaAdded(RawSource *source, RawVideoMedia *media); + + void onOutputMediaRemoved(CodedSource *source, CodedVideoMedia *media); + + void onOutputMediaRemoved(RawSource *source, RawVideoMedia *media); + + void stopResp(int status); + + static const char *stateStr(enum State val); + + PipelineFactory mFactory; + IPdraw::Listener *mListener; + enum State mState; + struct pomp_loop *mLoop; + pthread_t mLoopThread; + pthread_mutex_t mMutex; + Settings mSettings; + std::vector mElements; + void *mAndroidJvm; + + /* Calls from idle functions */ + pthread_mutex_t mAsyncMutex; + static void idleElementStateChange(void *userdata); + std::queue mElementStateChangeElementArgs; + std::queue mElementStateChangeStateArgs; + static void idleElementDelete(void *userdata); + std::queue mElementDeleteElementArgs; + static void idleRendererCompleteStop(void *userdata); + std::queue mRendererCompleteStopRendererArgs; + static void callStopResponse(void *userdata); + std::queue mStopRespStatusArgs; + static void callOnMediaAdded(void *userdata); + std::queue mMediaAddedInfoArgs; + static void callOnMediaRemoved(void *userdata); + std::queue mMediaRemovedInfoArgs; + /* callOnSocketCreated omitted. Function has to be synchronous */ +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_SESSION_HPP_ */ diff --git a/libpdraw/src/pdraw_settings.cpp b/libpdraw/src/pdraw_settings.cpp index 582bf94..9bd365a 100644 --- a/libpdraw/src/pdraw_settings.cpp +++ b/libpdraw/src/pdraw_settings.cpp @@ -1,233 +1,233 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * User settings - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_settings -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_settings.hpp" - -namespace Pdraw { - - -Settings::Settings(void) -{ - int res; - pthread_mutexattr_t attr; - bool attr_created = false; - - mPipelineMode = PDRAW_PIPELINE_MODE_DECODE_ALL; - mDisplayXdpi = SETTINGS_DISPLAY_XDPI; - mDisplayYdpi = SETTINGS_DISPLAY_YDPI; - mDisplayDeviceMarginTop = SETTINGS_DISPLAY_DEVICE_MARGIN; - mDisplayDeviceMarginBottom = SETTINGS_DISPLAY_DEVICE_MARGIN; - mDisplayDeviceMarginLeft = SETTINGS_DISPLAY_DEVICE_MARGIN; - mDisplayDeviceMarginRight = SETTINGS_DISPLAY_DEVICE_MARGIN; - mHmdModel = PDRAW_HMD_MODEL_UNKNOWN; - - res = pthread_mutexattr_init(&attr); - if (res != 0) { - ULOG_ERRNO("pthread_mutexattr_init", res); - goto error; - } - attr_created = true; - - res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - if (res != 0) { - ULOG_ERRNO("pthread_mutexattr_settype", res); - goto error; - } - - res = pthread_mutex_init(&mMutex, &attr); - if (res != 0) { - ULOG_ERRNO("pthread_mutex_init", res); - goto error; - } - - pthread_mutexattr_destroy(&attr); - return; - -error: - if (attr_created) - pthread_mutexattr_destroy(&attr); -} - - -Settings::~Settings(void) -{ - pthread_mutex_destroy(&mMutex); -} - - -void Settings::lock(void) -{ - pthread_mutex_lock(&mMutex); -} - - -void Settings::unlock(void) -{ - pthread_mutex_unlock(&mMutex); -} - - -void Settings::getFriendlyName(std::string *friendlyName) -{ - if (friendlyName == nullptr) - return; - - pthread_mutex_lock(&mMutex); - *friendlyName = mFriendlyName; - pthread_mutex_unlock(&mMutex); -} - - -void Settings::setFriendlyName(const std::string &friendlyName) -{ - pthread_mutex_lock(&mMutex); - mFriendlyName = friendlyName; - pthread_mutex_unlock(&mMutex); -} - - -void Settings::getSerialNumber(std::string *serialNumber) -{ - if (serialNumber == nullptr) - return; - - pthread_mutex_lock(&mMutex); - *serialNumber = mSerialNumber; - pthread_mutex_unlock(&mMutex); -} - - -void Settings::setSerialNumber(const std::string &serialNumber) -{ - pthread_mutex_lock(&mMutex); - mSerialNumber = serialNumber; - pthread_mutex_unlock(&mMutex); -} - - -void Settings::getSoftwareVersion(std::string *softwareVersion) -{ - if (softwareVersion == nullptr) - return; - - pthread_mutex_lock(&mMutex); - *softwareVersion = mSoftwareVersion; - pthread_mutex_unlock(&mMutex); -} - - -void Settings::setSoftwareVersion(const std::string &softwareVersion) -{ - pthread_mutex_lock(&mMutex); - mSoftwareVersion = softwareVersion; - pthread_mutex_unlock(&mMutex); -} - - -enum pdraw_pipeline_mode Settings::getPipelineMode(void) -{ - pthread_mutex_lock(&mMutex); - enum pdraw_pipeline_mode ret = mPipelineMode; - pthread_mutex_unlock(&mMutex); - return ret; -} - - -void Settings::setPipelineMode(enum pdraw_pipeline_mode mode) -{ - pthread_mutex_lock(&mMutex); - mPipelineMode = mode; - pthread_mutex_unlock(&mMutex); -} - - -void Settings::getDisplayScreenSettings(float *xdpi, - float *ydpi, - float *deviceMarginTop, - float *deviceMarginBottom, - float *deviceMarginLeft, - float *deviceMarginRight) -{ - pthread_mutex_lock(&mMutex); - if (xdpi) - *xdpi = mDisplayXdpi; - if (ydpi) - *ydpi = mDisplayYdpi; - if (deviceMarginTop) - *deviceMarginTop = mDisplayDeviceMarginTop; - if (deviceMarginTop) - *deviceMarginBottom = mDisplayDeviceMarginBottom; - if (deviceMarginTop) - *deviceMarginLeft = mDisplayDeviceMarginLeft; - if (deviceMarginTop) - *deviceMarginRight = mDisplayDeviceMarginRight; - pthread_mutex_unlock(&mMutex); -} - - -void Settings::setDisplayScreenSettings(float xdpi, - float ydpi, - float deviceMarginTop, - float deviceMarginBottom, - float deviceMarginLeft, - float deviceMarginRight) -{ - pthread_mutex_lock(&mMutex); - mDisplayXdpi = xdpi; - mDisplayYdpi = ydpi; - mDisplayDeviceMarginTop = deviceMarginTop; - mDisplayDeviceMarginBottom = deviceMarginBottom; - mDisplayDeviceMarginLeft = deviceMarginLeft; - mDisplayDeviceMarginRight = deviceMarginRight; - pthread_mutex_unlock(&mMutex); -} - - -enum pdraw_hmd_model Settings::getHmdModelSetting(void) -{ - pthread_mutex_lock(&mMutex); - enum pdraw_hmd_model ret = mHmdModel; - pthread_mutex_unlock(&mMutex); - return ret; -} - - -void Settings::setHmdModelSetting(enum pdraw_hmd_model hmdModel) -{ - pthread_mutex_lock(&mMutex); - mHmdModel = hmdModel; - pthread_mutex_unlock(&mMutex); -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * User settings + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_settings +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_settings.hpp" + +namespace Pdraw { + + +Settings::Settings(void) +{ + int res; + pthread_mutexattr_t attr; + bool attr_created = false; + + mPipelineMode = PDRAW_PIPELINE_MODE_DECODE_ALL; + mDisplayXdpi = SETTINGS_DISPLAY_XDPI; + mDisplayYdpi = SETTINGS_DISPLAY_YDPI; + mDisplayDeviceMarginTop = SETTINGS_DISPLAY_DEVICE_MARGIN; + mDisplayDeviceMarginBottom = SETTINGS_DISPLAY_DEVICE_MARGIN; + mDisplayDeviceMarginLeft = SETTINGS_DISPLAY_DEVICE_MARGIN; + mDisplayDeviceMarginRight = SETTINGS_DISPLAY_DEVICE_MARGIN; + mHmdModel = PDRAW_HMD_MODEL_UNKNOWN; + + res = pthread_mutexattr_init(&attr); + if (res != 0) { + ULOG_ERRNO("pthread_mutexattr_init", res); + goto error; + } + attr_created = true; + + res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + if (res != 0) { + ULOG_ERRNO("pthread_mutexattr_settype", res); + goto error; + } + + res = pthread_mutex_init(&mMutex, &attr); + if (res != 0) { + ULOG_ERRNO("pthread_mutex_init", res); + goto error; + } + + pthread_mutexattr_destroy(&attr); + return; + +error: + if (attr_created) + pthread_mutexattr_destroy(&attr); +} + + +Settings::~Settings(void) +{ + pthread_mutex_destroy(&mMutex); +} + + +void Settings::lock(void) +{ + pthread_mutex_lock(&mMutex); +} + + +void Settings::unlock(void) +{ + pthread_mutex_unlock(&mMutex); +} + + +void Settings::getFriendlyName(std::string *friendlyName) +{ + if (friendlyName == nullptr) + return; + + pthread_mutex_lock(&mMutex); + *friendlyName = mFriendlyName; + pthread_mutex_unlock(&mMutex); +} + + +void Settings::setFriendlyName(const std::string &friendlyName) +{ + pthread_mutex_lock(&mMutex); + mFriendlyName = friendlyName; + pthread_mutex_unlock(&mMutex); +} + + +void Settings::getSerialNumber(std::string *serialNumber) +{ + if (serialNumber == nullptr) + return; + + pthread_mutex_lock(&mMutex); + *serialNumber = mSerialNumber; + pthread_mutex_unlock(&mMutex); +} + + +void Settings::setSerialNumber(const std::string &serialNumber) +{ + pthread_mutex_lock(&mMutex); + mSerialNumber = serialNumber; + pthread_mutex_unlock(&mMutex); +} + + +void Settings::getSoftwareVersion(std::string *softwareVersion) +{ + if (softwareVersion == nullptr) + return; + + pthread_mutex_lock(&mMutex); + *softwareVersion = mSoftwareVersion; + pthread_mutex_unlock(&mMutex); +} + + +void Settings::setSoftwareVersion(const std::string &softwareVersion) +{ + pthread_mutex_lock(&mMutex); + mSoftwareVersion = softwareVersion; + pthread_mutex_unlock(&mMutex); +} + + +enum pdraw_pipeline_mode Settings::getPipelineMode(void) +{ + pthread_mutex_lock(&mMutex); + enum pdraw_pipeline_mode ret = mPipelineMode; + pthread_mutex_unlock(&mMutex); + return ret; +} + + +void Settings::setPipelineMode(enum pdraw_pipeline_mode mode) +{ + pthread_mutex_lock(&mMutex); + mPipelineMode = mode; + pthread_mutex_unlock(&mMutex); +} + + +void Settings::getDisplayScreenSettings(float *xdpi, + float *ydpi, + float *deviceMarginTop, + float *deviceMarginBottom, + float *deviceMarginLeft, + float *deviceMarginRight) +{ + pthread_mutex_lock(&mMutex); + if (xdpi) + *xdpi = mDisplayXdpi; + if (ydpi) + *ydpi = mDisplayYdpi; + if (deviceMarginTop) + *deviceMarginTop = mDisplayDeviceMarginTop; + if (deviceMarginTop) + *deviceMarginBottom = mDisplayDeviceMarginBottom; + if (deviceMarginTop) + *deviceMarginLeft = mDisplayDeviceMarginLeft; + if (deviceMarginTop) + *deviceMarginRight = mDisplayDeviceMarginRight; + pthread_mutex_unlock(&mMutex); +} + + +void Settings::setDisplayScreenSettings(float xdpi, + float ydpi, + float deviceMarginTop, + float deviceMarginBottom, + float deviceMarginLeft, + float deviceMarginRight) +{ + pthread_mutex_lock(&mMutex); + mDisplayXdpi = xdpi; + mDisplayYdpi = ydpi; + mDisplayDeviceMarginTop = deviceMarginTop; + mDisplayDeviceMarginBottom = deviceMarginBottom; + mDisplayDeviceMarginLeft = deviceMarginLeft; + mDisplayDeviceMarginRight = deviceMarginRight; + pthread_mutex_unlock(&mMutex); +} + + +enum pdraw_hmd_model Settings::getHmdModelSetting(void) +{ + pthread_mutex_lock(&mMutex); + enum pdraw_hmd_model ret = mHmdModel; + pthread_mutex_unlock(&mMutex); + return ret; +} + + +void Settings::setHmdModelSetting(enum pdraw_hmd_model hmdModel) +{ + pthread_mutex_lock(&mMutex); + mHmdModel = hmdModel; + pthread_mutex_unlock(&mMutex); +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_settings.hpp b/libpdraw/src/pdraw_settings.hpp index 4197299..34f04a1 100644 --- a/libpdraw/src/pdraw_settings.hpp +++ b/libpdraw/src/pdraw_settings.hpp @@ -1,111 +1,111 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * User settings - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_SETTINGS_HPP_ -#define _PDRAW_SETTINGS_HPP_ - -#include -#include -#include - -#include - -#include - -namespace Pdraw { - - -#define SETTINGS_DISPLAY_XDPI (200.0f) -#define SETTINGS_DISPLAY_YDPI (200.0f) -#define SETTINGS_DISPLAY_DEVICE_MARGIN (0.0f) - - -class Settings { -public: - Settings(void); - - ~Settings(void); - - void lock(void); - - void unlock(void); - - void getFriendlyName(std::string *friendlyName); - - void setFriendlyName(const std::string &friendlyName); - - void getSerialNumber(std::string *serialNumber); - - void setSerialNumber(const std::string &serialNumber); - - void getSoftwareVersion(std::string *softwareVersion); - - void setSoftwareVersion(const std::string &softwareVersion); - - enum pdraw_pipeline_mode getPipelineMode(void); - - void setPipelineMode(enum pdraw_pipeline_mode mode); - - void getDisplayScreenSettings(float *xdpi, - float *ydpi, - float *deviceMarginTop, - float *deviceMarginBottom, - float *deviceMarginLeft, - float *deviceMarginRight); - - void setDisplayScreenSettings(float xdpi, - float ydpi, - float deviceMarginTop, - float deviceMarginBottom, - float deviceMarginLeft, - float deviceMarginRight); - - enum pdraw_hmd_model getHmdModelSetting(void); - - void setHmdModelSetting(enum pdraw_hmd_model hmdModel); - -private: - pthread_mutex_t mMutex; - std::string mFriendlyName; - std::string mSerialNumber; - std::string mSoftwareVersion; - enum pdraw_pipeline_mode mPipelineMode; - float mDisplayXdpi; - float mDisplayYdpi; - float mDisplayDeviceMarginTop; - float mDisplayDeviceMarginBottom; - float mDisplayDeviceMarginLeft; - float mDisplayDeviceMarginRight; - enum pdraw_hmd_model mHmdModel; -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_SETTINGS_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * User settings + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_SETTINGS_HPP_ +#define _PDRAW_SETTINGS_HPP_ + +#include +#include +#include + +#include + +#include + +namespace Pdraw { + + +#define SETTINGS_DISPLAY_XDPI (200.0f) +#define SETTINGS_DISPLAY_YDPI (200.0f) +#define SETTINGS_DISPLAY_DEVICE_MARGIN (0.0f) + + +class Settings { +public: + Settings(void); + + ~Settings(void); + + void lock(void); + + void unlock(void); + + void getFriendlyName(std::string *friendlyName); + + void setFriendlyName(const std::string &friendlyName); + + void getSerialNumber(std::string *serialNumber); + + void setSerialNumber(const std::string &serialNumber); + + void getSoftwareVersion(std::string *softwareVersion); + + void setSoftwareVersion(const std::string &softwareVersion); + + enum pdraw_pipeline_mode getPipelineMode(void); + + void setPipelineMode(enum pdraw_pipeline_mode mode); + + void getDisplayScreenSettings(float *xdpi, + float *ydpi, + float *deviceMarginTop, + float *deviceMarginBottom, + float *deviceMarginLeft, + float *deviceMarginRight); + + void setDisplayScreenSettings(float xdpi, + float ydpi, + float deviceMarginTop, + float deviceMarginBottom, + float deviceMarginLeft, + float deviceMarginRight); + + enum pdraw_hmd_model getHmdModelSetting(void); + + void setHmdModelSetting(enum pdraw_hmd_model hmdModel); + +private: + pthread_mutex_t mMutex; + std::string mFriendlyName; + std::string mSerialNumber; + std::string mSoftwareVersion; + enum pdraw_pipeline_mode mPipelineMode; + float mDisplayXdpi; + float mDisplayYdpi; + float mDisplayDeviceMarginTop; + float mDisplayDeviceMarginBottom; + float mDisplayDeviceMarginLeft; + float mDisplayDeviceMarginRight; + enum pdraw_hmd_model mHmdModel; +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_SETTINGS_HPP_ */ diff --git a/libpdraw/src/pdraw_sink_coded_video.cpp b/libpdraw/src/pdraw_sink_coded_video.cpp index 8dce68b..1db37e3 100644 --- a/libpdraw/src/pdraw_sink_coded_video.cpp +++ b/libpdraw/src/pdraw_sink_coded_video.cpp @@ -1,613 +1,613 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Pipeline sink element - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_sink_coded_video -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_sink_coded_video.hpp" - -#include - -namespace Pdraw { - - -CodedSink::CodedSink(unsigned int maxInputMedias, - const struct vdef_coded_format *codedVideoMediaFormatCaps, - int codedVideoMediaFormatCapsCount) : - mMaxInputMedias(maxInputMedias), - mCodedVideoMediaFormatCaps(codedVideoMediaFormatCaps), - mCodedVideoMediaFormatCapsCount(codedVideoMediaFormatCapsCount) -{ - int res; - pthread_mutexattr_t attr; - bool attr_created = false; - - res = pthread_mutexattr_init(&attr); - if (res != 0) { - ULOG_ERRNO("pthread_mutexattr_init", res); - goto error; - } - attr_created = true; - - res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - if (res != 0) { - ULOG_ERRNO("pthread_mutexattr_settype", res); - goto error; - } - - res = pthread_mutex_init(&mMutex, &attr); - if (res != 0) { - ULOG_ERRNO("pthread_mutex_init", res); - goto error; - } - - pthread_mutexattr_destroy(&attr); - return; - -error: - if (attr_created) - pthread_mutexattr_destroy(&attr); -} - - -CodedSink::~CodedSink(void) -{ - int ret = removeInputMedias(); - if (ret < 0) - ULOG_ERRNO("removeInputMedias", -ret); - - unsigned int count = getInputMediaCount(); - if (count > 0) { - ULOGW("not all input ports have been removed! (count=%d)", - count); - } - - pthread_mutex_destroy(&mMutex); -} - - -void CodedSink::lock(void) -{ - pthread_mutex_lock(&mMutex); -} - - -void CodedSink::unlock(void) -{ - pthread_mutex_unlock(&mMutex); -} - - -unsigned int CodedSink::getInputMediaCount(void) -{ - pthread_mutex_lock(&mMutex); - unsigned int ret = mInputPorts.size(); - pthread_mutex_unlock(&mMutex); - return ret; -} - - -CodedVideoMedia *CodedSink::getInputMedia(unsigned int index) -{ - pthread_mutex_lock(&mMutex); - CodedVideoMedia *ret = (index < mInputPorts.size()) - ? mInputPorts.at(index).media - : nullptr; - pthread_mutex_unlock(&mMutex); - return ret; -} - - -CodedVideoMedia *CodedSink::findInputMedia(CodedVideoMedia *media) -{ - pthread_mutex_lock(&mMutex); - CodedVideoMedia *ret = nullptr; - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - if (p->media != media) { - p++; - continue; - } - ret = p->media; - break; - } - pthread_mutex_unlock(&mMutex); - return ret; -} - - -CodedSink::InputPort *CodedSink::getInputPort(CodedVideoMedia *media) -{ - if (media == nullptr) { - ULOG_ERRNO("media", EINVAL); - return nullptr; - } - - pthread_mutex_lock(&mMutex); - InputPort *ret = nullptr; - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - if (p->media != media) { - p++; - continue; - } - ret = &(*p); - break; - } - - pthread_mutex_unlock(&mMutex); - return ret; -} - - -int CodedSink::addInputMedia(CodedVideoMedia *media) -{ - if (media == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - if (getInputPort(media) != nullptr) { - pthread_mutex_unlock(&mMutex); - return -EEXIST; - } - if (mInputPorts.size() >= mMaxInputMedias) { - pthread_mutex_unlock(&mMutex); - return -ENOBUFS; - } - if (!vdef_coded_format_intersect(&media->format, - mCodedVideoMediaFormatCaps, - mCodedVideoMediaFormatCapsCount)) { - pthread_mutex_unlock(&mMutex); - ULOGE("%s: coded video media" - " format " VDEF_CODED_FORMAT_TO_STR_FMT " not supported", - getName().c_str(), - VDEF_CODED_FORMAT_TO_STR_ARG(&media->format)); - return -ENOSYS; - } - - InputPort port; - memset(&port, 0, sizeof(port)); - port.media = media; - port.channel = new CodedChannel(this); - if (port.channel == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOGE("failed to create channel"); - return -ENOMEM; - } - port.channel->setCodedVideoMediaFormatCaps( - mCodedVideoMediaFormatCaps, mCodedVideoMediaFormatCapsCount); - - mInputPorts.push_back(port); - pthread_mutex_unlock(&mMutex); - - ULOGI("%s: link media name=%s", - getName().c_str(), - media->getName().c_str()); - return 0; -} - - -int CodedSink::removeInputMedia(CodedVideoMedia *media) -{ - if (media == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - bool found = false; - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - if (p->media != media) { - p++; - continue; - } - found = true; - ULOGI("%s: unlink media name=%s", - getName().c_str(), - media->getName().c_str()); - int ret = p->channel->unlink(); - if (ret < 0) - ULOG_ERRNO("channel->unlink", -ret); - delete p->channel; - p->channel = nullptr; - mInputPorts.erase(p); - break; - } - - pthread_mutex_unlock(&mMutex); - if (!found) - return -ENOENT; - - return 0; -} - - -int CodedSink::removeInputMedias(void) -{ - pthread_mutex_lock(&mMutex); - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - ULOGI("%s: unlink media name=%s", - getName().c_str(), - p->media->getName().c_str()); - int ret = p->channel->unlink(); - if (ret < 0) - ULOG_ERRNO("channel->unlink", -ret); - delete p->channel; - p->channel = nullptr; - p++; - } - - mInputPorts.clear(); - - pthread_mutex_unlock(&mMutex); - return 0; -} - - -CodedChannel *CodedSink::getInputChannel(CodedVideoMedia *media) -{ - if (media == nullptr) { - ULOG_ERRNO("media", EINVAL); - return nullptr; - } - - pthread_mutex_lock(&mMutex); - InputPort *port = getInputPort(media); - if (port == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("port", ENOENT); - return nullptr; - } - - CodedChannel *ret = port->channel; - pthread_mutex_unlock(&mMutex); - return ret; -} - - -void CodedSink::onChannelQueue(CodedChannel *channel, - struct mbuf_coded_video_frame *frame) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - if (frame == nullptr) { - ULOG_ERRNO("frame", EINVAL); - return; - } - struct mbuf_coded_video_frame_queue *queue = channel->getQueue(); - if (queue == nullptr) { - ULOGE("invalid queue"); - return; - } - - int ret = mbuf_coded_video_frame_queue_push(queue, frame); - if (ret < 0) { - ULOG_ERRNO("mbuf_coded_video_frame_queue_push", -ret); - return; - } -} - - -void CodedSink::onChannelFlush(CodedChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - struct mbuf_coded_video_frame_queue *queue = channel->getQueue(); - if (queue == nullptr) { - ULOGE("invalid queue"); - return; - } - - int ret = mbuf_coded_video_frame_queue_flush(queue); - if (ret < 0) { - ULOG_ERRNO("mbuf_coded_video_frame_queue_flush", -ret); - return; - } - - ret = channel->flushDone(); - if (ret < 0) - ULOG_ERRNO("channel->flushDone", -ret); -} - - -void CodedSink::onChannelTeardown(CodedChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - pthread_mutex_lock(&mMutex); - CodedVideoMedia *media = nullptr; - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - if (p->channel != channel) { - p++; - continue; - } - media = p->media; - break; - } - - if (media == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("media", ENOENT); - return; - } - - int ret = removeInputMedia(media); - if (ret < 0) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("removeInputMedia", -ret); - return; - } - - pthread_mutex_unlock(&mMutex); -} - - -void CodedSink::onChannelSos(CodedChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - pthread_mutex_lock(&mMutex); - CodedVideoMedia *media = nullptr; - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - if (p->channel != channel) { - p++; - continue; - } - media = p->media; - break; - } - - if (media == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("media", ENOENT); - return; - } - - ULOGD("%s: channel SOS media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - /* Nothing to do here, the function should be - * overloaded by sub-classes */ - - pthread_mutex_unlock(&mMutex); -} - - -void CodedSink::onChannelEos(CodedChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - pthread_mutex_lock(&mMutex); - CodedVideoMedia *media = nullptr; - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - if (p->channel != channel) { - p++; - continue; - } - media = p->media; - break; - } - - if (media == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("media", ENOENT); - return; - } - - ULOGD("%s: channel EOS media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - /* Nothing to do here, the function should be - * overloaded by sub-classes */ - - pthread_mutex_unlock(&mMutex); -} - - -void CodedSink::onChannelReconfigure(CodedChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - pthread_mutex_lock(&mMutex); - CodedVideoMedia *media = nullptr; - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - if (p->channel != channel) { - p++; - continue; - } - media = p->media; - break; - } - - if (media == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("media", ENOENT); - return; - } - - ULOGD("%s: channel reconfigure media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - /* Nothing to do here, the function should be - * overloaded by sub-classes */ - - pthread_mutex_unlock(&mMutex); -} - - -void CodedSink::onChannelTimeout(CodedChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - pthread_mutex_lock(&mMutex); - CodedVideoMedia *media = nullptr; - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - if (p->channel != channel) { - p++; - continue; - } - media = p->media; - break; - } - - if (media == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("media", ENOENT); - return; - } - - ULOGD("%s: channel timeout media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - /* Nothing to do here, the function should be - * overloaded by sub-classes */ - - pthread_mutex_unlock(&mMutex); -} - - -void CodedSink::onChannelPhotoTrigger(CodedChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - pthread_mutex_lock(&mMutex); - CodedVideoMedia *media = nullptr; - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - if (p->channel != channel) { - p++; - continue; - } - media = p->media; - break; - } - - if (media == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("media", ENOENT); - return; - } - - ULOGD("%s: channel photo_trigger " - "media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - /* Nothing to do here, the function should be - * overloaded by sub-classes */ - - pthread_mutex_unlock(&mMutex); -} - - -void CodedSink::onChannelDownstreamEvent(CodedChannel *channel, - const struct pomp_msg *event) -{ - ULOGD("%s: channel downstream event %s", - getName().c_str(), - CodedChannel::getDownstreamEventStr( - (CodedChannel::DownstreamEvent)pomp_msg_get_id(event))); - - switch (pomp_msg_get_id(event)) { - case CodedChannel::DownstreamEvent::FLUSH: - onChannelFlush(channel); - break; - case CodedChannel::DownstreamEvent::TEARDOWN: - onChannelTeardown(channel); - break; - case CodedChannel::DownstreamEvent::SOS: - onChannelSos(channel); - break; - case CodedChannel::DownstreamEvent::EOS: - onChannelEos(channel); - break; - case CodedChannel::DownstreamEvent::RECONFIGURE: - onChannelReconfigure(channel); - break; - case CodedChannel::DownstreamEvent::TIMEOUT: - onChannelTimeout(channel); - break; - case CodedChannel::DownstreamEvent::PHOTO_TRIGGER: - onChannelPhotoTrigger(channel); - break; - default: - ULOG_ERRNO("event id %d", ENOSYS, pomp_msg_get_id(event)); - break; - } -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Pipeline sink element + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_sink_coded_video +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_sink_coded_video.hpp" + +#include + +namespace Pdraw { + + +CodedSink::CodedSink(unsigned int maxInputMedias, + const struct vdef_coded_format *codedVideoMediaFormatCaps, + int codedVideoMediaFormatCapsCount) : + mMaxInputMedias(maxInputMedias), + mCodedVideoMediaFormatCaps(codedVideoMediaFormatCaps), + mCodedVideoMediaFormatCapsCount(codedVideoMediaFormatCapsCount) +{ + int res; + pthread_mutexattr_t attr; + bool attr_created = false; + + res = pthread_mutexattr_init(&attr); + if (res != 0) { + ULOG_ERRNO("pthread_mutexattr_init", res); + goto error; + } + attr_created = true; + + res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + if (res != 0) { + ULOG_ERRNO("pthread_mutexattr_settype", res); + goto error; + } + + res = pthread_mutex_init(&mMutex, &attr); + if (res != 0) { + ULOG_ERRNO("pthread_mutex_init", res); + goto error; + } + + pthread_mutexattr_destroy(&attr); + return; + +error: + if (attr_created) + pthread_mutexattr_destroy(&attr); +} + + +CodedSink::~CodedSink(void) +{ + int ret = removeInputMedias(); + if (ret < 0) + ULOG_ERRNO("removeInputMedias", -ret); + + unsigned int count = getInputMediaCount(); + if (count > 0) { + ULOGW("not all input ports have been removed! (count=%d)", + count); + } + + pthread_mutex_destroy(&mMutex); +} + + +void CodedSink::lock(void) +{ + pthread_mutex_lock(&mMutex); +} + + +void CodedSink::unlock(void) +{ + pthread_mutex_unlock(&mMutex); +} + + +unsigned int CodedSink::getInputMediaCount(void) +{ + pthread_mutex_lock(&mMutex); + unsigned int ret = mInputPorts.size(); + pthread_mutex_unlock(&mMutex); + return ret; +} + + +CodedVideoMedia *CodedSink::getInputMedia(unsigned int index) +{ + pthread_mutex_lock(&mMutex); + CodedVideoMedia *ret = (index < mInputPorts.size()) + ? mInputPorts.at(index).media + : nullptr; + pthread_mutex_unlock(&mMutex); + return ret; +} + + +CodedVideoMedia *CodedSink::findInputMedia(CodedVideoMedia *media) +{ + pthread_mutex_lock(&mMutex); + CodedVideoMedia *ret = nullptr; + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + if (p->media != media) { + p++; + continue; + } + ret = p->media; + break; + } + pthread_mutex_unlock(&mMutex); + return ret; +} + + +CodedSink::InputPort *CodedSink::getInputPort(CodedVideoMedia *media) +{ + if (media == nullptr) { + ULOG_ERRNO("media", EINVAL); + return nullptr; + } + + pthread_mutex_lock(&mMutex); + InputPort *ret = nullptr; + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + if (p->media != media) { + p++; + continue; + } + ret = &(*p); + break; + } + + pthread_mutex_unlock(&mMutex); + return ret; +} + + +int CodedSink::addInputMedia(CodedVideoMedia *media) +{ + if (media == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + if (getInputPort(media) != nullptr) { + pthread_mutex_unlock(&mMutex); + return -EEXIST; + } + if (mInputPorts.size() >= mMaxInputMedias) { + pthread_mutex_unlock(&mMutex); + return -ENOBUFS; + } + if (!vdef_coded_format_intersect(&media->format, + mCodedVideoMediaFormatCaps, + mCodedVideoMediaFormatCapsCount)) { + pthread_mutex_unlock(&mMutex); + ULOGE("%s: coded video media" + " format " VDEF_CODED_FORMAT_TO_STR_FMT " not supported", + getName().c_str(), + VDEF_CODED_FORMAT_TO_STR_ARG(&media->format)); + return -ENOSYS; + } + + InputPort port; + memset(&port, 0, sizeof(port)); + port.media = media; + port.channel = new CodedChannel(this); + if (port.channel == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOGE("failed to create channel"); + return -ENOMEM; + } + port.channel->setCodedVideoMediaFormatCaps( + mCodedVideoMediaFormatCaps, mCodedVideoMediaFormatCapsCount); + + mInputPorts.push_back(port); + pthread_mutex_unlock(&mMutex); + + ULOGI("%s: link media name=%s", + getName().c_str(), + media->getName().c_str()); + return 0; +} + + +int CodedSink::removeInputMedia(CodedVideoMedia *media) +{ + if (media == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + bool found = false; + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + if (p->media != media) { + p++; + continue; + } + found = true; + ULOGI("%s: unlink media name=%s", + getName().c_str(), + media->getName().c_str()); + int ret = p->channel->unlink(); + if (ret < 0) + ULOG_ERRNO("channel->unlink", -ret); + delete p->channel; + p->channel = nullptr; + mInputPorts.erase(p); + break; + } + + pthread_mutex_unlock(&mMutex); + if (!found) + return -ENOENT; + + return 0; +} + + +int CodedSink::removeInputMedias(void) +{ + pthread_mutex_lock(&mMutex); + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + ULOGI("%s: unlink media name=%s", + getName().c_str(), + p->media->getName().c_str()); + int ret = p->channel->unlink(); + if (ret < 0) + ULOG_ERRNO("channel->unlink", -ret); + delete p->channel; + p->channel = nullptr; + p++; + } + + mInputPorts.clear(); + + pthread_mutex_unlock(&mMutex); + return 0; +} + + +CodedChannel *CodedSink::getInputChannel(CodedVideoMedia *media) +{ + if (media == nullptr) { + ULOG_ERRNO("media", EINVAL); + return nullptr; + } + + pthread_mutex_lock(&mMutex); + InputPort *port = getInputPort(media); + if (port == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("port", ENOENT); + return nullptr; + } + + CodedChannel *ret = port->channel; + pthread_mutex_unlock(&mMutex); + return ret; +} + + +void CodedSink::onChannelQueue(CodedChannel *channel, + struct mbuf_coded_video_frame *frame) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + if (frame == nullptr) { + ULOG_ERRNO("frame", EINVAL); + return; + } + struct mbuf_coded_video_frame_queue *queue = channel->getQueue(); + if (queue == nullptr) { + ULOGE("invalid queue"); + return; + } + + int ret = mbuf_coded_video_frame_queue_push(queue, frame); + if (ret < 0) { + ULOG_ERRNO("mbuf_coded_video_frame_queue_push", -ret); + return; + } +} + + +void CodedSink::onChannelFlush(CodedChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + struct mbuf_coded_video_frame_queue *queue = channel->getQueue(); + if (queue == nullptr) { + ULOGE("invalid queue"); + return; + } + + int ret = mbuf_coded_video_frame_queue_flush(queue); + if (ret < 0) { + ULOG_ERRNO("mbuf_coded_video_frame_queue_flush", -ret); + return; + } + + ret = channel->flushDone(); + if (ret < 0) + ULOG_ERRNO("channel->flushDone", -ret); +} + + +void CodedSink::onChannelTeardown(CodedChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + pthread_mutex_lock(&mMutex); + CodedVideoMedia *media = nullptr; + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + if (p->channel != channel) { + p++; + continue; + } + media = p->media; + break; + } + + if (media == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("media", ENOENT); + return; + } + + int ret = removeInputMedia(media); + if (ret < 0) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("removeInputMedia", -ret); + return; + } + + pthread_mutex_unlock(&mMutex); +} + + +void CodedSink::onChannelSos(CodedChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + pthread_mutex_lock(&mMutex); + CodedVideoMedia *media = nullptr; + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + if (p->channel != channel) { + p++; + continue; + } + media = p->media; + break; + } + + if (media == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("media", ENOENT); + return; + } + + ULOGD("%s: channel SOS media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + /* Nothing to do here, the function should be + * overloaded by sub-classes */ + + pthread_mutex_unlock(&mMutex); +} + + +void CodedSink::onChannelEos(CodedChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + pthread_mutex_lock(&mMutex); + CodedVideoMedia *media = nullptr; + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + if (p->channel != channel) { + p++; + continue; + } + media = p->media; + break; + } + + if (media == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("media", ENOENT); + return; + } + + ULOGD("%s: channel EOS media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + /* Nothing to do here, the function should be + * overloaded by sub-classes */ + + pthread_mutex_unlock(&mMutex); +} + + +void CodedSink::onChannelReconfigure(CodedChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + pthread_mutex_lock(&mMutex); + CodedVideoMedia *media = nullptr; + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + if (p->channel != channel) { + p++; + continue; + } + media = p->media; + break; + } + + if (media == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("media", ENOENT); + return; + } + + ULOGD("%s: channel reconfigure media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + /* Nothing to do here, the function should be + * overloaded by sub-classes */ + + pthread_mutex_unlock(&mMutex); +} + + +void CodedSink::onChannelTimeout(CodedChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + pthread_mutex_lock(&mMutex); + CodedVideoMedia *media = nullptr; + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + if (p->channel != channel) { + p++; + continue; + } + media = p->media; + break; + } + + if (media == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("media", ENOENT); + return; + } + + ULOGD("%s: channel timeout media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + /* Nothing to do here, the function should be + * overloaded by sub-classes */ + + pthread_mutex_unlock(&mMutex); +} + + +void CodedSink::onChannelPhotoTrigger(CodedChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + pthread_mutex_lock(&mMutex); + CodedVideoMedia *media = nullptr; + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + if (p->channel != channel) { + p++; + continue; + } + media = p->media; + break; + } + + if (media == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("media", ENOENT); + return; + } + + ULOGD("%s: channel photo_trigger " + "media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + /* Nothing to do here, the function should be + * overloaded by sub-classes */ + + pthread_mutex_unlock(&mMutex); +} + + +void CodedSink::onChannelDownstreamEvent(CodedChannel *channel, + const struct pomp_msg *event) +{ + ULOGD("%s: channel downstream event %s", + getName().c_str(), + CodedChannel::getDownstreamEventStr( + (CodedChannel::DownstreamEvent)pomp_msg_get_id(event))); + + switch (pomp_msg_get_id(event)) { + case CodedChannel::DownstreamEvent::FLUSH: + onChannelFlush(channel); + break; + case CodedChannel::DownstreamEvent::TEARDOWN: + onChannelTeardown(channel); + break; + case CodedChannel::DownstreamEvent::SOS: + onChannelSos(channel); + break; + case CodedChannel::DownstreamEvent::EOS: + onChannelEos(channel); + break; + case CodedChannel::DownstreamEvent::RECONFIGURE: + onChannelReconfigure(channel); + break; + case CodedChannel::DownstreamEvent::TIMEOUT: + onChannelTimeout(channel); + break; + case CodedChannel::DownstreamEvent::PHOTO_TRIGGER: + onChannelPhotoTrigger(channel); + break; + default: + ULOG_ERRNO("event id %d", ENOSYS, pomp_msg_get_id(event)); + break; + } +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_sink_coded_video.hpp b/libpdraw/src/pdraw_sink_coded_video.hpp index 1886727..670e403 100644 --- a/libpdraw/src/pdraw_sink_coded_video.hpp +++ b/libpdraw/src/pdraw_sink_coded_video.hpp @@ -1,121 +1,121 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Pipeline sink element for coded video - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_SINK_CODED_VIDEO_HPP_ -#define _PDRAW_SINK_CODED_VIDEO_HPP_ - -#include "pdraw_channel_coded_video.hpp" -#include "pdraw_media.hpp" - -#include - -namespace Pdraw { - -class CodedSink : public CodedChannel::SinkListener { -public: - virtual ~CodedSink(void); - - void lock(void); - - void unlock(void); - - virtual std::string &getName(void) = 0; - - int getCodedVideoMediaFormatCaps(const struct vdef_coded_format **caps) - { - if (!caps) - return -EINVAL; - *caps = mCodedVideoMediaFormatCaps; - return mCodedVideoMediaFormatCapsCount; - } - - unsigned int getInputMediaCount(void); - - CodedVideoMedia *getInputMedia(unsigned int index); - - CodedVideoMedia *findInputMedia(CodedVideoMedia *media); - - virtual int addInputMedia(CodedVideoMedia *media); - - virtual int removeInputMedia(CodedVideoMedia *media); - - CodedChannel *getInputChannel(CodedVideoMedia *media); - -protected: - struct InputPort { - CodedVideoMedia *media; - CodedChannel *channel; - }; - - CodedSink(unsigned int maxInputMedias, - const struct vdef_coded_format *codedVideoMediaFormatCaps, - int codedVideoMediaFormatCapsCount); - - void setCodedVideoMediaFormatCaps(const struct vdef_coded_format *caps, - int count) - { - mCodedVideoMediaFormatCaps = caps; - mCodedVideoMediaFormatCapsCount = count; - } - - InputPort *getInputPort(CodedVideoMedia *media); - - virtual int removeInputMedias(void); - - virtual void onChannelQueue(CodedChannel *channel, - struct mbuf_coded_video_frame *frame); - - virtual void onChannelDownstreamEvent(CodedChannel *channel, - const struct pomp_msg *event); - - virtual void onChannelFlush(CodedChannel *channel); - - virtual void onChannelTeardown(CodedChannel *channel); - - virtual void onChannelSos(CodedChannel *channel); - - virtual void onChannelEos(CodedChannel *channel); - - virtual void onChannelReconfigure(CodedChannel *channel); - - virtual void onChannelTimeout(CodedChannel *channel); - - virtual void onChannelPhotoTrigger(CodedChannel *channel); - - pthread_mutex_t mMutex; - unsigned int mMaxInputMedias; - std::vector mInputPorts; - const struct vdef_coded_format *mCodedVideoMediaFormatCaps; - int mCodedVideoMediaFormatCapsCount; -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_SINK_CODED_VIDEO_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Pipeline sink element for coded video + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_SINK_CODED_VIDEO_HPP_ +#define _PDRAW_SINK_CODED_VIDEO_HPP_ + +#include "pdraw_channel_coded_video.hpp" +#include "pdraw_media.hpp" + +#include + +namespace Pdraw { + +class CodedSink : public CodedChannel::SinkListener { +public: + virtual ~CodedSink(void); + + void lock(void); + + void unlock(void); + + virtual std::string &getName(void) = 0; + + int getCodedVideoMediaFormatCaps(const struct vdef_coded_format **caps) + { + if (!caps) + return -EINVAL; + *caps = mCodedVideoMediaFormatCaps; + return mCodedVideoMediaFormatCapsCount; + } + + unsigned int getInputMediaCount(void); + + CodedVideoMedia *getInputMedia(unsigned int index); + + CodedVideoMedia *findInputMedia(CodedVideoMedia *media); + + virtual int addInputMedia(CodedVideoMedia *media); + + virtual int removeInputMedia(CodedVideoMedia *media); + + CodedChannel *getInputChannel(CodedVideoMedia *media); + +protected: + struct InputPort { + CodedVideoMedia *media; + CodedChannel *channel; + }; + + CodedSink(unsigned int maxInputMedias, + const struct vdef_coded_format *codedVideoMediaFormatCaps, + int codedVideoMediaFormatCapsCount); + + void setCodedVideoMediaFormatCaps(const struct vdef_coded_format *caps, + int count) + { + mCodedVideoMediaFormatCaps = caps; + mCodedVideoMediaFormatCapsCount = count; + } + + InputPort *getInputPort(CodedVideoMedia *media); + + virtual int removeInputMedias(void); + + virtual void onChannelQueue(CodedChannel *channel, + struct mbuf_coded_video_frame *frame); + + virtual void onChannelDownstreamEvent(CodedChannel *channel, + const struct pomp_msg *event); + + virtual void onChannelFlush(CodedChannel *channel); + + virtual void onChannelTeardown(CodedChannel *channel); + + virtual void onChannelSos(CodedChannel *channel); + + virtual void onChannelEos(CodedChannel *channel); + + virtual void onChannelReconfigure(CodedChannel *channel); + + virtual void onChannelTimeout(CodedChannel *channel); + + virtual void onChannelPhotoTrigger(CodedChannel *channel); + + pthread_mutex_t mMutex; + unsigned int mMaxInputMedias; + std::vector mInputPorts; + const struct vdef_coded_format *mCodedVideoMediaFormatCaps; + int mCodedVideoMediaFormatCapsCount; +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_SINK_CODED_VIDEO_HPP_ */ diff --git a/libpdraw/src/pdraw_sink_raw_video.cpp b/libpdraw/src/pdraw_sink_raw_video.cpp index 7ece9cc..59ef20a 100644 --- a/libpdraw/src/pdraw_sink_raw_video.cpp +++ b/libpdraw/src/pdraw_sink_raw_video.cpp @@ -1,612 +1,612 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Pipeline sink element - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_sink_raw_video -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_sink_raw_video.hpp" - -#include - -namespace Pdraw { - - -RawSink::RawSink(unsigned int maxInputMedias, - const struct vdef_raw_format *rawVideoMediaFormatCaps, - int rawVideoMediaFormatCapsCount) : - mMaxInputMedias(maxInputMedias), - mRawVideoMediaFormatCaps(rawVideoMediaFormatCaps), - mRawVideoMediaFormatCapsCount(rawVideoMediaFormatCapsCount) -{ - int res; - pthread_mutexattr_t attr; - bool attr_created = false; - - res = pthread_mutexattr_init(&attr); - if (res != 0) { - ULOG_ERRNO("pthread_mutexattr_init", res); - goto error; - } - attr_created = true; - - res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - if (res != 0) { - ULOG_ERRNO("pthread_mutexattr_settype", res); - goto error; - } - - res = pthread_mutex_init(&mMutex, &attr); - if (res != 0) { - ULOG_ERRNO("pthread_mutex_init", res); - goto error; - } - - pthread_mutexattr_destroy(&attr); - return; - -error: - if (attr_created) - pthread_mutexattr_destroy(&attr); -} - - -RawSink::~RawSink(void) -{ - int ret = removeInputMedias(); - if (ret < 0) - ULOG_ERRNO("removeInputMedias", -ret); - - unsigned int count = getInputMediaCount(); - if (count > 0) { - ULOGW("not all input ports have been removed! (count=%d)", - count); - } - - pthread_mutex_destroy(&mMutex); -} - - -void RawSink::lock(void) -{ - pthread_mutex_lock(&mMutex); -} - - -void RawSink::unlock(void) -{ - pthread_mutex_unlock(&mMutex); -} - - -unsigned int RawSink::getInputMediaCount(void) -{ - pthread_mutex_lock(&mMutex); - unsigned int ret = mInputPorts.size(); - pthread_mutex_unlock(&mMutex); - return ret; -} - - -RawVideoMedia *RawSink::getInputMedia(unsigned int index) -{ - pthread_mutex_lock(&mMutex); - RawVideoMedia *ret = (index < mInputPorts.size()) - ? mInputPorts.at(index).media - : nullptr; - pthread_mutex_unlock(&mMutex); - return ret; -} - - -RawVideoMedia *RawSink::findInputMedia(RawVideoMedia *media) -{ - pthread_mutex_lock(&mMutex); - RawVideoMedia *ret = nullptr; - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - if (p->media != media) { - p++; - continue; - } - ret = p->media; - break; - } - pthread_mutex_unlock(&mMutex); - return ret; -} - - -RawSink::InputPort *RawSink::getInputPort(RawVideoMedia *media) -{ - if (media == nullptr) { - ULOG_ERRNO("media", EINVAL); - return nullptr; - } - - pthread_mutex_lock(&mMutex); - InputPort *ret = nullptr; - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - if (p->media != media) { - p++; - continue; - } - ret = &(*p); - break; - } - - pthread_mutex_unlock(&mMutex); - return ret; -} - - -int RawSink::addInputMedia(RawVideoMedia *media) -{ - if (media == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - if (getInputPort(media) != nullptr) { - pthread_mutex_unlock(&mMutex); - return -EEXIST; - } - if (mInputPorts.size() >= mMaxInputMedias) { - pthread_mutex_unlock(&mMutex); - return -ENOBUFS; - } - if (!vdef_raw_format_intersect(&media->format, - mRawVideoMediaFormatCaps, - mRawVideoMediaFormatCapsCount)) { - pthread_mutex_unlock(&mMutex); - ULOGE("raw video media" - " format " VDEF_RAW_FORMAT_TO_STR_FMT " not supported", - VDEF_RAW_FORMAT_TO_STR_ARG(&media->format)); - return -ENOSYS; - } - - InputPort port; - memset(&port, 0, sizeof(port)); - port.media = media; - port.channel = new RawChannel(this); - if (port.channel == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOGE("failed to create channel"); - return -ENOMEM; - } - port.channel->setRawVideoMediaFormatCaps(mRawVideoMediaFormatCaps, - mRawVideoMediaFormatCapsCount); - - mInputPorts.push_back(port); - pthread_mutex_unlock(&mMutex); - - ULOGI("%s: link media name=%s", - getName().c_str(), - media->getName().c_str()); - return 0; -} - - -int RawSink::removeInputMedia(RawVideoMedia *media) -{ - if (media == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - bool found = false; - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - if (p->media != media) { - p++; - continue; - } - found = true; - ULOGI("%s: unlink media name=%s", - getName().c_str(), - media->getName().c_str()); - int ret = p->channel->unlink(); - if (ret < 0) - ULOG_ERRNO("channel->unlink", -ret); - delete p->channel; - p->channel = nullptr; - mInputPorts.erase(p); - break; - } - - pthread_mutex_unlock(&mMutex); - if (!found) - return -ENOENT; - - return 0; -} - - -int RawSink::removeInputMedias(void) -{ - pthread_mutex_lock(&mMutex); - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - ULOGI("%s: unlink media name=%s", - getName().c_str(), - p->media->getName().c_str()); - int ret = p->channel->unlink(); - if (ret < 0) - ULOG_ERRNO("channel->unlink", -ret); - delete p->channel; - p->channel = nullptr; - p++; - } - - mInputPorts.clear(); - - pthread_mutex_unlock(&mMutex); - return 0; -} - - -RawChannel *RawSink::getInputChannel(RawVideoMedia *media) -{ - if (media == nullptr) { - ULOG_ERRNO("media", EINVAL); - return nullptr; - } - - pthread_mutex_lock(&mMutex); - InputPort *port = getInputPort(media); - if (port == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("port", ENOENT); - return nullptr; - } - - RawChannel *ret = port->channel; - pthread_mutex_unlock(&mMutex); - return ret; -} - - -void RawSink::onChannelQueue(RawChannel *channel, - struct mbuf_raw_video_frame *frame) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - if (frame == nullptr) { - ULOG_ERRNO("frame", EINVAL); - return; - } - struct mbuf_raw_video_frame_queue *queue = channel->getQueue(); - if (queue == nullptr) { - ULOGE("invalid queue"); - return; - } - - int ret = mbuf_raw_video_frame_queue_push(queue, frame); - if (ret < 0) { - ULOG_ERRNO("mbuf_raw_video_frame_queue_push", -ret); - return; - } -} - - -void RawSink::onChannelFlush(RawChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - struct mbuf_raw_video_frame_queue *queue = channel->getQueue(); - if (queue == nullptr) { - ULOGE("invalid queue"); - return; - } - - int ret = mbuf_raw_video_frame_queue_flush(queue); - if (ret < 0) { - ULOG_ERRNO("mbuf_raw_video_frame_queue_flush", -ret); - return; - } - - ret = channel->flushDone(); - if (ret < 0) - ULOG_ERRNO("channel->flushDone", -ret); -} - - -void RawSink::onChannelTeardown(RawChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - pthread_mutex_lock(&mMutex); - RawVideoMedia *media = nullptr; - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - if (p->channel != channel) { - p++; - continue; - } - media = p->media; - break; - } - - if (media == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("media", ENOENT); - return; - } - - int ret = removeInputMedia(media); - if (ret < 0) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("removeInputMedia", -ret); - return; - } - - pthread_mutex_unlock(&mMutex); -} - - -void RawSink::onChannelSos(RawChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - pthread_mutex_lock(&mMutex); - RawVideoMedia *media = nullptr; - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - if (p->channel != channel) { - p++; - continue; - } - media = p->media; - break; - } - - if (media == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("media", ENOENT); - return; - } - - ULOGD("%s: channel SOS media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - /* Nothing to do here, the function should be - * overloaded by sub-classes */ - - pthread_mutex_unlock(&mMutex); -} - - -void RawSink::onChannelEos(RawChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - pthread_mutex_lock(&mMutex); - RawVideoMedia *media = nullptr; - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - if (p->channel != channel) { - p++; - continue; - } - media = p->media; - break; - } - - if (media == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("media", ENOENT); - return; - } - - ULOGD("%s: channel EOS media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - /* Nothing to do here, the function should be - * overloaded by sub-classes */ - - pthread_mutex_unlock(&mMutex); -} - - -void RawSink::onChannelReconfigure(RawChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - pthread_mutex_lock(&mMutex); - RawVideoMedia *media = nullptr; - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - if (p->channel != channel) { - p++; - continue; - } - media = p->media; - break; - } - - if (media == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("media", ENOENT); - return; - } - - ULOGD("%s: channel reconfigure media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - /* Nothing to do here, the function should be - * overloaded by sub-classes */ - - pthread_mutex_unlock(&mMutex); -} - - -void RawSink::onChannelTimeout(RawChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - pthread_mutex_lock(&mMutex); - RawVideoMedia *media = nullptr; - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - if (p->channel != channel) { - p++; - continue; - } - media = p->media; - break; - } - - if (media == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("media", ENOENT); - return; - } - - ULOGD("%s: channel timeout media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - /* Nothing to do here, the function should be - * overloaded by sub-classes */ - - pthread_mutex_unlock(&mMutex); -} - - -void RawSink::onChannelPhotoTrigger(RawChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - pthread_mutex_lock(&mMutex); - RawVideoMedia *media = nullptr; - std::vector::iterator p = mInputPorts.begin(); - - while (p != mInputPorts.end()) { - if (p->channel != channel) { - p++; - continue; - } - media = p->media; - break; - } - - if (media == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("media", ENOENT); - return; - } - - ULOGD("%s: channel photo_trigger " - "media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - /* Nothing to do here, the function should be - * overloaded by sub-classes */ - - pthread_mutex_unlock(&mMutex); -} - - -void RawSink::onChannelDownstreamEvent(RawChannel *channel, - const struct pomp_msg *event) -{ - ULOGD("%s: channel downstream event %s", - getName().c_str(), - RawChannel::getDownstreamEventStr( - (RawChannel::DownstreamEvent)pomp_msg_get_id(event))); - - switch (pomp_msg_get_id(event)) { - case RawChannel::DownstreamEvent::FLUSH: - onChannelFlush(channel); - break; - case RawChannel::DownstreamEvent::TEARDOWN: - onChannelTeardown(channel); - break; - case RawChannel::DownstreamEvent::SOS: - onChannelSos(channel); - break; - case RawChannel::DownstreamEvent::EOS: - onChannelEos(channel); - break; - case RawChannel::DownstreamEvent::RECONFIGURE: - onChannelReconfigure(channel); - break; - case RawChannel::DownstreamEvent::TIMEOUT: - onChannelTimeout(channel); - break; - case RawChannel::DownstreamEvent::PHOTO_TRIGGER: - onChannelPhotoTrigger(channel); - break; - default: - ULOG_ERRNO("event id %d", ENOSYS, pomp_msg_get_id(event)); - break; - } -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Pipeline sink element + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_sink_raw_video +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_sink_raw_video.hpp" + +#include + +namespace Pdraw { + + +RawSink::RawSink(unsigned int maxInputMedias, + const struct vdef_raw_format *rawVideoMediaFormatCaps, + int rawVideoMediaFormatCapsCount) : + mMaxInputMedias(maxInputMedias), + mRawVideoMediaFormatCaps(rawVideoMediaFormatCaps), + mRawVideoMediaFormatCapsCount(rawVideoMediaFormatCapsCount) +{ + int res; + pthread_mutexattr_t attr; + bool attr_created = false; + + res = pthread_mutexattr_init(&attr); + if (res != 0) { + ULOG_ERRNO("pthread_mutexattr_init", res); + goto error; + } + attr_created = true; + + res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + if (res != 0) { + ULOG_ERRNO("pthread_mutexattr_settype", res); + goto error; + } + + res = pthread_mutex_init(&mMutex, &attr); + if (res != 0) { + ULOG_ERRNO("pthread_mutex_init", res); + goto error; + } + + pthread_mutexattr_destroy(&attr); + return; + +error: + if (attr_created) + pthread_mutexattr_destroy(&attr); +} + + +RawSink::~RawSink(void) +{ + int ret = removeInputMedias(); + if (ret < 0) + ULOG_ERRNO("removeInputMedias", -ret); + + unsigned int count = getInputMediaCount(); + if (count > 0) { + ULOGW("not all input ports have been removed! (count=%d)", + count); + } + + pthread_mutex_destroy(&mMutex); +} + + +void RawSink::lock(void) +{ + pthread_mutex_lock(&mMutex); +} + + +void RawSink::unlock(void) +{ + pthread_mutex_unlock(&mMutex); +} + + +unsigned int RawSink::getInputMediaCount(void) +{ + pthread_mutex_lock(&mMutex); + unsigned int ret = mInputPorts.size(); + pthread_mutex_unlock(&mMutex); + return ret; +} + + +RawVideoMedia *RawSink::getInputMedia(unsigned int index) +{ + pthread_mutex_lock(&mMutex); + RawVideoMedia *ret = (index < mInputPorts.size()) + ? mInputPorts.at(index).media + : nullptr; + pthread_mutex_unlock(&mMutex); + return ret; +} + + +RawVideoMedia *RawSink::findInputMedia(RawVideoMedia *media) +{ + pthread_mutex_lock(&mMutex); + RawVideoMedia *ret = nullptr; + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + if (p->media != media) { + p++; + continue; + } + ret = p->media; + break; + } + pthread_mutex_unlock(&mMutex); + return ret; +} + + +RawSink::InputPort *RawSink::getInputPort(RawVideoMedia *media) +{ + if (media == nullptr) { + ULOG_ERRNO("media", EINVAL); + return nullptr; + } + + pthread_mutex_lock(&mMutex); + InputPort *ret = nullptr; + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + if (p->media != media) { + p++; + continue; + } + ret = &(*p); + break; + } + + pthread_mutex_unlock(&mMutex); + return ret; +} + + +int RawSink::addInputMedia(RawVideoMedia *media) +{ + if (media == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + if (getInputPort(media) != nullptr) { + pthread_mutex_unlock(&mMutex); + return -EEXIST; + } + if (mInputPorts.size() >= mMaxInputMedias) { + pthread_mutex_unlock(&mMutex); + return -ENOBUFS; + } + if (!vdef_raw_format_intersect(&media->format, + mRawVideoMediaFormatCaps, + mRawVideoMediaFormatCapsCount)) { + pthread_mutex_unlock(&mMutex); + ULOGE("raw video media" + " format " VDEF_RAW_FORMAT_TO_STR_FMT " not supported", + VDEF_RAW_FORMAT_TO_STR_ARG(&media->format)); + return -ENOSYS; + } + + InputPort port; + memset(&port, 0, sizeof(port)); + port.media = media; + port.channel = new RawChannel(this); + if (port.channel == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOGE("failed to create channel"); + return -ENOMEM; + } + port.channel->setRawVideoMediaFormatCaps(mRawVideoMediaFormatCaps, + mRawVideoMediaFormatCapsCount); + + mInputPorts.push_back(port); + pthread_mutex_unlock(&mMutex); + + ULOGI("%s: link media name=%s", + getName().c_str(), + media->getName().c_str()); + return 0; +} + + +int RawSink::removeInputMedia(RawVideoMedia *media) +{ + if (media == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + bool found = false; + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + if (p->media != media) { + p++; + continue; + } + found = true; + ULOGI("%s: unlink media name=%s", + getName().c_str(), + media->getName().c_str()); + int ret = p->channel->unlink(); + if (ret < 0) + ULOG_ERRNO("channel->unlink", -ret); + delete p->channel; + p->channel = nullptr; + mInputPorts.erase(p); + break; + } + + pthread_mutex_unlock(&mMutex); + if (!found) + return -ENOENT; + + return 0; +} + + +int RawSink::removeInputMedias(void) +{ + pthread_mutex_lock(&mMutex); + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + ULOGI("%s: unlink media name=%s", + getName().c_str(), + p->media->getName().c_str()); + int ret = p->channel->unlink(); + if (ret < 0) + ULOG_ERRNO("channel->unlink", -ret); + delete p->channel; + p->channel = nullptr; + p++; + } + + mInputPorts.clear(); + + pthread_mutex_unlock(&mMutex); + return 0; +} + + +RawChannel *RawSink::getInputChannel(RawVideoMedia *media) +{ + if (media == nullptr) { + ULOG_ERRNO("media", EINVAL); + return nullptr; + } + + pthread_mutex_lock(&mMutex); + InputPort *port = getInputPort(media); + if (port == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("port", ENOENT); + return nullptr; + } + + RawChannel *ret = port->channel; + pthread_mutex_unlock(&mMutex); + return ret; +} + + +void RawSink::onChannelQueue(RawChannel *channel, + struct mbuf_raw_video_frame *frame) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + if (frame == nullptr) { + ULOG_ERRNO("frame", EINVAL); + return; + } + struct mbuf_raw_video_frame_queue *queue = channel->getQueue(); + if (queue == nullptr) { + ULOGE("invalid queue"); + return; + } + + int ret = mbuf_raw_video_frame_queue_push(queue, frame); + if (ret < 0) { + ULOG_ERRNO("mbuf_raw_video_frame_queue_push", -ret); + return; + } +} + + +void RawSink::onChannelFlush(RawChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + struct mbuf_raw_video_frame_queue *queue = channel->getQueue(); + if (queue == nullptr) { + ULOGE("invalid queue"); + return; + } + + int ret = mbuf_raw_video_frame_queue_flush(queue); + if (ret < 0) { + ULOG_ERRNO("mbuf_raw_video_frame_queue_flush", -ret); + return; + } + + ret = channel->flushDone(); + if (ret < 0) + ULOG_ERRNO("channel->flushDone", -ret); +} + + +void RawSink::onChannelTeardown(RawChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + pthread_mutex_lock(&mMutex); + RawVideoMedia *media = nullptr; + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + if (p->channel != channel) { + p++; + continue; + } + media = p->media; + break; + } + + if (media == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("media", ENOENT); + return; + } + + int ret = removeInputMedia(media); + if (ret < 0) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("removeInputMedia", -ret); + return; + } + + pthread_mutex_unlock(&mMutex); +} + + +void RawSink::onChannelSos(RawChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + pthread_mutex_lock(&mMutex); + RawVideoMedia *media = nullptr; + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + if (p->channel != channel) { + p++; + continue; + } + media = p->media; + break; + } + + if (media == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("media", ENOENT); + return; + } + + ULOGD("%s: channel SOS media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + /* Nothing to do here, the function should be + * overloaded by sub-classes */ + + pthread_mutex_unlock(&mMutex); +} + + +void RawSink::onChannelEos(RawChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + pthread_mutex_lock(&mMutex); + RawVideoMedia *media = nullptr; + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + if (p->channel != channel) { + p++; + continue; + } + media = p->media; + break; + } + + if (media == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("media", ENOENT); + return; + } + + ULOGD("%s: channel EOS media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + /* Nothing to do here, the function should be + * overloaded by sub-classes */ + + pthread_mutex_unlock(&mMutex); +} + + +void RawSink::onChannelReconfigure(RawChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + pthread_mutex_lock(&mMutex); + RawVideoMedia *media = nullptr; + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + if (p->channel != channel) { + p++; + continue; + } + media = p->media; + break; + } + + if (media == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("media", ENOENT); + return; + } + + ULOGD("%s: channel reconfigure media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + /* Nothing to do here, the function should be + * overloaded by sub-classes */ + + pthread_mutex_unlock(&mMutex); +} + + +void RawSink::onChannelTimeout(RawChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + pthread_mutex_lock(&mMutex); + RawVideoMedia *media = nullptr; + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + if (p->channel != channel) { + p++; + continue; + } + media = p->media; + break; + } + + if (media == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("media", ENOENT); + return; + } + + ULOGD("%s: channel timeout media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + /* Nothing to do here, the function should be + * overloaded by sub-classes */ + + pthread_mutex_unlock(&mMutex); +} + + +void RawSink::onChannelPhotoTrigger(RawChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + pthread_mutex_lock(&mMutex); + RawVideoMedia *media = nullptr; + std::vector::iterator p = mInputPorts.begin(); + + while (p != mInputPorts.end()) { + if (p->channel != channel) { + p++; + continue; + } + media = p->media; + break; + } + + if (media == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("media", ENOENT); + return; + } + + ULOGD("%s: channel photo_trigger " + "media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + /* Nothing to do here, the function should be + * overloaded by sub-classes */ + + pthread_mutex_unlock(&mMutex); +} + + +void RawSink::onChannelDownstreamEvent(RawChannel *channel, + const struct pomp_msg *event) +{ + ULOGD("%s: channel downstream event %s", + getName().c_str(), + RawChannel::getDownstreamEventStr( + (RawChannel::DownstreamEvent)pomp_msg_get_id(event))); + + switch (pomp_msg_get_id(event)) { + case RawChannel::DownstreamEvent::FLUSH: + onChannelFlush(channel); + break; + case RawChannel::DownstreamEvent::TEARDOWN: + onChannelTeardown(channel); + break; + case RawChannel::DownstreamEvent::SOS: + onChannelSos(channel); + break; + case RawChannel::DownstreamEvent::EOS: + onChannelEos(channel); + break; + case RawChannel::DownstreamEvent::RECONFIGURE: + onChannelReconfigure(channel); + break; + case RawChannel::DownstreamEvent::TIMEOUT: + onChannelTimeout(channel); + break; + case RawChannel::DownstreamEvent::PHOTO_TRIGGER: + onChannelPhotoTrigger(channel); + break; + default: + ULOG_ERRNO("event id %d", ENOSYS, pomp_msg_get_id(event)); + break; + } +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_sink_raw_video.hpp b/libpdraw/src/pdraw_sink_raw_video.hpp index aceaa5a..63055f3 100644 --- a/libpdraw/src/pdraw_sink_raw_video.hpp +++ b/libpdraw/src/pdraw_sink_raw_video.hpp @@ -1,121 +1,121 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Pipeline sink element for raw video - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_SINK_RAW_VIDEO_HPP_ -#define _PDRAW_SINK_RAW_VIDEO_HPP_ - -#include "pdraw_channel_raw_video.hpp" -#include "pdraw_media.hpp" - -#include - -namespace Pdraw { - -class RawSink : public RawChannel::SinkListener { -public: - virtual ~RawSink(void); - - void lock(void); - - void unlock(void); - - virtual std::string &getName(void) = 0; - - int getRawVideoMediaFormatCaps(const struct vdef_raw_format **caps) - { - if (!caps) - return -EINVAL; - *caps = mRawVideoMediaFormatCaps; - return mRawVideoMediaFormatCapsCount; - } - - unsigned int getInputMediaCount(void); - - RawVideoMedia *getInputMedia(unsigned int index); - - RawVideoMedia *findInputMedia(RawVideoMedia *media); - - virtual int addInputMedia(RawVideoMedia *media); - - virtual int removeInputMedia(RawVideoMedia *media); - - RawChannel *getInputChannel(RawVideoMedia *media); - -protected: - struct InputPort { - RawVideoMedia *media; - RawChannel *channel; - }; - - RawSink(unsigned int maxInputMedias, - const struct vdef_raw_format *rawVideoMediaFormatCaps, - int rawVideoMediaFormatCapsCount); - - void setRawVideoMediaFormatCaps(const struct vdef_raw_format *caps, - int count) - { - mRawVideoMediaFormatCaps = caps; - mRawVideoMediaFormatCapsCount = count; - } - - InputPort *getInputPort(RawVideoMedia *media); - - virtual int removeInputMedias(void); - - virtual void onChannelQueue(RawChannel *channel, - struct mbuf_raw_video_frame *frame); - - virtual void onChannelDownstreamEvent(RawChannel *channel, - const struct pomp_msg *event); - - virtual void onChannelFlush(RawChannel *channel); - - virtual void onChannelTeardown(RawChannel *channel); - - virtual void onChannelSos(RawChannel *channel); - - virtual void onChannelEos(RawChannel *channel); - - virtual void onChannelReconfigure(RawChannel *channel); - - virtual void onChannelTimeout(RawChannel *channel); - - virtual void onChannelPhotoTrigger(RawChannel *channel); - - pthread_mutex_t mMutex; - unsigned int mMaxInputMedias; - std::vector mInputPorts; - const struct vdef_raw_format *mRawVideoMediaFormatCaps; - int mRawVideoMediaFormatCapsCount; -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_SINK_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Pipeline sink element for raw video + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_SINK_RAW_VIDEO_HPP_ +#define _PDRAW_SINK_RAW_VIDEO_HPP_ + +#include "pdraw_channel_raw_video.hpp" +#include "pdraw_media.hpp" + +#include + +namespace Pdraw { + +class RawSink : public RawChannel::SinkListener { +public: + virtual ~RawSink(void); + + void lock(void); + + void unlock(void); + + virtual std::string &getName(void) = 0; + + int getRawVideoMediaFormatCaps(const struct vdef_raw_format **caps) + { + if (!caps) + return -EINVAL; + *caps = mRawVideoMediaFormatCaps; + return mRawVideoMediaFormatCapsCount; + } + + unsigned int getInputMediaCount(void); + + RawVideoMedia *getInputMedia(unsigned int index); + + RawVideoMedia *findInputMedia(RawVideoMedia *media); + + virtual int addInputMedia(RawVideoMedia *media); + + virtual int removeInputMedia(RawVideoMedia *media); + + RawChannel *getInputChannel(RawVideoMedia *media); + +protected: + struct InputPort { + RawVideoMedia *media; + RawChannel *channel; + }; + + RawSink(unsigned int maxInputMedias, + const struct vdef_raw_format *rawVideoMediaFormatCaps, + int rawVideoMediaFormatCapsCount); + + void setRawVideoMediaFormatCaps(const struct vdef_raw_format *caps, + int count) + { + mRawVideoMediaFormatCaps = caps; + mRawVideoMediaFormatCapsCount = count; + } + + InputPort *getInputPort(RawVideoMedia *media); + + virtual int removeInputMedias(void); + + virtual void onChannelQueue(RawChannel *channel, + struct mbuf_raw_video_frame *frame); + + virtual void onChannelDownstreamEvent(RawChannel *channel, + const struct pomp_msg *event); + + virtual void onChannelFlush(RawChannel *channel); + + virtual void onChannelTeardown(RawChannel *channel); + + virtual void onChannelSos(RawChannel *channel); + + virtual void onChannelEos(RawChannel *channel); + + virtual void onChannelReconfigure(RawChannel *channel); + + virtual void onChannelTimeout(RawChannel *channel); + + virtual void onChannelPhotoTrigger(RawChannel *channel); + + pthread_mutex_t mMutex; + unsigned int mMaxInputMedias; + std::vector mInputPorts; + const struct vdef_raw_format *mRawVideoMediaFormatCaps; + int mRawVideoMediaFormatCapsCount; +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_SINK_HPP_ */ diff --git a/libpdraw/src/pdraw_source_coded_video.cpp b/libpdraw/src/pdraw_source_coded_video.cpp index 9ea16f5..d39a570 100644 --- a/libpdraw/src/pdraw_source_coded_video.cpp +++ b/libpdraw/src/pdraw_source_coded_video.cpp @@ -1,939 +1,939 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Pipeline source element - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_source_coded_video -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_source_coded_video.hpp" - -#include - -#include - -namespace Pdraw { - - -CodedSource::CodedSource(unsigned int maxOutputMedias, Listener *listener) : - mMaxOutputMedias(maxOutputMedias), mListener(listener) -{ - int res; - pthread_mutexattr_t attr; - bool attr_created = false; - - res = pthread_mutexattr_init(&attr); - if (res != 0) { - ULOG_ERRNO("pthread_mutexattr_init", res); - goto error; - } - attr_created = true; - - res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - if (res != 0) { - ULOG_ERRNO("pthread_mutexattr_settype", res); - goto error; - } - - res = pthread_mutex_init(&mMutex, &attr); - if (res != 0) { - ULOG_ERRNO("pthread_mutex_init", res); - goto error; - } - - pthread_mutexattr_destroy(&attr); - return; - -error: - if (attr_created) - pthread_mutexattr_destroy(&attr); -} - - -CodedSource::~CodedSource(void) -{ - int ret = removeOutputPorts(); - if (ret < 0) - ULOG_ERRNO("removeOutputPorts", -ret); - - unsigned int count = getOutputMediaCount(); - if (count > 0) { - ULOGW("not all output ports have been removed! (count=%d)", - count); - } - - pthread_mutex_destroy(&mMutex); -} - - -void CodedSource::lock(void) -{ - pthread_mutex_lock(&mMutex); -} - - -void CodedSource::unlock(void) -{ - pthread_mutex_unlock(&mMutex); -} - - -unsigned int CodedSource::getOutputMediaCount(void) -{ - pthread_mutex_lock(&mMutex); - unsigned int ret = mOutputPorts.size(); - pthread_mutex_unlock(&mMutex); - return ret; -} - - -CodedVideoMedia *CodedSource::getOutputMedia(unsigned int index) -{ - pthread_mutex_lock(&mMutex); - CodedVideoMedia *ret = (index < mOutputPorts.size()) - ? mOutputPorts.at(index).media - : nullptr; - pthread_mutex_unlock(&mMutex); - return ret; -} - - -CodedVideoMedia *CodedSource::findOutputMedia(CodedVideoMedia *media) -{ - pthread_mutex_lock(&mMutex); - CodedVideoMedia *ret = nullptr; - std::vector::iterator p = mOutputPorts.begin(); - - while (p != mOutputPorts.end()) { - if (p->media != media) { - p++; - continue; - } - ret = p->media; - break; - } - pthread_mutex_unlock(&mMutex); - return ret; -} - - -CodedVideoMedia *CodedSource::getOutputMediaFromChannel(void *key) -{ - pthread_mutex_lock(&mMutex); - CodedVideoMedia *ret = nullptr; - std::vector::iterator p = mOutputPorts.begin(); - - while (p != mOutputPorts.end()) { - std::vector::iterator c = p->channels.begin(); - - while (c != p->channels.end()) { - if ((*c)->getKey() != key) { - c++; - continue; - } - ret = p->media; - break; - } - - if (ret == nullptr) { - p++; - continue; - } - break; - } - - pthread_mutex_unlock(&mMutex); - return ret; -} - - -CodedSource::OutputPort *CodedSource::getOutputPort(CodedVideoMedia *media) -{ - if (media == nullptr) { - ULOG_ERRNO("media", EINVAL); - return nullptr; - } - - pthread_mutex_lock(&mMutex); - OutputPort *ret = nullptr; - std::vector::iterator p = mOutputPorts.begin(); - - while (p != mOutputPorts.end()) { - if (p->media != media) { - p++; - continue; - } - ret = &(*p); - break; - } - - pthread_mutex_unlock(&mMutex); - return ret; -} - - -int CodedSource::addOutputPort(CodedVideoMedia *media) -{ - if (media == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - - if (mOutputPorts.size() >= mMaxOutputMedias) { - pthread_mutex_unlock(&mMutex); - return -ENOBUFS; - } - - OutputPort port; - port.media = media; - mOutputPorts.push_back(port); - - pthread_mutex_unlock(&mMutex); - - ULOGI("%s: add port for media name=%s", - getName().c_str(), - media->getName().c_str()); - - return 0; -} - - -int CodedSource::removeOutputPort(CodedVideoMedia *media) -{ - if (media == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - bool found = false; - std::vector::iterator p = mOutputPorts.begin(); - - while (p != mOutputPorts.end()) { - if (p->media != media) { - p++; - continue; - } - found = true; - - unsigned int count = p->channels.size(); - if (count > 0) { - pthread_mutex_unlock(&mMutex); - ULOGW("not all output channels have been removed! " - "(count=%d)", - count); - return -EBUSY; - } - p->media = nullptr; - destroyOutputPortMemoryPool(&(*p)); - mOutputPorts.erase(p); - break; - } - - pthread_mutex_unlock(&mMutex); - if (!found) - return -ENOENT; - - ULOGI("%s: delete port for media name=%s", - getName().c_str(), - media->getName().c_str()); - - return 0; -} - - -int CodedSource::removeOutputPorts(void) -{ - pthread_mutex_lock(&mMutex); - std::vector::iterator p = mOutputPorts.begin(); - - while (p != mOutputPorts.end()) { - if (mListener) - mListener->onOutputMediaRemoved(this, p->media); - - unsigned int count = p->channels.size(); - if (count > 0) { - pthread_mutex_unlock(&mMutex); - ULOGW("not all output channels have been removed! " - "(count=%d)", - count); - return -EBUSY; - } - ULOGI("%s: delete port for media name=%s", - getName().c_str(), - p->media->getName().c_str()); - /* Note: unlike removeOutputPort(), here the media is deleted */ - delete p->media; - p->media = nullptr; - destroyOutputPortMemoryPool(&(*p)); - p++; - } - - mOutputPorts.clear(); - - pthread_mutex_unlock(&mMutex); - return 0; -} - - -int CodedSource::createOutputPortMemoryPool(CodedVideoMedia *media, - unsigned int count, - size_t capacity) -{ - int ret; - - if (media == nullptr) - return -EINVAL; - if (count == 0) - return -EINVAL; - if (capacity == 0) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - OutputPort *port = nullptr; - std::vector::iterator p = mOutputPorts.begin(); - - while (p != mOutputPorts.end()) { - if (p->media != media) { - p++; - continue; - } - port = &(*p); - break; - } - - if (port == nullptr) { - pthread_mutex_unlock(&mMutex); - return -ENOENT; - } - - ret = mbuf_pool_new(mbuf_mem_generic_impl, - capacity, - count, - MBUF_POOL_NO_GROW, - 0, - getName().c_str(), - &port->pool); - if (ret < 0) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("mbuf_pool_new", -ret); - return ret; - } - - pthread_mutex_unlock(&mMutex); - return 0; -} - - -int CodedSource::destroyOutputPortMemoryPool(OutputPort *port) -{ - int ret = 0; - - if (port == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - - if (port->pool == nullptr) { - pthread_mutex_unlock(&mMutex); - return 0; - } - - if (!port->sharedPool) { - ret = mbuf_pool_destroy(port->pool); - if (ret < 0) - ULOG_ERRNO("mbuf_pool_destroy", -ret); - } - - port->pool = nullptr; - pthread_mutex_unlock(&mMutex); - return ret; -} - - -int CodedSource::destroyOutputPortMemoryPool(CodedVideoMedia *media) -{ - if (media == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - OutputPort *port = nullptr; - std::vector::iterator p = mOutputPorts.begin(); - - while (p != mOutputPorts.end()) { - if (p->media != media) { - p++; - continue; - } - port = &(*p); - break; - } - - if (port == nullptr) { - pthread_mutex_unlock(&mMutex); - return -ENOENT; - } - - int ret = destroyOutputPortMemoryPool(port); - pthread_mutex_unlock(&mMutex); - return ret; -} - - -int CodedSource::getOutputMemory(CodedVideoMedia **videoMedias, - unsigned int nbVideoMedias, - struct mbuf_mem **mem, - unsigned int *defaultMediaIndex) -{ - int ret = 0; - unsigned int outputChannelCount = 0; - unsigned int firstUsedMediaIndex = UINT_MAX, firstUsedMediaCount = 0; - CodedChannel *channel; - OutputPort *port; - struct mbuf_pool *pool = nullptr, *extPool = nullptr; - bool externalPool = false; - - if (videoMedias == nullptr || nbVideoMedias == 0) - return -EINVAL; - if (mem == nullptr) - return -EINVAL; - if (defaultMediaIndex == nullptr) - return -EINVAL; - - for (unsigned int i = 0; i < nbVideoMedias; i++) { - if ((videoMedias[i]->format.encoding != VDEF_ENCODING_H264) && - (videoMedias[i]->format.encoding != VDEF_ENCODING_H265)) - return -EINVAL; - } - - pthread_mutex_lock(&mMutex); - - for (unsigned int i = 0; i < nbVideoMedias; i++) { - outputChannelCount = getOutputChannelCount(videoMedias[i]); - if (outputChannelCount > 0 && firstUsedMediaIndex == UINT_MAX) { - firstUsedMediaCount = outputChannelCount; - firstUsedMediaIndex = i; - channel = getOutputChannel(videoMedias[i], - (unsigned int)0); - if (channel == nullptr) - ULOGW("invalid channel"); - else - extPool = channel->getPool(); - } - } - - /* If the first used media have a single output channel with an - * input pool, use it */ - if (firstUsedMediaCount == 1 && extPool != nullptr) { - pool = extPool; - externalPool = true; - } - -retry_internal_pool: - /* Otherwise, use the pool of the prefered media */ - if (pool == nullptr && firstUsedMediaIndex != UINT_MAX) { - port = getOutputPort(videoMedias[firstUsedMediaIndex]); - if (port != nullptr) - pool = port->pool; - } - - /* If still no pool, pick one from any media */ - if (pool == nullptr) { - for (unsigned int i = 0; i < nbVideoMedias; i++) { - port = getOutputPort(videoMedias[i]); - if (port != nullptr && port->pool != nullptr) { - pool = port->pool; - firstUsedMediaIndex = i; - break; - } - } - } - - /* If still no pool, return an error */ - if (pool == nullptr) { - ret = -EPROTO; - ULOGE("no pool available"); - goto exit; - } - - ret = mbuf_pool_get(pool, mem); - if ((ret < 0) || (*mem == nullptr)) { - if (ret != -EAGAIN) - ULOG_ERRNO("mbuf_pool_get:source", -ret); - else if (externalPool) { - ULOGD("no memory available in the external " - "pool, fall back to the source " - "output port pool"); - externalPool = false; - pool = nullptr; - goto retry_internal_pool; - } - } - - -exit: - *defaultMediaIndex = firstUsedMediaIndex; - pthread_mutex_unlock(&mMutex); - return ret; -} - - -int CodedSource::copyOutputFrame(CodedVideoMedia *srcMedia, - struct mbuf_coded_video_frame *srcFrame, - CodedVideoMedia *dstMedia, - struct mbuf_coded_video_frame **dstFrame) -{ - int ret; - unsigned int dummy; - struct mbuf_mem *dstMem = nullptr; - uint8_t *dstData = nullptr; - size_t dstLen; - struct vdef_coded_frame info; - - if (srcMedia == nullptr || srcFrame == nullptr || dstMedia == nullptr || - dstFrame == nullptr) - return -EINVAL; - - /* Encoding must stay the same */ - if (srcMedia->format.encoding != dstMedia->format.encoding) - return -EINVAL; - - ret = getOutputMemory(&dstMedia, 1, &dstMem, &dummy); - if (ret < 0) { - ULOG_ERRNO("getOutputMemory", -ret); - return ret; - } - - ret = mbuf_coded_video_frame_copy(srcFrame, dstMem, dstFrame); - if (ret < 0) { - ULOG_ERRNO("mbuf_coded_video_frame_copy", -ret); - goto exit; - } - - /* Update dstFrame format */ - ret = mbuf_coded_video_frame_get_frame_info(*dstFrame, &info); - if (ret != 0) { - ULOG_ERRNO("mbuf_coded_video_frame_get_frame_info", -ret); - goto exit; - } - info.format = dstMedia->format; - ret = mbuf_coded_video_frame_set_frame_info(*dstFrame, &info); - if (ret != 0) { - ULOG_ERRNO("mbuf_coded_video_frame_set_frame_info", -ret); - goto exit; - } - - ret = mbuf_coded_video_frame_finalize(*dstFrame); - if (ret < 0) { - ULOG_ERRNO("mbuf_coded_video_frame_finalize", -ret); - goto exit; - } - - /* If we have the same format, nothing more to do */ - if (vdef_coded_format_cmp(&srcMedia->format, &dstMedia->format)) - goto exit; - - /* We cannot convert to/from raw_nalus */ - if (srcMedia->format.data_format == VDEF_CODED_DATA_FORMAT_RAW_NALU || - dstMedia->format.data_format == VDEF_CODED_DATA_FORMAT_RAW_NALU) { - ULOGE("conversion from/to raw_nalu not supported"); - ret = -ENOSYS; - goto exit; - } - - /* Get the actual length of the frame */ - ret = mbuf_coded_video_frame_get_rw_packed_buffer( - *dstFrame, (void **)&dstData, &dstLen); - if (ret != 0) { - ULOG_ERRNO("mbuf_coded_video_frame_get_rw_packed_buffer", -ret); - goto exit; - } - - if (srcMedia->format.data_format == VDEF_CODED_DATA_FORMAT_AVCC && - dstMedia->format.data_format == - VDEF_CODED_DATA_FORMAT_BYTE_STREAM) { - switch (srcMedia->format.encoding) { - case VDEF_ENCODING_H264: - ret = h264_avcc_to_byte_stream(dstData, dstLen); - if (ret < 0) - ULOG_ERRNO("h264_avcc_to_byte_stream", -ret); - break; - case VDEF_ENCODING_H265: - ret = h265_hvcc_to_byte_stream(dstData, dstLen); - if (ret < 0) - ULOG_ERRNO("h265_hvcc_to_byte_stream", -ret); - break; - default: - break; - } - } else if (srcMedia->format.data_format == - VDEF_CODED_DATA_FORMAT_BYTE_STREAM && - dstMedia->format.data_format == - VDEF_CODED_DATA_FORMAT_AVCC) { - switch (srcMedia->format.encoding) { - case VDEF_ENCODING_H264: - ret = h264_byte_stream_to_avcc(dstData, dstLen); - if (ret < 0) - ULOG_ERRNO("h264_byte_stream_to_avcc", -ret); - break; - case VDEF_ENCODING_H265: - ret = h265_byte_stream_to_hvcc(dstData, dstLen); - if (ret < 0) - ULOG_ERRNO("h265_byte_stream_to_hvcc", -ret); - break; - default: - break; - } - } else { - ULOGE("bad conversion from " VDEF_CODED_FORMAT_TO_STR_FMT - " to " VDEF_CODED_FORMAT_TO_STR_FMT, - VDEF_CODED_FORMAT_TO_STR_ARG(&srcMedia->format), - VDEF_CODED_FORMAT_TO_STR_ARG(&dstMedia->format)); - ret = -EINVAL; - } - -exit: - if (dstData != nullptr) - mbuf_coded_video_frame_release_rw_packed_buffer(*dstFrame, - dstData); - if (dstMem != nullptr) - mbuf_mem_unref(dstMem); - if (ret != 0 && *dstFrame != nullptr) { - mbuf_coded_video_frame_unref(*dstFrame); - *dstFrame = nullptr; - } - return ret; -} - -unsigned int CodedSource::getOutputChannelCount(CodedVideoMedia *media) -{ - if (media == nullptr) { - ULOG_ERRNO("media", EINVAL); - return 0; - } - - pthread_mutex_lock(&mMutex); - OutputPort *port = getOutputPort(media); - if (port == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("port", ENOENT); - return 0; - } - - unsigned int ret = port->channels.size(); - pthread_mutex_unlock(&mMutex); - return ret; -} - - -CodedChannel *CodedSource::getOutputChannel(CodedVideoMedia *media, - unsigned int index) -{ - if (media == nullptr) { - ULOG_ERRNO("media", EINVAL); - return nullptr; - } - - pthread_mutex_lock(&mMutex); - OutputPort *port = getOutputPort(media); - if (port == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("port", ENOENT); - return nullptr; - } - if (index >= port->channels.size()) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("index", ENOENT); - return nullptr; - } - - CodedChannel *ret = port->channels.at(index); - pthread_mutex_unlock(&mMutex); - - return ret; -} - - -CodedChannel *CodedSource::getOutputChannel(CodedVideoMedia *media, void *key) -{ - if (media == nullptr) { - ULOG_ERRNO("media", EINVAL); - return nullptr; - } - if (key == nullptr) { - ULOG_ERRNO("key", EINVAL); - return nullptr; - } - - pthread_mutex_lock(&mMutex); - OutputPort *port = getOutputPort(media); - if (port == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("port", ENOENT); - return nullptr; - } - - CodedChannel *ret = nullptr; - std::vector::iterator c = port->channels.begin(); - - while (c != port->channels.end()) { - if ((*c)->getKey() != key) { - c++; - continue; - } - ret = *c; - break; - } - - pthread_mutex_unlock(&mMutex); - return ret; -} - - -int CodedSource::addOutputChannel(CodedVideoMedia *media, CodedChannel *channel) -{ - if (media == nullptr) - return -EINVAL; - if (channel == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - CodedChannel *c = getOutputChannel(media, channel->getKey()); - if (c != nullptr) { - pthread_mutex_unlock(&mMutex); - return -EEXIST; - } - OutputPort *port = getOutputPort(media); - if (port == nullptr) { - pthread_mutex_unlock(&mMutex); - return -ENOENT; - } - - channel->setSourceListener(this); - port->channels.push_back(channel); - pthread_mutex_unlock(&mMutex); - - ULOGI("%s: link media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - channel->getKey()); - return 0; -} - - -int CodedSource::removeOutputChannel(CodedVideoMedia *media, void *key) -{ - if (media == nullptr) - return -EINVAL; - if (key == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - OutputPort *port = getOutputPort(media); - if (port == nullptr) { - pthread_mutex_unlock(&mMutex); - return -ENOENT; - } - - bool found = false; - std::vector::iterator c = port->channels.begin(); - - while (c != port->channels.end()) { - if ((*c)->getKey() != key) { - c++; - continue; - } - found = true; - (*c)->setSourceListener(nullptr); - port->channels.erase(c); - break; - } - - pthread_mutex_unlock(&mMutex); - if (!found) - return -ENOENT; - - ULOGI("%s: unlink media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - key); - return 0; -} - - -int CodedSource::sendDownstreamEvent(CodedVideoMedia *media, - CodedChannel::DownstreamEvent event) -{ - int ret; - unsigned int outputChannelCount, i; - CodedChannel *channel; - - if (media == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - - /* Send the event downstream */ - outputChannelCount = getOutputChannelCount(media); - for (i = 0; i < outputChannelCount; i++) { - channel = getOutputChannel(media, i); - if (channel == nullptr) { - ULOGW("invalid channel"); - continue; - } - ret = channel->sendDownstreamEvent(event); - if (ret < 0) - ULOG_ERRNO("channel->sendDownstreamEvent", -ret); - } - - pthread_mutex_unlock(&mMutex); - - return 0; -} - - -void CodedSource::onChannelUnlink(CodedChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); - if (media == nullptr) { - ULOGE("media not found"); - return; - } - - int ret = removeOutputChannel(media, channel->getKey()); - if (ret < 0) - ULOG_ERRNO("removeOutputChannel", -ret); -} - - -void CodedSource::onChannelFlushed(CodedChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); - if (media == nullptr) { - ULOGE("media not found"); - return; - } - ULOGD("%s: channel flushed media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - /* Nothing to do here, the function should be - * overloaded by sub-classes */ -} - - -void CodedSource::onChannelResync(CodedChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); - if (media == nullptr) { - ULOGE("media not found"); - return; - } - ULOGD("%s: channel resync media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - /* Nothing to do here, the function should be - * overloaded by sub-classes */ -} - - -void CodedSource::onChannelVideoPresStats(CodedChannel *channel, - VideoPresStats *stats) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); - if (media == nullptr) { - ULOGE("media not found"); - return; - } - ULOGD("%s: channel video stats media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - /* Nothing to do here, the function should be - * overloaded by sub-classes */ -} - - -void CodedSource::onChannelUpstreamEvent(CodedChannel *channel, - const struct pomp_msg *event) -{ - VideoPresStats stats; - int err; - - ULOGD("%s: channel upstream event %s", - getName().c_str(), - CodedChannel::getUpstreamEventStr( - (CodedChannel::UpstreamEvent)pomp_msg_get_id(event))); - - switch (pomp_msg_get_id(event)) { - case CodedChannel::UpstreamEvent::UNLINK: - onChannelUnlink(channel); - break; - case CodedChannel::UpstreamEvent::FLUSHED: - onChannelFlushed(channel); - break; - case CodedChannel::UpstreamEvent::RESYNC: - onChannelResync(channel); - break; - case CodedChannel::UpstreamEvent::VIDEO_PRES_STATS: - err = stats.readMsg(event); - if (err < 0) - ULOG_ERRNO("stats.readMsg", -err); - else - onChannelVideoPresStats(channel, &stats); - break; - default: - ULOG_ERRNO("event id %d", ENOSYS, pomp_msg_get_id(event)); - break; - } -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Pipeline source element + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_source_coded_video +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_source_coded_video.hpp" + +#include + +#include + +namespace Pdraw { + + +CodedSource::CodedSource(unsigned int maxOutputMedias, Listener *listener) : + mMaxOutputMedias(maxOutputMedias), mListener(listener) +{ + int res; + pthread_mutexattr_t attr; + bool attr_created = false; + + res = pthread_mutexattr_init(&attr); + if (res != 0) { + ULOG_ERRNO("pthread_mutexattr_init", res); + goto error; + } + attr_created = true; + + res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + if (res != 0) { + ULOG_ERRNO("pthread_mutexattr_settype", res); + goto error; + } + + res = pthread_mutex_init(&mMutex, &attr); + if (res != 0) { + ULOG_ERRNO("pthread_mutex_init", res); + goto error; + } + + pthread_mutexattr_destroy(&attr); + return; + +error: + if (attr_created) + pthread_mutexattr_destroy(&attr); +} + + +CodedSource::~CodedSource(void) +{ + int ret = removeOutputPorts(); + if (ret < 0) + ULOG_ERRNO("removeOutputPorts", -ret); + + unsigned int count = getOutputMediaCount(); + if (count > 0) { + ULOGW("not all output ports have been removed! (count=%d)", + count); + } + + pthread_mutex_destroy(&mMutex); +} + + +void CodedSource::lock(void) +{ + pthread_mutex_lock(&mMutex); +} + + +void CodedSource::unlock(void) +{ + pthread_mutex_unlock(&mMutex); +} + + +unsigned int CodedSource::getOutputMediaCount(void) +{ + pthread_mutex_lock(&mMutex); + unsigned int ret = mOutputPorts.size(); + pthread_mutex_unlock(&mMutex); + return ret; +} + + +CodedVideoMedia *CodedSource::getOutputMedia(unsigned int index) +{ + pthread_mutex_lock(&mMutex); + CodedVideoMedia *ret = (index < mOutputPorts.size()) + ? mOutputPorts.at(index).media + : nullptr; + pthread_mutex_unlock(&mMutex); + return ret; +} + + +CodedVideoMedia *CodedSource::findOutputMedia(CodedVideoMedia *media) +{ + pthread_mutex_lock(&mMutex); + CodedVideoMedia *ret = nullptr; + std::vector::iterator p = mOutputPorts.begin(); + + while (p != mOutputPorts.end()) { + if (p->media != media) { + p++; + continue; + } + ret = p->media; + break; + } + pthread_mutex_unlock(&mMutex); + return ret; +} + + +CodedVideoMedia *CodedSource::getOutputMediaFromChannel(void *key) +{ + pthread_mutex_lock(&mMutex); + CodedVideoMedia *ret = nullptr; + std::vector::iterator p = mOutputPorts.begin(); + + while (p != mOutputPorts.end()) { + std::vector::iterator c = p->channels.begin(); + + while (c != p->channels.end()) { + if ((*c)->getKey() != key) { + c++; + continue; + } + ret = p->media; + break; + } + + if (ret == nullptr) { + p++; + continue; + } + break; + } + + pthread_mutex_unlock(&mMutex); + return ret; +} + + +CodedSource::OutputPort *CodedSource::getOutputPort(CodedVideoMedia *media) +{ + if (media == nullptr) { + ULOG_ERRNO("media", EINVAL); + return nullptr; + } + + pthread_mutex_lock(&mMutex); + OutputPort *ret = nullptr; + std::vector::iterator p = mOutputPorts.begin(); + + while (p != mOutputPorts.end()) { + if (p->media != media) { + p++; + continue; + } + ret = &(*p); + break; + } + + pthread_mutex_unlock(&mMutex); + return ret; +} + + +int CodedSource::addOutputPort(CodedVideoMedia *media) +{ + if (media == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + + if (mOutputPorts.size() >= mMaxOutputMedias) { + pthread_mutex_unlock(&mMutex); + return -ENOBUFS; + } + + OutputPort port; + port.media = media; + mOutputPorts.push_back(port); + + pthread_mutex_unlock(&mMutex); + + ULOGI("%s: add port for media name=%s", + getName().c_str(), + media->getName().c_str()); + + return 0; +} + + +int CodedSource::removeOutputPort(CodedVideoMedia *media) +{ + if (media == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + bool found = false; + std::vector::iterator p = mOutputPorts.begin(); + + while (p != mOutputPorts.end()) { + if (p->media != media) { + p++; + continue; + } + found = true; + + unsigned int count = p->channels.size(); + if (count > 0) { + pthread_mutex_unlock(&mMutex); + ULOGW("not all output channels have been removed! " + "(count=%d)", + count); + return -EBUSY; + } + p->media = nullptr; + destroyOutputPortMemoryPool(&(*p)); + mOutputPorts.erase(p); + break; + } + + pthread_mutex_unlock(&mMutex); + if (!found) + return -ENOENT; + + ULOGI("%s: delete port for media name=%s", + getName().c_str(), + media->getName().c_str()); + + return 0; +} + + +int CodedSource::removeOutputPorts(void) +{ + pthread_mutex_lock(&mMutex); + std::vector::iterator p = mOutputPorts.begin(); + + while (p != mOutputPorts.end()) { + if (mListener) + mListener->onOutputMediaRemoved(this, p->media); + + unsigned int count = p->channels.size(); + if (count > 0) { + pthread_mutex_unlock(&mMutex); + ULOGW("not all output channels have been removed! " + "(count=%d)", + count); + return -EBUSY; + } + ULOGI("%s: delete port for media name=%s", + getName().c_str(), + p->media->getName().c_str()); + /* Note: unlike removeOutputPort(), here the media is deleted */ + delete p->media; + p->media = nullptr; + destroyOutputPortMemoryPool(&(*p)); + p++; + } + + mOutputPorts.clear(); + + pthread_mutex_unlock(&mMutex); + return 0; +} + + +int CodedSource::createOutputPortMemoryPool(CodedVideoMedia *media, + unsigned int count, + size_t capacity) +{ + int ret; + + if (media == nullptr) + return -EINVAL; + if (count == 0) + return -EINVAL; + if (capacity == 0) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + OutputPort *port = nullptr; + std::vector::iterator p = mOutputPorts.begin(); + + while (p != mOutputPorts.end()) { + if (p->media != media) { + p++; + continue; + } + port = &(*p); + break; + } + + if (port == nullptr) { + pthread_mutex_unlock(&mMutex); + return -ENOENT; + } + + ret = mbuf_pool_new(mbuf_mem_generic_impl, + capacity, + count, + MBUF_POOL_NO_GROW, + 0, + getName().c_str(), + &port->pool); + if (ret < 0) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("mbuf_pool_new", -ret); + return ret; + } + + pthread_mutex_unlock(&mMutex); + return 0; +} + + +int CodedSource::destroyOutputPortMemoryPool(OutputPort *port) +{ + int ret = 0; + + if (port == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + + if (port->pool == nullptr) { + pthread_mutex_unlock(&mMutex); + return 0; + } + + if (!port->sharedPool) { + ret = mbuf_pool_destroy(port->pool); + if (ret < 0) + ULOG_ERRNO("mbuf_pool_destroy", -ret); + } + + port->pool = nullptr; + pthread_mutex_unlock(&mMutex); + return ret; +} + + +int CodedSource::destroyOutputPortMemoryPool(CodedVideoMedia *media) +{ + if (media == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + OutputPort *port = nullptr; + std::vector::iterator p = mOutputPorts.begin(); + + while (p != mOutputPorts.end()) { + if (p->media != media) { + p++; + continue; + } + port = &(*p); + break; + } + + if (port == nullptr) { + pthread_mutex_unlock(&mMutex); + return -ENOENT; + } + + int ret = destroyOutputPortMemoryPool(port); + pthread_mutex_unlock(&mMutex); + return ret; +} + + +int CodedSource::getOutputMemory(CodedVideoMedia **videoMedias, + unsigned int nbVideoMedias, + struct mbuf_mem **mem, + unsigned int *defaultMediaIndex) +{ + int ret = 0; + unsigned int outputChannelCount = 0; + unsigned int firstUsedMediaIndex = UINT_MAX, firstUsedMediaCount = 0; + CodedChannel *channel; + OutputPort *port; + struct mbuf_pool *pool = nullptr, *extPool = nullptr; + bool externalPool = false; + + if (videoMedias == nullptr || nbVideoMedias == 0) + return -EINVAL; + if (mem == nullptr) + return -EINVAL; + if (defaultMediaIndex == nullptr) + return -EINVAL; + + for (unsigned int i = 0; i < nbVideoMedias; i++) { + if ((videoMedias[i]->format.encoding != VDEF_ENCODING_H264) && + (videoMedias[i]->format.encoding != VDEF_ENCODING_H265)) + return -EINVAL; + } + + pthread_mutex_lock(&mMutex); + + for (unsigned int i = 0; i < nbVideoMedias; i++) { + outputChannelCount = getOutputChannelCount(videoMedias[i]); + if (outputChannelCount > 0 && firstUsedMediaIndex == UINT_MAX) { + firstUsedMediaCount = outputChannelCount; + firstUsedMediaIndex = i; + channel = getOutputChannel(videoMedias[i], + (unsigned int)0); + if (channel == nullptr) + ULOGW("invalid channel"); + else + extPool = channel->getPool(); + } + } + + /* If the first used media have a single output channel with an + * input pool, use it */ + if (firstUsedMediaCount == 1 && extPool != nullptr) { + pool = extPool; + externalPool = true; + } + +retry_internal_pool: + /* Otherwise, use the pool of the prefered media */ + if (pool == nullptr && firstUsedMediaIndex != UINT_MAX) { + port = getOutputPort(videoMedias[firstUsedMediaIndex]); + if (port != nullptr) + pool = port->pool; + } + + /* If still no pool, pick one from any media */ + if (pool == nullptr) { + for (unsigned int i = 0; i < nbVideoMedias; i++) { + port = getOutputPort(videoMedias[i]); + if (port != nullptr && port->pool != nullptr) { + pool = port->pool; + firstUsedMediaIndex = i; + break; + } + } + } + + /* If still no pool, return an error */ + if (pool == nullptr) { + ret = -EPROTO; + ULOGE("no pool available"); + goto exit; + } + + ret = mbuf_pool_get(pool, mem); + if ((ret < 0) || (*mem == nullptr)) { + if (ret != -EAGAIN) + ULOG_ERRNO("mbuf_pool_get:source", -ret); + else if (externalPool) { + ULOGD("no memory available in the external " + "pool, fall back to the source " + "output port pool"); + externalPool = false; + pool = nullptr; + goto retry_internal_pool; + } + } + + +exit: + *defaultMediaIndex = firstUsedMediaIndex; + pthread_mutex_unlock(&mMutex); + return ret; +} + + +int CodedSource::copyOutputFrame(CodedVideoMedia *srcMedia, + struct mbuf_coded_video_frame *srcFrame, + CodedVideoMedia *dstMedia, + struct mbuf_coded_video_frame **dstFrame) +{ + int ret; + unsigned int dummy; + struct mbuf_mem *dstMem = nullptr; + uint8_t *dstData = nullptr; + size_t dstLen; + struct vdef_coded_frame info; + + if (srcMedia == nullptr || srcFrame == nullptr || dstMedia == nullptr || + dstFrame == nullptr) + return -EINVAL; + + /* Encoding must stay the same */ + if (srcMedia->format.encoding != dstMedia->format.encoding) + return -EINVAL; + + ret = getOutputMemory(&dstMedia, 1, &dstMem, &dummy); + if (ret < 0) { + ULOG_ERRNO("getOutputMemory", -ret); + return ret; + } + + ret = mbuf_coded_video_frame_copy(srcFrame, dstMem, dstFrame); + if (ret < 0) { + ULOG_ERRNO("mbuf_coded_video_frame_copy", -ret); + goto exit; + } + + /* Update dstFrame format */ + ret = mbuf_coded_video_frame_get_frame_info(*dstFrame, &info); + if (ret != 0) { + ULOG_ERRNO("mbuf_coded_video_frame_get_frame_info", -ret); + goto exit; + } + info.format = dstMedia->format; + ret = mbuf_coded_video_frame_set_frame_info(*dstFrame, &info); + if (ret != 0) { + ULOG_ERRNO("mbuf_coded_video_frame_set_frame_info", -ret); + goto exit; + } + + ret = mbuf_coded_video_frame_finalize(*dstFrame); + if (ret < 0) { + ULOG_ERRNO("mbuf_coded_video_frame_finalize", -ret); + goto exit; + } + + /* If we have the same format, nothing more to do */ + if (vdef_coded_format_cmp(&srcMedia->format, &dstMedia->format)) + goto exit; + + /* We cannot convert to/from raw_nalus */ + if (srcMedia->format.data_format == VDEF_CODED_DATA_FORMAT_RAW_NALU || + dstMedia->format.data_format == VDEF_CODED_DATA_FORMAT_RAW_NALU) { + ULOGE("conversion from/to raw_nalu not supported"); + ret = -ENOSYS; + goto exit; + } + + /* Get the actual length of the frame */ + ret = mbuf_coded_video_frame_get_rw_packed_buffer( + *dstFrame, (void **)&dstData, &dstLen); + if (ret != 0) { + ULOG_ERRNO("mbuf_coded_video_frame_get_rw_packed_buffer", -ret); + goto exit; + } + + if (srcMedia->format.data_format == VDEF_CODED_DATA_FORMAT_AVCC && + dstMedia->format.data_format == + VDEF_CODED_DATA_FORMAT_BYTE_STREAM) { + switch (srcMedia->format.encoding) { + case VDEF_ENCODING_H264: + ret = h264_avcc_to_byte_stream(dstData, dstLen); + if (ret < 0) + ULOG_ERRNO("h264_avcc_to_byte_stream", -ret); + break; + case VDEF_ENCODING_H265: + ret = h265_hvcc_to_byte_stream(dstData, dstLen); + if (ret < 0) + ULOG_ERRNO("h265_hvcc_to_byte_stream", -ret); + break; + default: + break; + } + } else if (srcMedia->format.data_format == + VDEF_CODED_DATA_FORMAT_BYTE_STREAM && + dstMedia->format.data_format == + VDEF_CODED_DATA_FORMAT_AVCC) { + switch (srcMedia->format.encoding) { + case VDEF_ENCODING_H264: + ret = h264_byte_stream_to_avcc(dstData, dstLen); + if (ret < 0) + ULOG_ERRNO("h264_byte_stream_to_avcc", -ret); + break; + case VDEF_ENCODING_H265: + ret = h265_byte_stream_to_hvcc(dstData, dstLen); + if (ret < 0) + ULOG_ERRNO("h265_byte_stream_to_hvcc", -ret); + break; + default: + break; + } + } else { + ULOGE("bad conversion from " VDEF_CODED_FORMAT_TO_STR_FMT + " to " VDEF_CODED_FORMAT_TO_STR_FMT, + VDEF_CODED_FORMAT_TO_STR_ARG(&srcMedia->format), + VDEF_CODED_FORMAT_TO_STR_ARG(&dstMedia->format)); + ret = -EINVAL; + } + +exit: + if (dstData != nullptr) + mbuf_coded_video_frame_release_rw_packed_buffer(*dstFrame, + dstData); + if (dstMem != nullptr) + mbuf_mem_unref(dstMem); + if (ret != 0 && *dstFrame != nullptr) { + mbuf_coded_video_frame_unref(*dstFrame); + *dstFrame = nullptr; + } + return ret; +} + +unsigned int CodedSource::getOutputChannelCount(CodedVideoMedia *media) +{ + if (media == nullptr) { + ULOG_ERRNO("media", EINVAL); + return 0; + } + + pthread_mutex_lock(&mMutex); + OutputPort *port = getOutputPort(media); + if (port == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("port", ENOENT); + return 0; + } + + unsigned int ret = port->channels.size(); + pthread_mutex_unlock(&mMutex); + return ret; +} + + +CodedChannel *CodedSource::getOutputChannel(CodedVideoMedia *media, + unsigned int index) +{ + if (media == nullptr) { + ULOG_ERRNO("media", EINVAL); + return nullptr; + } + + pthread_mutex_lock(&mMutex); + OutputPort *port = getOutputPort(media); + if (port == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("port", ENOENT); + return nullptr; + } + if (index >= port->channels.size()) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("index", ENOENT); + return nullptr; + } + + CodedChannel *ret = port->channels.at(index); + pthread_mutex_unlock(&mMutex); + + return ret; +} + + +CodedChannel *CodedSource::getOutputChannel(CodedVideoMedia *media, void *key) +{ + if (media == nullptr) { + ULOG_ERRNO("media", EINVAL); + return nullptr; + } + if (key == nullptr) { + ULOG_ERRNO("key", EINVAL); + return nullptr; + } + + pthread_mutex_lock(&mMutex); + OutputPort *port = getOutputPort(media); + if (port == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("port", ENOENT); + return nullptr; + } + + CodedChannel *ret = nullptr; + std::vector::iterator c = port->channels.begin(); + + while (c != port->channels.end()) { + if ((*c)->getKey() != key) { + c++; + continue; + } + ret = *c; + break; + } + + pthread_mutex_unlock(&mMutex); + return ret; +} + + +int CodedSource::addOutputChannel(CodedVideoMedia *media, CodedChannel *channel) +{ + if (media == nullptr) + return -EINVAL; + if (channel == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + CodedChannel *c = getOutputChannel(media, channel->getKey()); + if (c != nullptr) { + pthread_mutex_unlock(&mMutex); + return -EEXIST; + } + OutputPort *port = getOutputPort(media); + if (port == nullptr) { + pthread_mutex_unlock(&mMutex); + return -ENOENT; + } + + channel->setSourceListener(this); + port->channels.push_back(channel); + pthread_mutex_unlock(&mMutex); + + ULOGI("%s: link media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + channel->getKey()); + return 0; +} + + +int CodedSource::removeOutputChannel(CodedVideoMedia *media, void *key) +{ + if (media == nullptr) + return -EINVAL; + if (key == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + OutputPort *port = getOutputPort(media); + if (port == nullptr) { + pthread_mutex_unlock(&mMutex); + return -ENOENT; + } + + bool found = false; + std::vector::iterator c = port->channels.begin(); + + while (c != port->channels.end()) { + if ((*c)->getKey() != key) { + c++; + continue; + } + found = true; + (*c)->setSourceListener(nullptr); + port->channels.erase(c); + break; + } + + pthread_mutex_unlock(&mMutex); + if (!found) + return -ENOENT; + + ULOGI("%s: unlink media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + key); + return 0; +} + + +int CodedSource::sendDownstreamEvent(CodedVideoMedia *media, + CodedChannel::DownstreamEvent event) +{ + int ret; + unsigned int outputChannelCount, i; + CodedChannel *channel; + + if (media == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + + /* Send the event downstream */ + outputChannelCount = getOutputChannelCount(media); + for (i = 0; i < outputChannelCount; i++) { + channel = getOutputChannel(media, i); + if (channel == nullptr) { + ULOGW("invalid channel"); + continue; + } + ret = channel->sendDownstreamEvent(event); + if (ret < 0) + ULOG_ERRNO("channel->sendDownstreamEvent", -ret); + } + + pthread_mutex_unlock(&mMutex); + + return 0; +} + + +void CodedSource::onChannelUnlink(CodedChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); + if (media == nullptr) { + ULOGE("media not found"); + return; + } + + int ret = removeOutputChannel(media, channel->getKey()); + if (ret < 0) + ULOG_ERRNO("removeOutputChannel", -ret); +} + + +void CodedSource::onChannelFlushed(CodedChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); + if (media == nullptr) { + ULOGE("media not found"); + return; + } + ULOGD("%s: channel flushed media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + /* Nothing to do here, the function should be + * overloaded by sub-classes */ +} + + +void CodedSource::onChannelResync(CodedChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); + if (media == nullptr) { + ULOGE("media not found"); + return; + } + ULOGD("%s: channel resync media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + /* Nothing to do here, the function should be + * overloaded by sub-classes */ +} + + +void CodedSource::onChannelVideoPresStats(CodedChannel *channel, + VideoPresStats *stats) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + CodedVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); + if (media == nullptr) { + ULOGE("media not found"); + return; + } + ULOGD("%s: channel video stats media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + /* Nothing to do here, the function should be + * overloaded by sub-classes */ +} + + +void CodedSource::onChannelUpstreamEvent(CodedChannel *channel, + const struct pomp_msg *event) +{ + VideoPresStats stats; + int err; + + ULOGD("%s: channel upstream event %s", + getName().c_str(), + CodedChannel::getUpstreamEventStr( + (CodedChannel::UpstreamEvent)pomp_msg_get_id(event))); + + switch (pomp_msg_get_id(event)) { + case CodedChannel::UpstreamEvent::UNLINK: + onChannelUnlink(channel); + break; + case CodedChannel::UpstreamEvent::FLUSHED: + onChannelFlushed(channel); + break; + case CodedChannel::UpstreamEvent::RESYNC: + onChannelResync(channel); + break; + case CodedChannel::UpstreamEvent::VIDEO_PRES_STATS: + err = stats.readMsg(event); + if (err < 0) + ULOG_ERRNO("stats.readMsg", -err); + else + onChannelVideoPresStats(channel, &stats); + break; + default: + ULOG_ERRNO("event id %d", ENOSYS, pomp_msg_get_id(event)); + break; + } +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_source_coded_video.hpp b/libpdraw/src/pdraw_source_coded_video.hpp index ca00b30..30cae43 100644 --- a/libpdraw/src/pdraw_source_coded_video.hpp +++ b/libpdraw/src/pdraw_source_coded_video.hpp @@ -1,146 +1,146 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Pipeline source element for coded video - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_SOURCE_CODED_VIDEO_HPP_ -#define _PDRAW_SOURCE_CODED_VIDEO_HPP_ - -#include "pdraw_channel_coded_video.hpp" -#include "pdraw_media.hpp" - -#include - -namespace Pdraw { - -class CodedSource : public CodedChannel::SourceListener { -public: - class Listener { - public: - virtual ~Listener(void) {} - - virtual void onOutputMediaAdded(CodedSource *source, - CodedVideoMedia *media) = 0; - - virtual void onOutputMediaRemoved(CodedSource *source, - CodedVideoMedia *media) = 0; - }; - - virtual ~CodedSource(void); - - void lock(void); - - void unlock(void); - - virtual std::string &getName(void) = 0; - - unsigned int getOutputMediaCount(void); - - CodedVideoMedia *getOutputMedia(unsigned int index); - - CodedVideoMedia *findOutputMedia(CodedVideoMedia *media); - - unsigned int getOutputChannelCount(CodedVideoMedia *media); - - CodedChannel *getOutputChannel(CodedVideoMedia *media, - unsigned int index); - - CodedChannel *getOutputChannel(CodedVideoMedia *media, void *key); - - int addOutputChannel(CodedVideoMedia *media, CodedChannel *channel); - - int removeOutputChannel(CodedVideoMedia *media, void *key); - -protected: - struct OutputPort { - CodedVideoMedia *media; - std::vector channels; - struct mbuf_pool *pool; - bool sharedPool; - - inline OutputPort() : - media(nullptr), pool(nullptr), sharedPool(false) - { - } - }; - - CodedSource(unsigned int maxOutputMedias, Listener *listener); - - CodedVideoMedia *getOutputMediaFromChannel(void *key); - - OutputPort *getOutputPort(CodedVideoMedia *media); - - int addOutputPort(CodedVideoMedia *media); - - int removeOutputPort(CodedVideoMedia *media); - - int removeOutputPorts(void); - - int createOutputPortMemoryPool(CodedVideoMedia *media, - unsigned int count, - size_t capacity); - - int destroyOutputPortMemoryPool(CodedVideoMedia *media); - - int getOutputMemory(CodedVideoMedia **videoMedias, - unsigned int nbVideoMedias, - struct mbuf_mem **mem, - unsigned int *defaultMediaIndex); - - int copyOutputFrame(CodedVideoMedia *srcMedia, - struct mbuf_coded_video_frame *srcFrame, - CodedVideoMedia *dstMedia, - struct mbuf_coded_video_frame **dstFrame); - - int sendDownstreamEvent(CodedVideoMedia *media, - CodedChannel::DownstreamEvent event); - - virtual void onChannelUpstreamEvent(CodedChannel *channel, - const struct pomp_msg *event); - - virtual void onChannelUnlink(CodedChannel *channel); - - virtual void onChannelFlushed(CodedChannel *channel); - - virtual void onChannelResync(CodedChannel *channel); - - virtual void onChannelVideoPresStats(CodedChannel *channel, - VideoPresStats *stats); - - pthread_mutex_t mMutex; - unsigned int mMaxOutputMedias; - std::vector mOutputPorts; - Listener *mListener; - -private: - int destroyOutputPortMemoryPool(OutputPort *port); -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_SOURCE_CODED_VIDEO_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Pipeline source element for coded video + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_SOURCE_CODED_VIDEO_HPP_ +#define _PDRAW_SOURCE_CODED_VIDEO_HPP_ + +#include "pdraw_channel_coded_video.hpp" +#include "pdraw_media.hpp" + +#include + +namespace Pdraw { + +class CodedSource : public CodedChannel::SourceListener { +public: + class Listener { + public: + virtual ~Listener(void) {} + + virtual void onOutputMediaAdded(CodedSource *source, + CodedVideoMedia *media) = 0; + + virtual void onOutputMediaRemoved(CodedSource *source, + CodedVideoMedia *media) = 0; + }; + + virtual ~CodedSource(void); + + void lock(void); + + void unlock(void); + + virtual std::string &getName(void) = 0; + + unsigned int getOutputMediaCount(void); + + CodedVideoMedia *getOutputMedia(unsigned int index); + + CodedVideoMedia *findOutputMedia(CodedVideoMedia *media); + + unsigned int getOutputChannelCount(CodedVideoMedia *media); + + CodedChannel *getOutputChannel(CodedVideoMedia *media, + unsigned int index); + + CodedChannel *getOutputChannel(CodedVideoMedia *media, void *key); + + int addOutputChannel(CodedVideoMedia *media, CodedChannel *channel); + + int removeOutputChannel(CodedVideoMedia *media, void *key); + +protected: + struct OutputPort { + CodedVideoMedia *media; + std::vector channels; + struct mbuf_pool *pool; + bool sharedPool; + + inline OutputPort() : + media(nullptr), pool(nullptr), sharedPool(false) + { + } + }; + + CodedSource(unsigned int maxOutputMedias, Listener *listener); + + CodedVideoMedia *getOutputMediaFromChannel(void *key); + + OutputPort *getOutputPort(CodedVideoMedia *media); + + int addOutputPort(CodedVideoMedia *media); + + int removeOutputPort(CodedVideoMedia *media); + + int removeOutputPorts(void); + + int createOutputPortMemoryPool(CodedVideoMedia *media, + unsigned int count, + size_t capacity); + + int destroyOutputPortMemoryPool(CodedVideoMedia *media); + + int getOutputMemory(CodedVideoMedia **videoMedias, + unsigned int nbVideoMedias, + struct mbuf_mem **mem, + unsigned int *defaultMediaIndex); + + int copyOutputFrame(CodedVideoMedia *srcMedia, + struct mbuf_coded_video_frame *srcFrame, + CodedVideoMedia *dstMedia, + struct mbuf_coded_video_frame **dstFrame); + + int sendDownstreamEvent(CodedVideoMedia *media, + CodedChannel::DownstreamEvent event); + + virtual void onChannelUpstreamEvent(CodedChannel *channel, + const struct pomp_msg *event); + + virtual void onChannelUnlink(CodedChannel *channel); + + virtual void onChannelFlushed(CodedChannel *channel); + + virtual void onChannelResync(CodedChannel *channel); + + virtual void onChannelVideoPresStats(CodedChannel *channel, + VideoPresStats *stats); + + pthread_mutex_t mMutex; + unsigned int mMaxOutputMedias; + std::vector mOutputPorts; + Listener *mListener; + +private: + int destroyOutputPortMemoryPool(OutputPort *port); +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_SOURCE_CODED_VIDEO_HPP_ */ diff --git a/libpdraw/src/pdraw_source_raw_video.cpp b/libpdraw/src/pdraw_source_raw_video.cpp index b4dd2ad..91a422e 100644 --- a/libpdraw/src/pdraw_source_raw_video.cpp +++ b/libpdraw/src/pdraw_source_raw_video.cpp @@ -1,714 +1,714 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Pipeline source element - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_source_raw_video -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_source_raw_video.hpp" - -#include - -#include - -namespace Pdraw { - - -RawSource::RawSource(unsigned int maxOutputMedias, Listener *listener) : - mMaxOutputMedias(maxOutputMedias), mListener(listener) -{ - int res; - pthread_mutexattr_t attr; - bool attr_created = false; - - res = pthread_mutexattr_init(&attr); - if (res != 0) { - ULOG_ERRNO("pthread_mutexattr_init", res); - goto error; - } - attr_created = true; - - res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - if (res != 0) { - ULOG_ERRNO("pthread_mutexattr_settype", res); - goto error; - } - - res = pthread_mutex_init(&mMutex, &attr); - if (res != 0) { - ULOG_ERRNO("pthread_mutex_init", res); - goto error; - } - - pthread_mutexattr_destroy(&attr); - return; - -error: - if (attr_created) - pthread_mutexattr_destroy(&attr); -} - - -RawSource::~RawSource(void) -{ - int ret = removeOutputPorts(); - if (ret < 0) - ULOG_ERRNO("removeOutputPorts", -ret); - - unsigned int count = getOutputMediaCount(); - if (count > 0) { - ULOGW("not all output ports have been removed! (count=%d)", - count); - } - - pthread_mutex_destroy(&mMutex); -} - - -void RawSource::lock(void) -{ - pthread_mutex_lock(&mMutex); -} - - -void RawSource::unlock(void) -{ - pthread_mutex_unlock(&mMutex); -} - - -unsigned int RawSource::getOutputMediaCount(void) -{ - pthread_mutex_lock(&mMutex); - unsigned int ret = mOutputPorts.size(); - pthread_mutex_unlock(&mMutex); - return ret; -} - - -RawVideoMedia *RawSource::getOutputMedia(unsigned int index) -{ - pthread_mutex_lock(&mMutex); - RawVideoMedia *ret = (index < mOutputPorts.size()) - ? mOutputPorts.at(index).media - : nullptr; - pthread_mutex_unlock(&mMutex); - return ret; -} - - -RawVideoMedia *RawSource::findOutputMedia(RawVideoMedia *media) -{ - pthread_mutex_lock(&mMutex); - RawVideoMedia *ret = nullptr; - std::vector::iterator p = mOutputPorts.begin(); - - while (p != mOutputPorts.end()) { - if (p->media != media) { - p++; - continue; - } - ret = p->media; - break; - } - pthread_mutex_unlock(&mMutex); - return ret; -} - - -RawVideoMedia *RawSource::getOutputMediaFromChannel(void *key) -{ - pthread_mutex_lock(&mMutex); - RawVideoMedia *ret = nullptr; - std::vector::iterator p = mOutputPorts.begin(); - - while (p != mOutputPorts.end()) { - std::vector::iterator c = p->channels.begin(); - - while (c != p->channels.end()) { - if ((*c)->getKey() != key) { - c++; - continue; - } - ret = p->media; - break; - } - - if (ret == nullptr) { - p++; - continue; - } - break; - } - - pthread_mutex_unlock(&mMutex); - return ret; -} - - -RawSource::OutputPort *RawSource::getOutputPort(RawVideoMedia *media) -{ - if (media == nullptr) { - ULOG_ERRNO("media", EINVAL); - return nullptr; - } - - pthread_mutex_lock(&mMutex); - OutputPort *ret = nullptr; - std::vector::iterator p = mOutputPorts.begin(); - - while (p != mOutputPorts.end()) { - if (p->media != media) { - p++; - continue; - } - ret = &(*p); - break; - } - - pthread_mutex_unlock(&mMutex); - return ret; -} - - -int RawSource::addOutputPort(RawVideoMedia *media) -{ - if (media == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - - if (mOutputPorts.size() >= mMaxOutputMedias) { - pthread_mutex_unlock(&mMutex); - return -ENOBUFS; - } - - OutputPort port; - port.media = media; - mOutputPorts.push_back(port); - - pthread_mutex_unlock(&mMutex); - - ULOGI("%s: add port for media name=%s", - getName().c_str(), - media->getName().c_str()); - - return 0; -} - - -int RawSource::removeOutputPort(RawVideoMedia *media) -{ - if (media == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - bool found = false; - std::vector::iterator p = mOutputPorts.begin(); - - while (p != mOutputPorts.end()) { - if (p->media != media) { - p++; - continue; - } - found = true; - - unsigned int count = p->channels.size(); - if (count > 0) { - pthread_mutex_unlock(&mMutex); - ULOGW("not all output channels have been removed! " - "(count=%d)", - count); - return -EBUSY; - } - p->media = nullptr; - destroyOutputPortMemoryPool(&(*p)); - mOutputPorts.erase(p); - break; - } - - pthread_mutex_unlock(&mMutex); - if (!found) - return -ENOENT; - - ULOGI("%s: delete port for media name=%s", - getName().c_str(), - media->getName().c_str()); - - return 0; -} - - -int RawSource::removeOutputPorts(void) -{ - pthread_mutex_lock(&mMutex); - std::vector::iterator p = mOutputPorts.begin(); - - while (p != mOutputPorts.end()) { - if (mListener) - mListener->onOutputMediaRemoved(this, p->media); - - unsigned int count = p->channels.size(); - if (count > 0) { - pthread_mutex_unlock(&mMutex); - ULOGW("not all output channels have been removed! " - "(count=%d)", - count); - return -EBUSY; - } - ULOGI("%s: delete port for media name=%s", - getName().c_str(), - p->media->getName().c_str()); - /* Note: unlike removeOutputPort(), here the media is deleted */ - delete p->media; - p->media = nullptr; - destroyOutputPortMemoryPool(&(*p)); - p++; - } - - mOutputPorts.clear(); - - pthread_mutex_unlock(&mMutex); - return 0; -} - - -int RawSource::createOutputPortMemoryPool(RawVideoMedia *media, - unsigned int count, - size_t capacity) -{ - int ret; - - if (media == nullptr) - return -EINVAL; - if (count == 0) - return -EINVAL; - if (capacity == 0) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - OutputPort *port = nullptr; - std::vector::iterator p = mOutputPorts.begin(); - - while (p != mOutputPorts.end()) { - if (p->media != media) { - p++; - continue; - } - port = &(*p); - break; - } - - if (port == nullptr) { - pthread_mutex_unlock(&mMutex); - return -ENOENT; - } - - ret = mbuf_pool_new(mbuf_mem_generic_impl, - capacity, - count, - MBUF_POOL_NO_GROW, - 0, - getName().c_str(), - &port->pool); - if (ret < 0) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("mbuf_pool_new", -ret); - return ret; - } - - pthread_mutex_unlock(&mMutex); - return 0; -} - - -int RawSource::destroyOutputPortMemoryPool(OutputPort *port) -{ - int ret = 0; - - if (port == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - - if (port->pool == nullptr) { - pthread_mutex_unlock(&mMutex); - return 0; - } - - if (!port->sharedPool) { - ret = mbuf_pool_destroy(port->pool); - if (ret < 0) - ULOG_ERRNO("mbuf_pool_destroy", -ret); - } - - port->pool = nullptr; - pthread_mutex_unlock(&mMutex); - return ret; -} - - -int RawSource::destroyOutputPortMemoryPool(RawVideoMedia *media) -{ - if (media == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - OutputPort *port = nullptr; - std::vector::iterator p = mOutputPorts.begin(); - - while (p != mOutputPorts.end()) { - if (p->media != media) { - p++; - continue; - } - port = &(*p); - break; - } - - if (port == nullptr) { - pthread_mutex_unlock(&mMutex); - return -ENOENT; - } - - int ret = destroyOutputPortMemoryPool(port); - pthread_mutex_unlock(&mMutex); - return ret; -} - - -unsigned int RawSource::getOutputChannelCount(RawVideoMedia *media) -{ - if (media == nullptr) { - ULOG_ERRNO("media", EINVAL); - return 0; - } - - pthread_mutex_lock(&mMutex); - OutputPort *port = getOutputPort(media); - if (port == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("port", ENOENT); - return 0; - } - - unsigned int ret = port->channels.size(); - pthread_mutex_unlock(&mMutex); - return ret; -} - - -RawChannel *RawSource::getOutputChannel(RawVideoMedia *media, - unsigned int index) -{ - if (media == nullptr) { - ULOG_ERRNO("media", EINVAL); - return nullptr; - } - - pthread_mutex_lock(&mMutex); - OutputPort *port = getOutputPort(media); - if (port == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("port", ENOENT); - return nullptr; - } - if (index >= port->channels.size()) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("index", ENOENT); - return nullptr; - } - - RawChannel *ret = port->channels.at(index); - pthread_mutex_unlock(&mMutex); - - return ret; -} - - -RawChannel *RawSource::getOutputChannel(RawVideoMedia *media, void *key) -{ - if (media == nullptr) { - ULOG_ERRNO("media", EINVAL); - return nullptr; - } - if (key == nullptr) { - ULOG_ERRNO("key", EINVAL); - return nullptr; - } - - pthread_mutex_lock(&mMutex); - OutputPort *port = getOutputPort(media); - if (port == nullptr) { - pthread_mutex_unlock(&mMutex); - ULOG_ERRNO("port", ENOENT); - return nullptr; - } - - RawChannel *ret = nullptr; - std::vector::iterator c = port->channels.begin(); - - while (c != port->channels.end()) { - if ((*c)->getKey() != key) { - c++; - continue; - } - ret = *c; - break; - } - - pthread_mutex_unlock(&mMutex); - return ret; -} - - -int RawSource::addOutputChannel(RawVideoMedia *media, RawChannel *channel) -{ - if (media == nullptr) - return -EINVAL; - if (channel == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - RawChannel *c = getOutputChannel(media, channel->getKey()); - if (c != nullptr) { - pthread_mutex_unlock(&mMutex); - return -EEXIST; - } - OutputPort *port = getOutputPort(media); - if (port == nullptr) { - pthread_mutex_unlock(&mMutex); - return -ENOENT; - } - - channel->setSourceListener(this); - port->channels.push_back(channel); - pthread_mutex_unlock(&mMutex); - - ULOGI("%s: link media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - channel->getKey()); - return 0; -} - - -int RawSource::removeOutputChannel(RawVideoMedia *media, void *key) -{ - if (media == nullptr) - return -EINVAL; - if (key == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - OutputPort *port = getOutputPort(media); - if (port == nullptr) { - pthread_mutex_unlock(&mMutex); - return -ENOENT; - } - - bool found = false; - std::vector::iterator c = port->channels.begin(); - - while (c != port->channels.end()) { - if ((*c)->getKey() != key) { - c++; - continue; - } - found = true; - (*c)->setSourceListener(nullptr); - port->channels.erase(c); - break; - } - - pthread_mutex_unlock(&mMutex); - if (!found) - return -ENOENT; - - ULOGI("%s: unlink media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - key); - return 0; -} - - -int RawSource::sendDownstreamEvent(RawVideoMedia *media, - RawChannel::DownstreamEvent event) -{ - int ret; - unsigned int outputChannelCount, i; - RawChannel *channel; - - if (media == nullptr) - return -EINVAL; - - pthread_mutex_lock(&mMutex); - - /* Send the event downstream */ - outputChannelCount = getOutputChannelCount(media); - for (i = 0; i < outputChannelCount; i++) { - channel = getOutputChannel(media, i); - if (channel == nullptr) { - ULOGW("invalid channel"); - continue; - } - ret = channel->sendDownstreamEvent(event); - if (ret < 0) - ULOG_ERRNO("channel->sendDownstreamEvent", -ret); - } - - pthread_mutex_unlock(&mMutex); - - return 0; -} - - -void RawSource::onChannelUnlink(RawChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - RawVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); - if (media == nullptr) { - ULOGE("media not found"); - return; - } - - int ret = removeOutputChannel(media, channel->getKey()); - if (ret < 0) - ULOG_ERRNO("removeOutputChannel", -ret); -} - - -void RawSource::onChannelFlushed(RawChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - RawVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); - if (media == nullptr) { - ULOGE("media not found"); - return; - } - ULOGD("%s: channel flushed media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - /* Nothing to do here, the function should be - * overloaded by sub-classes */ -} - - -void RawSource::onChannelResync(RawChannel *channel) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - RawVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); - if (media == nullptr) { - ULOGE("media not found"); - return; - } - ULOGD("%s: channel resync media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - /* Nothing to do here, the function should be - * overloaded by sub-classes */ -} - - -void RawSource::onChannelVideoPresStats(RawChannel *channel, - VideoPresStats *stats) -{ - if (channel == nullptr) { - ULOG_ERRNO("channel", EINVAL); - return; - } - - RawVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); - if (media == nullptr) { - ULOGE("media not found"); - return; - } - ULOGD("%s: channel video stats media name=%s (channel key=%p)", - getName().c_str(), - media->getName().c_str(), - channel->getKey()); - - /* Nothing to do here, the function should be - * overloaded by sub-classes */ -} - - -void RawSource::onChannelUpstreamEvent(RawChannel *channel, - const struct pomp_msg *event) -{ - VideoPresStats stats; - int err; - - ULOGD("%s: channel upstream event %s", - getName().c_str(), - RawChannel::getUpstreamEventStr( - (RawChannel::UpstreamEvent)pomp_msg_get_id(event))); - - switch (pomp_msg_get_id(event)) { - case RawChannel::UpstreamEvent::UNLINK: - onChannelUnlink(channel); - break; - case RawChannel::UpstreamEvent::FLUSHED: - onChannelFlushed(channel); - break; - case RawChannel::UpstreamEvent::RESYNC: - onChannelResync(channel); - break; - case RawChannel::UpstreamEvent::VIDEO_PRES_STATS: - err = stats.readMsg(event); - if (err < 0) - ULOG_ERRNO("stats.readMsg", -err); - else - onChannelVideoPresStats(channel, &stats); - break; - default: - ULOG_ERRNO("event id %d", ENOSYS, pomp_msg_get_id(event)); - break; - } -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Pipeline source element + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_source_raw_video +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_source_raw_video.hpp" + +#include + +#include + +namespace Pdraw { + + +RawSource::RawSource(unsigned int maxOutputMedias, Listener *listener) : + mMaxOutputMedias(maxOutputMedias), mListener(listener) +{ + int res; + pthread_mutexattr_t attr; + bool attr_created = false; + + res = pthread_mutexattr_init(&attr); + if (res != 0) { + ULOG_ERRNO("pthread_mutexattr_init", res); + goto error; + } + attr_created = true; + + res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + if (res != 0) { + ULOG_ERRNO("pthread_mutexattr_settype", res); + goto error; + } + + res = pthread_mutex_init(&mMutex, &attr); + if (res != 0) { + ULOG_ERRNO("pthread_mutex_init", res); + goto error; + } + + pthread_mutexattr_destroy(&attr); + return; + +error: + if (attr_created) + pthread_mutexattr_destroy(&attr); +} + + +RawSource::~RawSource(void) +{ + int ret = removeOutputPorts(); + if (ret < 0) + ULOG_ERRNO("removeOutputPorts", -ret); + + unsigned int count = getOutputMediaCount(); + if (count > 0) { + ULOGW("not all output ports have been removed! (count=%d)", + count); + } + + pthread_mutex_destroy(&mMutex); +} + + +void RawSource::lock(void) +{ + pthread_mutex_lock(&mMutex); +} + + +void RawSource::unlock(void) +{ + pthread_mutex_unlock(&mMutex); +} + + +unsigned int RawSource::getOutputMediaCount(void) +{ + pthread_mutex_lock(&mMutex); + unsigned int ret = mOutputPorts.size(); + pthread_mutex_unlock(&mMutex); + return ret; +} + + +RawVideoMedia *RawSource::getOutputMedia(unsigned int index) +{ + pthread_mutex_lock(&mMutex); + RawVideoMedia *ret = (index < mOutputPorts.size()) + ? mOutputPorts.at(index).media + : nullptr; + pthread_mutex_unlock(&mMutex); + return ret; +} + + +RawVideoMedia *RawSource::findOutputMedia(RawVideoMedia *media) +{ + pthread_mutex_lock(&mMutex); + RawVideoMedia *ret = nullptr; + std::vector::iterator p = mOutputPorts.begin(); + + while (p != mOutputPorts.end()) { + if (p->media != media) { + p++; + continue; + } + ret = p->media; + break; + } + pthread_mutex_unlock(&mMutex); + return ret; +} + + +RawVideoMedia *RawSource::getOutputMediaFromChannel(void *key) +{ + pthread_mutex_lock(&mMutex); + RawVideoMedia *ret = nullptr; + std::vector::iterator p = mOutputPorts.begin(); + + while (p != mOutputPorts.end()) { + std::vector::iterator c = p->channels.begin(); + + while (c != p->channels.end()) { + if ((*c)->getKey() != key) { + c++; + continue; + } + ret = p->media; + break; + } + + if (ret == nullptr) { + p++; + continue; + } + break; + } + + pthread_mutex_unlock(&mMutex); + return ret; +} + + +RawSource::OutputPort *RawSource::getOutputPort(RawVideoMedia *media) +{ + if (media == nullptr) { + ULOG_ERRNO("media", EINVAL); + return nullptr; + } + + pthread_mutex_lock(&mMutex); + OutputPort *ret = nullptr; + std::vector::iterator p = mOutputPorts.begin(); + + while (p != mOutputPorts.end()) { + if (p->media != media) { + p++; + continue; + } + ret = &(*p); + break; + } + + pthread_mutex_unlock(&mMutex); + return ret; +} + + +int RawSource::addOutputPort(RawVideoMedia *media) +{ + if (media == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + + if (mOutputPorts.size() >= mMaxOutputMedias) { + pthread_mutex_unlock(&mMutex); + return -ENOBUFS; + } + + OutputPort port; + port.media = media; + mOutputPorts.push_back(port); + + pthread_mutex_unlock(&mMutex); + + ULOGI("%s: add port for media name=%s", + getName().c_str(), + media->getName().c_str()); + + return 0; +} + + +int RawSource::removeOutputPort(RawVideoMedia *media) +{ + if (media == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + bool found = false; + std::vector::iterator p = mOutputPorts.begin(); + + while (p != mOutputPorts.end()) { + if (p->media != media) { + p++; + continue; + } + found = true; + + unsigned int count = p->channels.size(); + if (count > 0) { + pthread_mutex_unlock(&mMutex); + ULOGW("not all output channels have been removed! " + "(count=%d)", + count); + return -EBUSY; + } + p->media = nullptr; + destroyOutputPortMemoryPool(&(*p)); + mOutputPorts.erase(p); + break; + } + + pthread_mutex_unlock(&mMutex); + if (!found) + return -ENOENT; + + ULOGI("%s: delete port for media name=%s", + getName().c_str(), + media->getName().c_str()); + + return 0; +} + + +int RawSource::removeOutputPorts(void) +{ + pthread_mutex_lock(&mMutex); + std::vector::iterator p = mOutputPorts.begin(); + + while (p != mOutputPorts.end()) { + if (mListener) + mListener->onOutputMediaRemoved(this, p->media); + + unsigned int count = p->channels.size(); + if (count > 0) { + pthread_mutex_unlock(&mMutex); + ULOGW("not all output channels have been removed! " + "(count=%d)", + count); + return -EBUSY; + } + ULOGI("%s: delete port for media name=%s", + getName().c_str(), + p->media->getName().c_str()); + /* Note: unlike removeOutputPort(), here the media is deleted */ + delete p->media; + p->media = nullptr; + destroyOutputPortMemoryPool(&(*p)); + p++; + } + + mOutputPorts.clear(); + + pthread_mutex_unlock(&mMutex); + return 0; +} + + +int RawSource::createOutputPortMemoryPool(RawVideoMedia *media, + unsigned int count, + size_t capacity) +{ + int ret; + + if (media == nullptr) + return -EINVAL; + if (count == 0) + return -EINVAL; + if (capacity == 0) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + OutputPort *port = nullptr; + std::vector::iterator p = mOutputPorts.begin(); + + while (p != mOutputPorts.end()) { + if (p->media != media) { + p++; + continue; + } + port = &(*p); + break; + } + + if (port == nullptr) { + pthread_mutex_unlock(&mMutex); + return -ENOENT; + } + + ret = mbuf_pool_new(mbuf_mem_generic_impl, + capacity, + count, + MBUF_POOL_NO_GROW, + 0, + getName().c_str(), + &port->pool); + if (ret < 0) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("mbuf_pool_new", -ret); + return ret; + } + + pthread_mutex_unlock(&mMutex); + return 0; +} + + +int RawSource::destroyOutputPortMemoryPool(OutputPort *port) +{ + int ret = 0; + + if (port == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + + if (port->pool == nullptr) { + pthread_mutex_unlock(&mMutex); + return 0; + } + + if (!port->sharedPool) { + ret = mbuf_pool_destroy(port->pool); + if (ret < 0) + ULOG_ERRNO("mbuf_pool_destroy", -ret); + } + + port->pool = nullptr; + pthread_mutex_unlock(&mMutex); + return ret; +} + + +int RawSource::destroyOutputPortMemoryPool(RawVideoMedia *media) +{ + if (media == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + OutputPort *port = nullptr; + std::vector::iterator p = mOutputPorts.begin(); + + while (p != mOutputPorts.end()) { + if (p->media != media) { + p++; + continue; + } + port = &(*p); + break; + } + + if (port == nullptr) { + pthread_mutex_unlock(&mMutex); + return -ENOENT; + } + + int ret = destroyOutputPortMemoryPool(port); + pthread_mutex_unlock(&mMutex); + return ret; +} + + +unsigned int RawSource::getOutputChannelCount(RawVideoMedia *media) +{ + if (media == nullptr) { + ULOG_ERRNO("media", EINVAL); + return 0; + } + + pthread_mutex_lock(&mMutex); + OutputPort *port = getOutputPort(media); + if (port == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("port", ENOENT); + return 0; + } + + unsigned int ret = port->channels.size(); + pthread_mutex_unlock(&mMutex); + return ret; +} + + +RawChannel *RawSource::getOutputChannel(RawVideoMedia *media, + unsigned int index) +{ + if (media == nullptr) { + ULOG_ERRNO("media", EINVAL); + return nullptr; + } + + pthread_mutex_lock(&mMutex); + OutputPort *port = getOutputPort(media); + if (port == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("port", ENOENT); + return nullptr; + } + if (index >= port->channels.size()) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("index", ENOENT); + return nullptr; + } + + RawChannel *ret = port->channels.at(index); + pthread_mutex_unlock(&mMutex); + + return ret; +} + + +RawChannel *RawSource::getOutputChannel(RawVideoMedia *media, void *key) +{ + if (media == nullptr) { + ULOG_ERRNO("media", EINVAL); + return nullptr; + } + if (key == nullptr) { + ULOG_ERRNO("key", EINVAL); + return nullptr; + } + + pthread_mutex_lock(&mMutex); + OutputPort *port = getOutputPort(media); + if (port == nullptr) { + pthread_mutex_unlock(&mMutex); + ULOG_ERRNO("port", ENOENT); + return nullptr; + } + + RawChannel *ret = nullptr; + std::vector::iterator c = port->channels.begin(); + + while (c != port->channels.end()) { + if ((*c)->getKey() != key) { + c++; + continue; + } + ret = *c; + break; + } + + pthread_mutex_unlock(&mMutex); + return ret; +} + + +int RawSource::addOutputChannel(RawVideoMedia *media, RawChannel *channel) +{ + if (media == nullptr) + return -EINVAL; + if (channel == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + RawChannel *c = getOutputChannel(media, channel->getKey()); + if (c != nullptr) { + pthread_mutex_unlock(&mMutex); + return -EEXIST; + } + OutputPort *port = getOutputPort(media); + if (port == nullptr) { + pthread_mutex_unlock(&mMutex); + return -ENOENT; + } + + channel->setSourceListener(this); + port->channels.push_back(channel); + pthread_mutex_unlock(&mMutex); + + ULOGI("%s: link media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + channel->getKey()); + return 0; +} + + +int RawSource::removeOutputChannel(RawVideoMedia *media, void *key) +{ + if (media == nullptr) + return -EINVAL; + if (key == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + OutputPort *port = getOutputPort(media); + if (port == nullptr) { + pthread_mutex_unlock(&mMutex); + return -ENOENT; + } + + bool found = false; + std::vector::iterator c = port->channels.begin(); + + while (c != port->channels.end()) { + if ((*c)->getKey() != key) { + c++; + continue; + } + found = true; + (*c)->setSourceListener(nullptr); + port->channels.erase(c); + break; + } + + pthread_mutex_unlock(&mMutex); + if (!found) + return -ENOENT; + + ULOGI("%s: unlink media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + key); + return 0; +} + + +int RawSource::sendDownstreamEvent(RawVideoMedia *media, + RawChannel::DownstreamEvent event) +{ + int ret; + unsigned int outputChannelCount, i; + RawChannel *channel; + + if (media == nullptr) + return -EINVAL; + + pthread_mutex_lock(&mMutex); + + /* Send the event downstream */ + outputChannelCount = getOutputChannelCount(media); + for (i = 0; i < outputChannelCount; i++) { + channel = getOutputChannel(media, i); + if (channel == nullptr) { + ULOGW("invalid channel"); + continue; + } + ret = channel->sendDownstreamEvent(event); + if (ret < 0) + ULOG_ERRNO("channel->sendDownstreamEvent", -ret); + } + + pthread_mutex_unlock(&mMutex); + + return 0; +} + + +void RawSource::onChannelUnlink(RawChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + RawVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); + if (media == nullptr) { + ULOGE("media not found"); + return; + } + + int ret = removeOutputChannel(media, channel->getKey()); + if (ret < 0) + ULOG_ERRNO("removeOutputChannel", -ret); +} + + +void RawSource::onChannelFlushed(RawChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + RawVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); + if (media == nullptr) { + ULOGE("media not found"); + return; + } + ULOGD("%s: channel flushed media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + /* Nothing to do here, the function should be + * overloaded by sub-classes */ +} + + +void RawSource::onChannelResync(RawChannel *channel) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + RawVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); + if (media == nullptr) { + ULOGE("media not found"); + return; + } + ULOGD("%s: channel resync media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + /* Nothing to do here, the function should be + * overloaded by sub-classes */ +} + + +void RawSource::onChannelVideoPresStats(RawChannel *channel, + VideoPresStats *stats) +{ + if (channel == nullptr) { + ULOG_ERRNO("channel", EINVAL); + return; + } + + RawVideoMedia *media = getOutputMediaFromChannel(channel->getKey()); + if (media == nullptr) { + ULOGE("media not found"); + return; + } + ULOGD("%s: channel video stats media name=%s (channel key=%p)", + getName().c_str(), + media->getName().c_str(), + channel->getKey()); + + /* Nothing to do here, the function should be + * overloaded by sub-classes */ +} + + +void RawSource::onChannelUpstreamEvent(RawChannel *channel, + const struct pomp_msg *event) +{ + VideoPresStats stats; + int err; + + ULOGD("%s: channel upstream event %s", + getName().c_str(), + RawChannel::getUpstreamEventStr( + (RawChannel::UpstreamEvent)pomp_msg_get_id(event))); + + switch (pomp_msg_get_id(event)) { + case RawChannel::UpstreamEvent::UNLINK: + onChannelUnlink(channel); + break; + case RawChannel::UpstreamEvent::FLUSHED: + onChannelFlushed(channel); + break; + case RawChannel::UpstreamEvent::RESYNC: + onChannelResync(channel); + break; + case RawChannel::UpstreamEvent::VIDEO_PRES_STATS: + err = stats.readMsg(event); + if (err < 0) + ULOG_ERRNO("stats.readMsg", -err); + else + onChannelVideoPresStats(channel, &stats); + break; + default: + ULOG_ERRNO("event id %d", ENOSYS, pomp_msg_get_id(event)); + break; + } +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_source_raw_video.hpp b/libpdraw/src/pdraw_source_raw_video.hpp index cd9d57d..36ee427 100644 --- a/libpdraw/src/pdraw_source_raw_video.hpp +++ b/libpdraw/src/pdraw_source_raw_video.hpp @@ -1,135 +1,135 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Pipeline source element for raw video - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_SOURCE_RAW_VIDEO_HPP_ -#define _PDRAW_SOURCE_RAW_VIDEO_HPP_ - -#include "pdraw_channel_raw_video.hpp" -#include "pdraw_media.hpp" - -#include - -namespace Pdraw { - -class RawSource : public RawChannel::SourceListener { -public: - class Listener { - public: - virtual ~Listener(void) {} - - virtual void onOutputMediaAdded(RawSource *source, - RawVideoMedia *media) = 0; - - virtual void onOutputMediaRemoved(RawSource *source, - RawVideoMedia *media) = 0; - }; - - virtual ~RawSource(void); - - void lock(void); - - void unlock(void); - - virtual std::string &getName(void) = 0; - - unsigned int getOutputMediaCount(void); - - RawVideoMedia *getOutputMedia(unsigned int index); - - RawVideoMedia *findOutputMedia(RawVideoMedia *media); - - unsigned int getOutputChannelCount(RawVideoMedia *media); - - RawChannel *getOutputChannel(RawVideoMedia *media, unsigned int index); - - RawChannel *getOutputChannel(RawVideoMedia *media, void *key); - - int addOutputChannel(RawVideoMedia *media, RawChannel *channel); - - int removeOutputChannel(RawVideoMedia *media, void *key); - -protected: - struct OutputPort { - RawVideoMedia *media; - std::vector channels; - struct mbuf_pool *pool; - bool sharedPool; - - inline OutputPort() : - media(nullptr), pool(nullptr), sharedPool(false) - { - } - }; - - RawSource(unsigned int maxOutputMedias, Listener *listener); - - RawVideoMedia *getOutputMediaFromChannel(void *key); - - OutputPort *getOutputPort(RawVideoMedia *media); - - int addOutputPort(RawVideoMedia *media); - - int removeOutputPort(RawVideoMedia *media); - - int removeOutputPorts(void); - - int createOutputPortMemoryPool(RawVideoMedia *media, - unsigned int count, - size_t capacity); - - int destroyOutputPortMemoryPool(RawVideoMedia *media); - - int sendDownstreamEvent(RawVideoMedia *media, - RawChannel::DownstreamEvent event); - - virtual void onChannelUpstreamEvent(RawChannel *channel, - const struct pomp_msg *event); - - virtual void onChannelUnlink(RawChannel *channel); - - virtual void onChannelFlushed(RawChannel *channel); - - virtual void onChannelResync(RawChannel *channel); - - virtual void onChannelVideoPresStats(RawChannel *channel, - VideoPresStats *stats); - - pthread_mutex_t mMutex; - unsigned int mMaxOutputMedias; - std::vector mOutputPorts; - Listener *mListener; - -private: - int destroyOutputPortMemoryPool(OutputPort *port); -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_SOURCE_RAW_VIDEO_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Pipeline source element for raw video + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_SOURCE_RAW_VIDEO_HPP_ +#define _PDRAW_SOURCE_RAW_VIDEO_HPP_ + +#include "pdraw_channel_raw_video.hpp" +#include "pdraw_media.hpp" + +#include + +namespace Pdraw { + +class RawSource : public RawChannel::SourceListener { +public: + class Listener { + public: + virtual ~Listener(void) {} + + virtual void onOutputMediaAdded(RawSource *source, + RawVideoMedia *media) = 0; + + virtual void onOutputMediaRemoved(RawSource *source, + RawVideoMedia *media) = 0; + }; + + virtual ~RawSource(void); + + void lock(void); + + void unlock(void); + + virtual std::string &getName(void) = 0; + + unsigned int getOutputMediaCount(void); + + RawVideoMedia *getOutputMedia(unsigned int index); + + RawVideoMedia *findOutputMedia(RawVideoMedia *media); + + unsigned int getOutputChannelCount(RawVideoMedia *media); + + RawChannel *getOutputChannel(RawVideoMedia *media, unsigned int index); + + RawChannel *getOutputChannel(RawVideoMedia *media, void *key); + + int addOutputChannel(RawVideoMedia *media, RawChannel *channel); + + int removeOutputChannel(RawVideoMedia *media, void *key); + +protected: + struct OutputPort { + RawVideoMedia *media; + std::vector channels; + struct mbuf_pool *pool; + bool sharedPool; + + inline OutputPort() : + media(nullptr), pool(nullptr), sharedPool(false) + { + } + }; + + RawSource(unsigned int maxOutputMedias, Listener *listener); + + RawVideoMedia *getOutputMediaFromChannel(void *key); + + OutputPort *getOutputPort(RawVideoMedia *media); + + int addOutputPort(RawVideoMedia *media); + + int removeOutputPort(RawVideoMedia *media); + + int removeOutputPorts(void); + + int createOutputPortMemoryPool(RawVideoMedia *media, + unsigned int count, + size_t capacity); + + int destroyOutputPortMemoryPool(RawVideoMedia *media); + + int sendDownstreamEvent(RawVideoMedia *media, + RawChannel::DownstreamEvent event); + + virtual void onChannelUpstreamEvent(RawChannel *channel, + const struct pomp_msg *event); + + virtual void onChannelUnlink(RawChannel *channel); + + virtual void onChannelFlushed(RawChannel *channel); + + virtual void onChannelResync(RawChannel *channel); + + virtual void onChannelVideoPresStats(RawChannel *channel, + VideoPresStats *stats); + + pthread_mutex_t mMutex; + unsigned int mMaxOutputMedias; + std::vector mOutputPorts; + Listener *mListener; + +private: + int destroyOutputPortMemoryPool(OutputPort *port); +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_SOURCE_RAW_VIDEO_HPP_ */ diff --git a/libpdraw/src/pdraw_utils.cpp b/libpdraw/src/pdraw_utils.cpp index 5a5a842..397eb28 100644 --- a/libpdraw/src/pdraw_utils.cpp +++ b/libpdraw/src/pdraw_utils.cpp @@ -1,663 +1,663 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Utilities - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_utils -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_utils.hpp" - -#include -#include - -#ifdef BUILD_JSON -# include -#endif - -extern "C" { -const char *PDRAW_ANCILLARY_DATA_KEY_VIDEOFRAME = "pdraw.video.frame"; -} - -/* Approximation without the Simphson integration; - * see http://dev.theomader.com/gaussian-kernel-calculator/ */ -void pdraw_gaussianDistribution(float *samples, - unsigned int sampleCount, - float sigma) -{ - unsigned int i; - float a, x, g, start, step, sum; - - if (samples == nullptr) - return; - - if (sampleCount == 0) - return; - if (!(sampleCount & 1)) - sampleCount--; - if (sampleCount == 0) - return; - - a = 1.f / (sqrtf(2.f * M_PI) * sigma); - start = -(float)sampleCount / 2.f; - step = (sampleCount > 1) - ? (float)sampleCount / ((float)sampleCount - 1.f) - : 0.f; - - /* Compute coefs */ - for (i = 0, x = start, sum = 0.f; i < (sampleCount + 1) / 2; - i++, x += step) { - g = expf(-x * x / (2.f * sigma * sigma)) * a; - sum += (i == sampleCount / 2) ? g : g * 2.f; - /* Array is symmetric so write to one side of the array only */ - *(samples + i) = g; - } - - /* Normalization */ - for (i = 0; i < (sampleCount + 1) / 2; i++) { - g = *(samples + i) / sum; - /* Array is symmetric so write to both sides of the array - * (the middle value is written twice but it doesn't matter) */ - *(samples + i) = g; - *(samples + sampleCount - 1 - i) = g; - } -} - - -void pdraw_friendlyTimeFromUs(uint64_t time, - unsigned int *hrs, - unsigned int *min, - unsigned int *sec, - unsigned int *msec) -{ - unsigned int _hrs = - (unsigned int)((time + 500) / 1000 / 60 / 60) / 1000; - unsigned int _min = - (unsigned int)((time + 500) / 1000 / 60 - _hrs * 60000) / 1000; - unsigned int _sec = (unsigned int)((time + 500) / 1000 - - _hrs * 60 * 60000 - _min * 60000) / - 1000; - unsigned int _msec = - (unsigned int)((time + 500) / 1000 - _hrs * 60 * 60000 - - _min * 60000 - _sec * 1000); - if (hrs) - *hrs = _hrs; - if (min) - *min = _min; - if (sec) - *sec = _sec; - if (msec) - *msec = _msec; -} - - -const char *pdraw_hmdModelStr(enum pdraw_hmd_model val) -{ - switch (val) { - default: - case PDRAW_HMD_MODEL_COCKPITGLASSES: - return "COCKPITGLASSES"; - case PDRAW_HMD_MODEL_COCKPITGLASSES_2: - return "COCKPITGLASSES_2"; - } -} - - -const char *pdraw_pipelineModeStr(enum pdraw_pipeline_mode val) -{ - switch (val) { - default: - case PDRAW_PIPELINE_MODE_DECODE_ALL: - return "DECODE_ALL"; - case PDRAW_PIPELINE_MODE_DECODE_NONE: - return "DECODE_NONE"; - } -} - - -const char *pdraw_playbackTypeStr(enum pdraw_playback_type val) -{ - switch (val) { - default: - case PDRAW_PLAYBACK_TYPE_UNKNOWN: - return "UNKNOWN"; - case PDRAW_PLAYBACK_TYPE_LIVE: - return "LIVE"; - case PDRAW_PLAYBACK_TYPE_REPLAY: - return "REPLAY"; - } -} - - -const char *pdraw_mediaTypeStr(enum pdraw_media_type val) -{ - switch (val) { - default: - case PDRAW_MEDIA_TYPE_UNKNOWN: - return "UNKNOWN"; - case PDRAW_MEDIA_TYPE_VIDEO: - return "VIDEO"; - } -} - - -const char *pdraw_videoTypeStr(enum pdraw_video_type val) -{ - switch (val) { - default: - case PDRAW_VIDEO_TYPE_DEFAULT_CAMERA: - return "DEFAULT_CAMERA"; - } -} - - -const char *pdraw_histogramChannelStr(enum pdraw_histogram_channel val) -{ - switch (val) { - default: - return "UNKNOWN"; - case PDRAW_HISTOGRAM_CHANNEL_RED: - return "RED"; - case PDRAW_HISTOGRAM_CHANNEL_GREEN: - return "GREEN"; - case PDRAW_HISTOGRAM_CHANNEL_BLUE: - return "BLUE"; - case PDRAW_HISTOGRAM_CHANNEL_LUMA: - return "LUMA"; - } -} - - -const char *pdraw_videoRendererSchedulingModeStr( - enum pdraw_video_renderer_scheduling_mode val) -{ - switch (val) { - case PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ASAP: - return "ASAP"; - case PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ADAPTIVE: - return "ADAPTIVE"; - case PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_MAX: - default: - return "UNKNOWN"; - } -} - - -const char * -pdraw_videoRendererFillModeStr(enum pdraw_video_renderer_fill_mode val) -{ - switch (val) { - case PDRAW_VIDEO_RENDERER_FILL_MODE_FIT: - return "FIT"; - case PDRAW_VIDEO_RENDERER_FILL_MODE_CROP: - return "CROP"; - case PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_CROP: - return "FIT_PAD_BLUR_CROP"; - case PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_EXTEND: - return "FIT_PAD_BLUR_EXTEND"; - case PDRAW_VIDEO_RENDERER_FILL_MODE_MAX: - default: - return "UNKNOWN"; - } -} - - -const char *pdraw_videoRendererTransitionFlagStr( - enum pdraw_video_renderer_transition_flag val) -{ - switch (val) { - default: - return "NONE"; - case PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_SOS: - return "SOS"; - case PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_EOS: - return "EOS"; - case PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_RECONFIGURE: - return "RECONFIGURE"; - case PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_TIMEOUT: - return "TIMEOUT"; - case PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_PHOTO_TRIGGER: - return "PHOTO_TRIGGER"; - } -} - - -#ifdef BUILD_JSON -static void jsonFillRawVideoInfo(struct json_object *jobj, - const struct vdef_raw_frame *frame) -{ - int ret; - struct json_object *jobj_frame = json_object_new_object(); - if (jobj_frame == nullptr) { - ULOG_ERRNO("json_object_new_object", ENOMEM); - return; - } - struct json_object *jobj_info = json_object_new_object(); - if (jobj_info == nullptr) { - ULOG_ERRNO("json_object_new_object", ENOMEM); - json_object_put(jobj_frame); - return; - } - - json_object_object_add(jobj_frame, - "timestamp", - json_object_new_int64(frame->info.timestamp)); - json_object_object_add(jobj_frame, - "timescale", - json_object_new_int(frame->info.timescale)); - json_object_object_add( - jobj_frame, "index", json_object_new_int(frame->info.index)); - char *fmt = nullptr; - ret = asprintf(&fmt, - VDEF_RAW_FORMAT_TO_STR_FMT, - VDEF_RAW_FORMAT_TO_STR_ARG(&frame->format)); - if (fmt != nullptr) { - json_object_object_add( - jobj_frame, "format", json_object_new_string(fmt)); - free(fmt); - } - - json_object_object_add(jobj_info, - "full_range", - json_object_new_boolean(frame->info.full_range)); - json_object_object_add( - jobj_info, - "color_primaries", - json_object_new_string(vdef_color_primaries_to_str( - frame->info.color_primaries))); - json_object_object_add( - jobj_info, - "transfer_function", - json_object_new_string(vdef_transfer_function_to_str( - frame->info.transfer_function))); - json_object_object_add(jobj_info, - "matrix_coefs", - json_object_new_string(vdef_matrix_coefs_to_str( - frame->info.matrix_coefs))); - json_object_object_add( - jobj_info, - "width", - json_object_new_int(frame->info.resolution.width)); - json_object_object_add( - jobj_info, - "height", - json_object_new_int(frame->info.resolution.height)); - json_object_object_add(jobj_info, - "sar_width", - json_object_new_int(frame->info.sar.width)); - json_object_object_add(jobj_info, - "sar_height", - json_object_new_int(frame->info.sar.height)); - - json_object_object_add(jobj_frame, "info", jobj_info); - json_object_object_add(jobj, "frame", jobj_frame); -} - - -static void jsonFillCodedVideoInfo(struct json_object *jobj, - const struct vdef_coded_frame *frame) -{ - int ret; - struct json_object *jobj_frame = json_object_new_object(); - if (jobj_frame == nullptr) { - ULOG_ERRNO("json_object_new_object", ENOMEM); - return; - } - struct json_object *jobj_info = json_object_new_object(); - if (jobj_info == nullptr) { - ULOG_ERRNO("json_object_new_object", ENOMEM); - json_object_put(jobj_frame); - return; - } - - json_object_object_add(jobj_frame, - "timestamp", - json_object_new_int64(frame->info.timestamp)); - json_object_object_add(jobj_frame, - "timescale", - json_object_new_int(frame->info.timescale)); - json_object_object_add( - jobj_frame, "index", json_object_new_int(frame->info.index)); - char *fmt = nullptr; - ret = asprintf(&fmt, - VDEF_CODED_FORMAT_TO_STR_FMT, - VDEF_CODED_FORMAT_TO_STR_ARG(&frame->format)); - if (fmt != nullptr) { - json_object_object_add( - jobj_frame, "format", json_object_new_string(fmt)); - free(fmt); - } - - json_object_object_add(jobj_info, - "full_range", - json_object_new_boolean(frame->info.full_range)); - json_object_object_add( - jobj_info, - "color_primaries", - json_object_new_string(vdef_color_primaries_to_str( - frame->info.color_primaries))); - json_object_object_add( - jobj_info, - "transfer_function", - json_object_new_string(vdef_transfer_function_to_str( - frame->info.transfer_function))); - json_object_object_add(jobj_info, - "matrix_coefs", - json_object_new_string(vdef_matrix_coefs_to_str( - frame->info.matrix_coefs))); - json_object_object_add( - jobj_info, - "width", - json_object_new_int(frame->info.resolution.width)); - json_object_object_add( - jobj_info, - "height", - json_object_new_int(frame->info.resolution.height)); - json_object_object_add(jobj_info, - "sar_width", - json_object_new_int(frame->info.sar.width)); - json_object_object_add(jobj_info, - "sar_height", - json_object_new_int(frame->info.sar.height)); - - json_object_object_add(jobj_frame, "info", jobj_info); - json_object_object_add(jobj, "frame", jobj_frame); -} - - -int pdraw_frameMetadataToJsonStr(const struct pdraw_video_frame *frame, - struct vmeta_frame *metadata, - char *output, - unsigned int len) -{ - if (!frame || !output) - return -EINVAL; - - const char *jstr; - struct json_object *jobj = json_object_new_object(); - if (jobj == nullptr) - return -ENOMEM; - int ret = pdraw_frameMetadataToJson(frame, metadata, jobj); - if (ret < 0) - goto out; - - jstr = json_object_to_json_string(jobj); - if (strlen(jstr) + 1 > len) { - ret = -ENOBUFS; - goto out; - } - strcpy(output, jstr); - -out: - json_object_put(jobj); - return ret; -} - - -int pdraw_frameMetadataToJson(const struct pdraw_video_frame *frame, - struct vmeta_frame *metadata, - struct json_object *jobj) -{ - if (!frame || !jobj) - return -EINVAL; - - int ret = 0; - struct json_object *jobj_sub = nullptr; - - json_object_object_add( - jobj, - "format", - json_object_new_string(vdef_frame_type_to_str(frame->format))); - switch (frame->format) { - case VDEF_FRAME_TYPE_RAW: - jobj_sub = json_object_new_object(); - if (jobj_sub == nullptr) - return -ENOMEM; - jsonFillRawVideoInfo(jobj_sub, &frame->raw); - json_object_object_add(jobj, "raw", jobj_sub); - json_object_object_add( - jobj, - "has_errors", - json_object_new_int(!!(frame->raw.info.flags & - VDEF_FRAME_FLAG_VISUAL_ERROR))); - json_object_object_add( - jobj, - "is_silent", - json_object_new_int(!!(frame->raw.info.flags & - VDEF_FRAME_FLAG_SILENT))); - break; - - case VDEF_FRAME_TYPE_CODED: - jobj_sub = json_object_new_object(); - if (jobj_sub == nullptr) - return -ENOMEM; - jsonFillCodedVideoInfo(jobj_sub, &frame->coded); - json_object_object_add(jobj, "coded", jobj_sub); - json_object_object_add( - jobj, - "has_errors", - json_object_new_int(!!(frame->coded.info.flags & - VDEF_FRAME_FLAG_VISUAL_ERROR))); - json_object_object_add( - jobj, - "is_silent", - json_object_new_int(!!(frame->coded.info.flags & - VDEF_FRAME_FLAG_SILENT))); - break; - - default: - ULOGW("unknown frame format: %d(%s)", - frame->format, - vdef_frame_type_to_str(frame->format)); - break; - } - json_object_object_add( - jobj, "is_sync", json_object_new_int(frame->is_sync)); - json_object_object_add( - jobj, "is_ref", json_object_new_int(frame->is_ref)); - json_object_object_add(jobj, - "ntp_timestamp", - json_object_new_int64(frame->ntp_timestamp)); - json_object_object_add( - jobj, - "ntp_unskewed_timestamp", - json_object_new_int64(frame->ntp_unskewed_timestamp)); - json_object_object_add(jobj, - "ntp_raw_timestamp", - json_object_new_int64(frame->ntp_raw_timestamp)); - json_object_object_add( - jobj, - "ntp_raw_unskewed_timestamp", - json_object_new_int64(frame->ntp_raw_unskewed_timestamp)); - json_object_object_add(jobj, - "play_timestamp", - json_object_new_int64(frame->play_timestamp)); - json_object_object_add(jobj, - "capture_timestamp", - json_object_new_int64(frame->capture_timestamp)); - json_object_object_add(jobj, - "local_timestamp", - json_object_new_int64(frame->local_timestamp)); - if (metadata) { - jobj_sub = json_object_new_object(); - if (jobj_sub == nullptr) - return -ENOMEM; - int ret = vmeta_frame_to_json(metadata, jobj_sub); - if (ret < 0) { - if (jobj_sub != nullptr) - json_object_put(jobj_sub); - return ret; - } - json_object_object_add(jobj, "metadata", jobj_sub); - } - - return ret; -} - - -#else /* BUILD_JSON undefined */ - -int pdraw_frameMetadataToJsonStr(const struct pdraw_video_frame *frame, - struct vmeta_frame *metadata, - char *output, - unsigned int len) -{ - ULOGW("%s not implemented", __func__); - return -ENOSYS; -} - - -int pdraw_frameMetadataToJson(const struct pdraw_video_frame *frame, - struct vmeta_frame *metadata, - struct json_object *jobj) -{ - ULOGW("%s not implemented", __func__); - return -ENOSYS; -} - -#endif /* BUILD_JSON */ - - -uint64_t pdraw_getTimestampFromMbufFrame(struct mbuf_coded_video_frame *frame, - const char *key) -{ - int res; - struct mbuf_ancillary_data *data; - uint64_t ts = 0; - const void *raw_data; - size_t len; - - res = mbuf_coded_video_frame_get_ancillary_data(frame, key, &data); - if (res < 0) - return 0; - - raw_data = mbuf_ancillary_data_get_buffer(data, &len); - if (!raw_data || len != sizeof(ts)) - goto out; - memcpy(&ts, raw_data, sizeof(ts)); - -out: - mbuf_ancillary_data_unref(data); - return ts; -} - - -uint64_t pdraw_getTimestampFromMbufFrame(struct mbuf_raw_video_frame *frame, - const char *key) -{ - int res; - struct mbuf_ancillary_data *data; - uint64_t ts = 0; - const void *raw_data; - size_t len; - - res = mbuf_raw_video_frame_get_ancillary_data(frame, key, &data); - if (res < 0) - return 0; - - raw_data = mbuf_ancillary_data_get_buffer(data, &len); - if (!raw_data || len != sizeof(ts)) - goto out; - memcpy(&ts, raw_data, sizeof(ts)); - -out: - mbuf_ancillary_data_unref(data); - return ts; -} - - -struct pdraw_media_info *pdraw_mediaInfoDup(const struct pdraw_media_info *src) -{ - struct pdraw_media_info *dst; - - ULOG_ERRNO_RETURN_VAL_IF(src == NULL, EINVAL, NULL); - - dst = (pdraw_media_info *)malloc(sizeof(*src)); - if (dst == NULL) { - ULOG_ERRNO("calloc", ENOMEM); - return NULL; - } - *dst = *src; - - dst->name = NULL; - dst->path = NULL; - - dst->name = strdup(src->name); - if (dst->name == NULL) { - ULOG_ERRNO("strdup", ENOMEM); - goto failure; - } - dst->path = strdup(src->path); - if (dst->path == NULL) { - ULOG_ERRNO("strdup", ENOMEM); - goto failure; - } - - return dst; - -failure: - free((void *)dst->name); - free((void *)dst->path); - free(dst); - return NULL; -} - - -void pdraw_mediaInfoFree(struct pdraw_media_info *media_info) -{ - if (media_info == NULL) - return; - - free((void *)media_info->name); - free((void *)media_info->path); - free(media_info); -} - - -namespace Pdraw { - -std::atomic Loggable::mIdCounter(0); - -Loggable::Loggable() -{ - mName = std::string(__func__) + "#" + std::to_string(++mIdCounter); - self = this; -} - -void Loggable::setName(std::string &name) -{ - mName = name; -} - -void Loggable::setName(const char *name) -{ - mName = std::string(name); -} - -} // namespace Pdraw +/** + * Parrot Drones Awesome Video Viewer Library + * Utilities + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_utils +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_utils.hpp" + +#include +#include + +#ifdef BUILD_JSON +# include +#endif + +extern "C" { +const char *PDRAW_ANCILLARY_DATA_KEY_VIDEOFRAME = "pdraw.video.frame"; +} + +/* Approximation without the Simphson integration; + * see http://dev.theomader.com/gaussian-kernel-calculator/ */ +void pdraw_gaussianDistribution(float *samples, + unsigned int sampleCount, + float sigma) +{ + unsigned int i; + float a, x, g, start, step, sum; + + if (samples == nullptr) + return; + + if (sampleCount == 0) + return; + if (!(sampleCount & 1)) + sampleCount--; + if (sampleCount == 0) + return; + + a = 1.f / (sqrtf(2.f * M_PI) * sigma); + start = -(float)sampleCount / 2.f; + step = (sampleCount > 1) + ? (float)sampleCount / ((float)sampleCount - 1.f) + : 0.f; + + /* Compute coefs */ + for (i = 0, x = start, sum = 0.f; i < (sampleCount + 1) / 2; + i++, x += step) { + g = expf(-x * x / (2.f * sigma * sigma)) * a; + sum += (i == sampleCount / 2) ? g : g * 2.f; + /* Array is symmetric so write to one side of the array only */ + *(samples + i) = g; + } + + /* Normalization */ + for (i = 0; i < (sampleCount + 1) / 2; i++) { + g = *(samples + i) / sum; + /* Array is symmetric so write to both sides of the array + * (the middle value is written twice but it doesn't matter) */ + *(samples + i) = g; + *(samples + sampleCount - 1 - i) = g; + } +} + + +void pdraw_friendlyTimeFromUs(uint64_t time, + unsigned int *hrs, + unsigned int *min, + unsigned int *sec, + unsigned int *msec) +{ + unsigned int _hrs = + (unsigned int)((time + 500) / 1000 / 60 / 60) / 1000; + unsigned int _min = + (unsigned int)((time + 500) / 1000 / 60 - _hrs * 60000) / 1000; + unsigned int _sec = (unsigned int)((time + 500) / 1000 - + _hrs * 60 * 60000 - _min * 60000) / + 1000; + unsigned int _msec = + (unsigned int)((time + 500) / 1000 - _hrs * 60 * 60000 - + _min * 60000 - _sec * 1000); + if (hrs) + *hrs = _hrs; + if (min) + *min = _min; + if (sec) + *sec = _sec; + if (msec) + *msec = _msec; +} + + +const char *pdraw_hmdModelStr(enum pdraw_hmd_model val) +{ + switch (val) { + default: + case PDRAW_HMD_MODEL_COCKPITGLASSES: + return "COCKPITGLASSES"; + case PDRAW_HMD_MODEL_COCKPITGLASSES_2: + return "COCKPITGLASSES_2"; + } +} + + +const char *pdraw_pipelineModeStr(enum pdraw_pipeline_mode val) +{ + switch (val) { + default: + case PDRAW_PIPELINE_MODE_DECODE_ALL: + return "DECODE_ALL"; + case PDRAW_PIPELINE_MODE_DECODE_NONE: + return "DECODE_NONE"; + } +} + + +const char *pdraw_playbackTypeStr(enum pdraw_playback_type val) +{ + switch (val) { + default: + case PDRAW_PLAYBACK_TYPE_UNKNOWN: + return "UNKNOWN"; + case PDRAW_PLAYBACK_TYPE_LIVE: + return "LIVE"; + case PDRAW_PLAYBACK_TYPE_REPLAY: + return "REPLAY"; + } +} + + +const char *pdraw_mediaTypeStr(enum pdraw_media_type val) +{ + switch (val) { + default: + case PDRAW_MEDIA_TYPE_UNKNOWN: + return "UNKNOWN"; + case PDRAW_MEDIA_TYPE_VIDEO: + return "VIDEO"; + } +} + + +const char *pdraw_videoTypeStr(enum pdraw_video_type val) +{ + switch (val) { + default: + case PDRAW_VIDEO_TYPE_DEFAULT_CAMERA: + return "DEFAULT_CAMERA"; + } +} + + +const char *pdraw_histogramChannelStr(enum pdraw_histogram_channel val) +{ + switch (val) { + default: + return "UNKNOWN"; + case PDRAW_HISTOGRAM_CHANNEL_RED: + return "RED"; + case PDRAW_HISTOGRAM_CHANNEL_GREEN: + return "GREEN"; + case PDRAW_HISTOGRAM_CHANNEL_BLUE: + return "BLUE"; + case PDRAW_HISTOGRAM_CHANNEL_LUMA: + return "LUMA"; + } +} + + +const char *pdraw_videoRendererSchedulingModeStr( + enum pdraw_video_renderer_scheduling_mode val) +{ + switch (val) { + case PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ASAP: + return "ASAP"; + case PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ADAPTIVE: + return "ADAPTIVE"; + case PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_MAX: + default: + return "UNKNOWN"; + } +} + + +const char * +pdraw_videoRendererFillModeStr(enum pdraw_video_renderer_fill_mode val) +{ + switch (val) { + case PDRAW_VIDEO_RENDERER_FILL_MODE_FIT: + return "FIT"; + case PDRAW_VIDEO_RENDERER_FILL_MODE_CROP: + return "CROP"; + case PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_CROP: + return "FIT_PAD_BLUR_CROP"; + case PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_EXTEND: + return "FIT_PAD_BLUR_EXTEND"; + case PDRAW_VIDEO_RENDERER_FILL_MODE_MAX: + default: + return "UNKNOWN"; + } +} + + +const char *pdraw_videoRendererTransitionFlagStr( + enum pdraw_video_renderer_transition_flag val) +{ + switch (val) { + default: + return "NONE"; + case PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_SOS: + return "SOS"; + case PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_EOS: + return "EOS"; + case PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_RECONFIGURE: + return "RECONFIGURE"; + case PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_TIMEOUT: + return "TIMEOUT"; + case PDRAW_VIDEO_RENDERER_TRANSITION_FLAG_PHOTO_TRIGGER: + return "PHOTO_TRIGGER"; + } +} + + +#ifdef BUILD_JSON +static void jsonFillRawVideoInfo(struct json_object *jobj, + const struct vdef_raw_frame *frame) +{ + int ret; + struct json_object *jobj_frame = json_object_new_object(); + if (jobj_frame == nullptr) { + ULOG_ERRNO("json_object_new_object", ENOMEM); + return; + } + struct json_object *jobj_info = json_object_new_object(); + if (jobj_info == nullptr) { + ULOG_ERRNO("json_object_new_object", ENOMEM); + json_object_put(jobj_frame); + return; + } + + json_object_object_add(jobj_frame, + "timestamp", + json_object_new_int64(frame->info.timestamp)); + json_object_object_add(jobj_frame, + "timescale", + json_object_new_int(frame->info.timescale)); + json_object_object_add( + jobj_frame, "index", json_object_new_int(frame->info.index)); + char *fmt = nullptr; + ret = asprintf(&fmt, + VDEF_RAW_FORMAT_TO_STR_FMT, + VDEF_RAW_FORMAT_TO_STR_ARG(&frame->format)); + if (fmt != nullptr) { + json_object_object_add( + jobj_frame, "format", json_object_new_string(fmt)); + free(fmt); + } + + json_object_object_add(jobj_info, + "full_range", + json_object_new_boolean(frame->info.full_range)); + json_object_object_add( + jobj_info, + "color_primaries", + json_object_new_string(vdef_color_primaries_to_str( + frame->info.color_primaries))); + json_object_object_add( + jobj_info, + "transfer_function", + json_object_new_string(vdef_transfer_function_to_str( + frame->info.transfer_function))); + json_object_object_add(jobj_info, + "matrix_coefs", + json_object_new_string(vdef_matrix_coefs_to_str( + frame->info.matrix_coefs))); + json_object_object_add( + jobj_info, + "width", + json_object_new_int(frame->info.resolution.width)); + json_object_object_add( + jobj_info, + "height", + json_object_new_int(frame->info.resolution.height)); + json_object_object_add(jobj_info, + "sar_width", + json_object_new_int(frame->info.sar.width)); + json_object_object_add(jobj_info, + "sar_height", + json_object_new_int(frame->info.sar.height)); + + json_object_object_add(jobj_frame, "info", jobj_info); + json_object_object_add(jobj, "frame", jobj_frame); +} + + +static void jsonFillCodedVideoInfo(struct json_object *jobj, + const struct vdef_coded_frame *frame) +{ + int ret; + struct json_object *jobj_frame = json_object_new_object(); + if (jobj_frame == nullptr) { + ULOG_ERRNO("json_object_new_object", ENOMEM); + return; + } + struct json_object *jobj_info = json_object_new_object(); + if (jobj_info == nullptr) { + ULOG_ERRNO("json_object_new_object", ENOMEM); + json_object_put(jobj_frame); + return; + } + + json_object_object_add(jobj_frame, + "timestamp", + json_object_new_int64(frame->info.timestamp)); + json_object_object_add(jobj_frame, + "timescale", + json_object_new_int(frame->info.timescale)); + json_object_object_add( + jobj_frame, "index", json_object_new_int(frame->info.index)); + char *fmt = nullptr; + ret = asprintf(&fmt, + VDEF_CODED_FORMAT_TO_STR_FMT, + VDEF_CODED_FORMAT_TO_STR_ARG(&frame->format)); + if (fmt != nullptr) { + json_object_object_add( + jobj_frame, "format", json_object_new_string(fmt)); + free(fmt); + } + + json_object_object_add(jobj_info, + "full_range", + json_object_new_boolean(frame->info.full_range)); + json_object_object_add( + jobj_info, + "color_primaries", + json_object_new_string(vdef_color_primaries_to_str( + frame->info.color_primaries))); + json_object_object_add( + jobj_info, + "transfer_function", + json_object_new_string(vdef_transfer_function_to_str( + frame->info.transfer_function))); + json_object_object_add(jobj_info, + "matrix_coefs", + json_object_new_string(vdef_matrix_coefs_to_str( + frame->info.matrix_coefs))); + json_object_object_add( + jobj_info, + "width", + json_object_new_int(frame->info.resolution.width)); + json_object_object_add( + jobj_info, + "height", + json_object_new_int(frame->info.resolution.height)); + json_object_object_add(jobj_info, + "sar_width", + json_object_new_int(frame->info.sar.width)); + json_object_object_add(jobj_info, + "sar_height", + json_object_new_int(frame->info.sar.height)); + + json_object_object_add(jobj_frame, "info", jobj_info); + json_object_object_add(jobj, "frame", jobj_frame); +} + + +int pdraw_frameMetadataToJsonStr(const struct pdraw_video_frame *frame, + struct vmeta_frame *metadata, + char *output, + unsigned int len) +{ + if (!frame || !output) + return -EINVAL; + + const char *jstr; + struct json_object *jobj = json_object_new_object(); + if (jobj == nullptr) + return -ENOMEM; + int ret = pdraw_frameMetadataToJson(frame, metadata, jobj); + if (ret < 0) + goto out; + + jstr = json_object_to_json_string(jobj); + if (strlen(jstr) + 1 > len) { + ret = -ENOBUFS; + goto out; + } + strcpy(output, jstr); + +out: + json_object_put(jobj); + return ret; +} + + +int pdraw_frameMetadataToJson(const struct pdraw_video_frame *frame, + struct vmeta_frame *metadata, + struct json_object *jobj) +{ + if (!frame || !jobj) + return -EINVAL; + + int ret = 0; + struct json_object *jobj_sub = nullptr; + + json_object_object_add( + jobj, + "format", + json_object_new_string(vdef_frame_type_to_str(frame->format))); + switch (frame->format) { + case VDEF_FRAME_TYPE_RAW: + jobj_sub = json_object_new_object(); + if (jobj_sub == nullptr) + return -ENOMEM; + jsonFillRawVideoInfo(jobj_sub, &frame->raw); + json_object_object_add(jobj, "raw", jobj_sub); + json_object_object_add( + jobj, + "has_errors", + json_object_new_int(!!(frame->raw.info.flags & + VDEF_FRAME_FLAG_VISUAL_ERROR))); + json_object_object_add( + jobj, + "is_silent", + json_object_new_int(!!(frame->raw.info.flags & + VDEF_FRAME_FLAG_SILENT))); + break; + + case VDEF_FRAME_TYPE_CODED: + jobj_sub = json_object_new_object(); + if (jobj_sub == nullptr) + return -ENOMEM; + jsonFillCodedVideoInfo(jobj_sub, &frame->coded); + json_object_object_add(jobj, "coded", jobj_sub); + json_object_object_add( + jobj, + "has_errors", + json_object_new_int(!!(frame->coded.info.flags & + VDEF_FRAME_FLAG_VISUAL_ERROR))); + json_object_object_add( + jobj, + "is_silent", + json_object_new_int(!!(frame->coded.info.flags & + VDEF_FRAME_FLAG_SILENT))); + break; + + default: + ULOGW("unknown frame format: %d(%s)", + frame->format, + vdef_frame_type_to_str(frame->format)); + break; + } + json_object_object_add( + jobj, "is_sync", json_object_new_int(frame->is_sync)); + json_object_object_add( + jobj, "is_ref", json_object_new_int(frame->is_ref)); + json_object_object_add(jobj, + "ntp_timestamp", + json_object_new_int64(frame->ntp_timestamp)); + json_object_object_add( + jobj, + "ntp_unskewed_timestamp", + json_object_new_int64(frame->ntp_unskewed_timestamp)); + json_object_object_add(jobj, + "ntp_raw_timestamp", + json_object_new_int64(frame->ntp_raw_timestamp)); + json_object_object_add( + jobj, + "ntp_raw_unskewed_timestamp", + json_object_new_int64(frame->ntp_raw_unskewed_timestamp)); + json_object_object_add(jobj, + "play_timestamp", + json_object_new_int64(frame->play_timestamp)); + json_object_object_add(jobj, + "capture_timestamp", + json_object_new_int64(frame->capture_timestamp)); + json_object_object_add(jobj, + "local_timestamp", + json_object_new_int64(frame->local_timestamp)); + if (metadata) { + jobj_sub = json_object_new_object(); + if (jobj_sub == nullptr) + return -ENOMEM; + int ret = vmeta_frame_to_json(metadata, jobj_sub); + if (ret < 0) { + if (jobj_sub != nullptr) + json_object_put(jobj_sub); + return ret; + } + json_object_object_add(jobj, "metadata", jobj_sub); + } + + return ret; +} + + +#else /* BUILD_JSON undefined */ + +int pdraw_frameMetadataToJsonStr(const struct pdraw_video_frame *frame, + struct vmeta_frame *metadata, + char *output, + unsigned int len) +{ + ULOGW("%s not implemented", __func__); + return -ENOSYS; +} + + +int pdraw_frameMetadataToJson(const struct pdraw_video_frame *frame, + struct vmeta_frame *metadata, + struct json_object *jobj) +{ + ULOGW("%s not implemented", __func__); + return -ENOSYS; +} + +#endif /* BUILD_JSON */ + + +uint64_t pdraw_getTimestampFromMbufFrame(struct mbuf_coded_video_frame *frame, + const char *key) +{ + int res; + struct mbuf_ancillary_data *data; + uint64_t ts = 0; + const void *raw_data; + size_t len; + + res = mbuf_coded_video_frame_get_ancillary_data(frame, key, &data); + if (res < 0) + return 0; + + raw_data = mbuf_ancillary_data_get_buffer(data, &len); + if (!raw_data || len != sizeof(ts)) + goto out; + memcpy(&ts, raw_data, sizeof(ts)); + +out: + mbuf_ancillary_data_unref(data); + return ts; +} + + +uint64_t pdraw_getTimestampFromMbufFrame(struct mbuf_raw_video_frame *frame, + const char *key) +{ + int res; + struct mbuf_ancillary_data *data; + uint64_t ts = 0; + const void *raw_data; + size_t len; + + res = mbuf_raw_video_frame_get_ancillary_data(frame, key, &data); + if (res < 0) + return 0; + + raw_data = mbuf_ancillary_data_get_buffer(data, &len); + if (!raw_data || len != sizeof(ts)) + goto out; + memcpy(&ts, raw_data, sizeof(ts)); + +out: + mbuf_ancillary_data_unref(data); + return ts; +} + + +struct pdraw_media_info *pdraw_mediaInfoDup(const struct pdraw_media_info *src) +{ + struct pdraw_media_info *dst; + + ULOG_ERRNO_RETURN_VAL_IF(src == NULL, EINVAL, NULL); + + dst = (pdraw_media_info *)malloc(sizeof(*src)); + if (dst == NULL) { + ULOG_ERRNO("calloc", ENOMEM); + return NULL; + } + *dst = *src; + + dst->name = NULL; + dst->path = NULL; + + dst->name = strdup(src->name); + if (dst->name == NULL) { + ULOG_ERRNO("strdup", ENOMEM); + goto failure; + } + dst->path = strdup(src->path); + if (dst->path == NULL) { + ULOG_ERRNO("strdup", ENOMEM); + goto failure; + } + + return dst; + +failure: + free((void *)dst->name); + free((void *)dst->path); + free(dst); + return NULL; +} + + +void pdraw_mediaInfoFree(struct pdraw_media_info *media_info) +{ + if (media_info == NULL) + return; + + free((void *)media_info->name); + free((void *)media_info->path); + free(media_info); +} + + +namespace Pdraw { + +std::atomic Loggable::mIdCounter(0); + +Loggable::Loggable() +{ + mName = std::string(__func__) + "#" + std::to_string(++mIdCounter); + self = this; +} + +void Loggable::setName(std::string &name) +{ + mName = name; +} + +void Loggable::setName(const char *name) +{ + mName = std::string(name); +} + +} // namespace Pdraw diff --git a/libpdraw/src/pdraw_utils.hpp b/libpdraw/src/pdraw_utils.hpp index eec3313..8da52a9 100644 --- a/libpdraw/src/pdraw_utils.hpp +++ b/libpdraw/src/pdraw_utils.hpp @@ -1,217 +1,217 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Utilities - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_UTILS_HPP_ -#define _PDRAW_UTILS_HPP_ - -/* This file includes to get ULOG_xxx definitions. In order for each - * file to keep its own logging tag, we must ensure that ULOG_TAG is defined - * BEFORE including this file */ -#ifndef ULOG_TAG -# error Please define ULOG_TAG before including pdraw_utils.hpp -#else -# include -#endif - -#include -#include - -#include - -#include - -#include - - -/* Logging macros */ -/* These macros must be either called from a member of a Loggable class, - * of from a function where a local 'self' pointer to a Loggable class is - * defined. - * These macros use the same semantics as their ULOGx variants, except that - * they add the Loggable name before the logged string - */ -#define _PDRAW_LOG_INT(_pri, _fmt, ...) \ - ULOG_PRI(_pri, "%s: " _fmt, Loggable::_getCName(self), ##__VA_ARGS__) -#define PDRAW_LOGD(_fmt, ...) _PDRAW_LOG_INT(ULOG_DEBUG, _fmt, ##__VA_ARGS__) -#define PDRAW_LOGI(_fmt, ...) _PDRAW_LOG_INT(ULOG_INFO, _fmt, ##__VA_ARGS__) -#define PDRAW_LOGW(_fmt, ...) _PDRAW_LOG_INT(ULOG_WARN, _fmt, ##__VA_ARGS__) -#define PDRAW_LOGE(_fmt, ...) _PDRAW_LOG_INT(ULOG_ERR, _fmt, ##__VA_ARGS__) -#define PDRAW_LOG_ERRNO(_fmt, _err, ...) \ - ULOGE_ERRNO( \ - (_err), "%s: " _fmt, Loggable::_getCName(self), ##__VA_ARGS__) -#define PDRAW_LOG_ERRNO_RETURN_IF(_cond, _err) \ - do { \ - if (ULOG_UNLIKELY(_cond)) { \ - PDRAW_LOG_ERRNO("", (_err)); \ - return; \ - } \ - } while (0) -#define PDRAW_LOG_ERRNO_RETURN_ERR_IF(_cond, _err) \ - do { \ - if (ULOG_UNLIKELY(_cond)) { \ - int __pdraw_errno__err = (_err); \ - PDRAW_LOG_ERRNO("", (__pdraw_errno__err)); \ - return -(__pdraw_errno__err); \ - } \ - } while (0) -#define PDRAW_LOG_ERRNO_RETURN_VAL_IF(_cond, _err, _val) \ - do { \ - if (ULOG_UNLIKELY(_cond)) { \ - PDRAW_LOG_ERRNO("", (_err)); \ - /* codecheck_ignore[RETURN_PARENTHESES] */ \ - return (_val); \ - } \ - } while (0) - - -#define PDRAW_STATIC_ASSERT(x) typedef char __STATIC_ASSERT__[(x) ? 1 : -1] - - -void pdraw_gaussianDistribution(float *samples, - unsigned int sampleCount, - float sigma); - - -void pdraw_friendlyTimeFromUs(uint64_t time, - unsigned int *hrs, - unsigned int *min, - unsigned int *sec, - unsigned int *msec); - - -const char *pdraw_hmdModelStr(enum pdraw_hmd_model val); - - -const char *pdraw_pipelineModeStr(enum pdraw_pipeline_mode val); - - -const char *pdraw_playbackTypeStr(enum pdraw_playback_type val); - - -const char *pdraw_mediaTypeStr(enum pdraw_media_type val); - - -const char *pdraw_videoTypeStr(enum pdraw_video_type val); - - -const char *pdraw_histogramChannelStr(enum pdraw_histogram_channel val); - - -const char *pdraw_videoRendererSchedulingModeStr( - enum pdraw_video_renderer_scheduling_mode val); - - -const char * -pdraw_videoRendererFillModeStr(enum pdraw_video_renderer_fill_mode val); - - -const char *pdraw_videoRendererTransitionFlagStr( - enum pdraw_video_renderer_transition_flag val); - - -int pdraw_frameMetadataToJson(const struct pdraw_video_frame *frame, - struct vmeta_frame *metadata, - struct json_object *jobj); - - -int pdraw_frameMetadataToJsonStr(const struct pdraw_video_frame *frame, - struct vmeta_frame *metadata, - char *output, - unsigned int len); - -uint64_t pdraw_getTimestampFromMbufFrame(struct mbuf_coded_video_frame *frame, - const char *key); - -uint64_t pdraw_getTimestampFromMbufFrame(struct mbuf_raw_video_frame *frame, - const char *key); - -struct pdraw_media_info *pdraw_mediaInfoDup(const struct pdraw_media_info *src); - -void pdraw_mediaInfoFree(struct pdraw_media_info *media_info); - -/* */ - - -static inline char *xstrdup(const char *s) -{ - return s == nullptr ? nullptr : strdup(s); -} - - -static inline int xstrcmp(const char *s1, const char *s2) -{ - if (s1 == nullptr && s2 == nullptr) - return 0; - else if (s1 == nullptr) - return -1; - else if (s2 == nullptr) - return 1; - return strcmp(s1, s2); -} - - -/* Loggable internals */ - -namespace Pdraw { -class Loggable { -public: - std::string &getName() - { - return mName; - } - - const char *getCName() - { - return mName.c_str(); - } - - /* Helper function for log macros, do not call directly */ - static const char *_getCName(Loggable *l) - { - if (l == nullptr) - return "(NULL)"; - return l->getCName(); - } - -protected: - Loggable(); - - void setName(std::string &name); - - void setName(const char *name); - - std::string mName; - static std::atomic mIdCounter; - Loggable *self; -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_UTILS_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Utilities + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_UTILS_HPP_ +#define _PDRAW_UTILS_HPP_ + +/* This file includes to get ULOG_xxx definitions. In order for each + * file to keep its own logging tag, we must ensure that ULOG_TAG is defined + * BEFORE including this file */ +#ifndef ULOG_TAG +# error Please define ULOG_TAG before including pdraw_utils.hpp +#else +# include +#endif + +#include +#include + +#include + +#include + +#include + + +/* Logging macros */ +/* These macros must be either called from a member of a Loggable class, + * of from a function where a local 'self' pointer to a Loggable class is + * defined. + * These macros use the same semantics as their ULOGx variants, except that + * they add the Loggable name before the logged string + */ +#define _PDRAW_LOG_INT(_pri, _fmt, ...) \ + ULOG_PRI(_pri, "%s: " _fmt, Loggable::_getCName(self), ##__VA_ARGS__) +#define PDRAW_LOGD(_fmt, ...) _PDRAW_LOG_INT(ULOG_DEBUG, _fmt, ##__VA_ARGS__) +#define PDRAW_LOGI(_fmt, ...) _PDRAW_LOG_INT(ULOG_INFO, _fmt, ##__VA_ARGS__) +#define PDRAW_LOGW(_fmt, ...) _PDRAW_LOG_INT(ULOG_WARN, _fmt, ##__VA_ARGS__) +#define PDRAW_LOGE(_fmt, ...) _PDRAW_LOG_INT(ULOG_ERR, _fmt, ##__VA_ARGS__) +#define PDRAW_LOG_ERRNO(_fmt, _err, ...) \ + ULOGE_ERRNO( \ + (_err), "%s: " _fmt, Loggable::_getCName(self), ##__VA_ARGS__) +#define PDRAW_LOG_ERRNO_RETURN_IF(_cond, _err) \ + do { \ + if (ULOG_UNLIKELY(_cond)) { \ + PDRAW_LOG_ERRNO("", (_err)); \ + return; \ + } \ + } while (0) +#define PDRAW_LOG_ERRNO_RETURN_ERR_IF(_cond, _err) \ + do { \ + if (ULOG_UNLIKELY(_cond)) { \ + int __pdraw_errno__err = (_err); \ + PDRAW_LOG_ERRNO("", (__pdraw_errno__err)); \ + return -(__pdraw_errno__err); \ + } \ + } while (0) +#define PDRAW_LOG_ERRNO_RETURN_VAL_IF(_cond, _err, _val) \ + do { \ + if (ULOG_UNLIKELY(_cond)) { \ + PDRAW_LOG_ERRNO("", (_err)); \ + /* codecheck_ignore[RETURN_PARENTHESES] */ \ + return (_val); \ + } \ + } while (0) + + +#define PDRAW_STATIC_ASSERT(x) typedef char __STATIC_ASSERT__[(x) ? 1 : -1] + + +void pdraw_gaussianDistribution(float *samples, + unsigned int sampleCount, + float sigma); + + +void pdraw_friendlyTimeFromUs(uint64_t time, + unsigned int *hrs, + unsigned int *min, + unsigned int *sec, + unsigned int *msec); + + +const char *pdraw_hmdModelStr(enum pdraw_hmd_model val); + + +const char *pdraw_pipelineModeStr(enum pdraw_pipeline_mode val); + + +const char *pdraw_playbackTypeStr(enum pdraw_playback_type val); + + +const char *pdraw_mediaTypeStr(enum pdraw_media_type val); + + +const char *pdraw_videoTypeStr(enum pdraw_video_type val); + + +const char *pdraw_histogramChannelStr(enum pdraw_histogram_channel val); + + +const char *pdraw_videoRendererSchedulingModeStr( + enum pdraw_video_renderer_scheduling_mode val); + + +const char * +pdraw_videoRendererFillModeStr(enum pdraw_video_renderer_fill_mode val); + + +const char *pdraw_videoRendererTransitionFlagStr( + enum pdraw_video_renderer_transition_flag val); + + +int pdraw_frameMetadataToJson(const struct pdraw_video_frame *frame, + struct vmeta_frame *metadata, + struct json_object *jobj); + + +int pdraw_frameMetadataToJsonStr(const struct pdraw_video_frame *frame, + struct vmeta_frame *metadata, + char *output, + unsigned int len); + +uint64_t pdraw_getTimestampFromMbufFrame(struct mbuf_coded_video_frame *frame, + const char *key); + +uint64_t pdraw_getTimestampFromMbufFrame(struct mbuf_raw_video_frame *frame, + const char *key); + +struct pdraw_media_info *pdraw_mediaInfoDup(const struct pdraw_media_info *src); + +void pdraw_mediaInfoFree(struct pdraw_media_info *media_info); + +/* */ + + +static inline char *xstrdup(const char *s) +{ + return s == nullptr ? nullptr : strdup(s); +} + + +static inline int xstrcmp(const char *s1, const char *s2) +{ + if (s1 == nullptr && s2 == nullptr) + return 0; + else if (s1 == nullptr) + return -1; + else if (s2 == nullptr) + return 1; + return strcmp(s1, s2); +} + + +/* Loggable internals */ + +namespace Pdraw { +class Loggable { +public: + std::string &getName() + { + return mName; + } + + const char *getCName() + { + return mName.c_str(); + } + + /* Helper function for log macros, do not call directly */ + static const char *_getCName(Loggable *l) + { + if (l == nullptr) + return "(NULL)"; + return l->getCName(); + } + +protected: + Loggable(); + + void setName(std::string &name); + + void setName(const char *name); + + std::string mName; + static std::atomic mIdCounter; + Loggable *self; +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_UTILS_HPP_ */ diff --git a/libpdraw/src/pdraw_video_pres_stats.cpp b/libpdraw/src/pdraw_video_pres_stats.cpp index e1ad422..b01a22d 100644 --- a/libpdraw/src/pdraw_video_pres_stats.cpp +++ b/libpdraw/src/pdraw_video_pres_stats.cpp @@ -1,100 +1,100 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Video presentation statistics - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_video_pres_stats -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include - -#include "pdraw_video_pres_stats.hpp" - -namespace Pdraw { - - -VideoPresStats::VideoPresStats(void) : - timestamp(0), presentationFrameCount(0), - presentationTimestampDeltaIntegral(0), - presentationTimestampDeltaIntegralSq(0), - presentationTimingErrorIntegral(0), - presentationTimingErrorIntegralSq(0), - presentationEstimatedLatencyIntegral(0), - presentationEstimatedLatencyIntegralSq(0), - playerLatencyIntegral(0), playerLatencyIntegralSq(0), - estimatedLatencyPrecisionIntegral(0) -{ -} - - -int VideoPresStats::writeMsg(struct pomp_msg *msg, uint32_t msgid) -{ - ULOG_ERRNO_RETURN_ERR_IF(msg == nullptr, EINVAL); - - return pomp_msg_write(msg, - msgid, - "%" PRIu64 "%" PRIu32 "%" PRIu64 "%" PRIu64 - "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 - "%" PRIu64 "%" PRIu64 "%" PRIu64, - timestamp, - presentationFrameCount, - presentationTimestampDeltaIntegral, - presentationTimestampDeltaIntegralSq, - presentationTimingErrorIntegral, - presentationTimingErrorIntegralSq, - presentationEstimatedLatencyIntegral, - presentationEstimatedLatencyIntegralSq, - playerLatencyIntegral, - playerLatencyIntegralSq, - estimatedLatencyPrecisionIntegral); -} - - -int VideoPresStats::readMsg(const struct pomp_msg *msg) -{ - ULOG_ERRNO_RETURN_ERR_IF(msg == nullptr, EINVAL); - - return pomp_msg_read(msg, - "%" PRIu64 "%" PRIu32 "%" PRIu64 "%" PRIu64 - "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 - "%" PRIu64 "%" PRIu64 "%" PRIu64, - ×tamp, - &presentationFrameCount, - &presentationTimestampDeltaIntegral, - &presentationTimestampDeltaIntegralSq, - &presentationTimingErrorIntegral, - &presentationTimingErrorIntegralSq, - &presentationEstimatedLatencyIntegral, - &presentationEstimatedLatencyIntegralSq, - &playerLatencyIntegral, - &playerLatencyIntegralSq, - &estimatedLatencyPrecisionIntegral); -} - -} /* namespace Pdraw */ +/** + * Parrot Drones Awesome Video Viewer Library + * Video presentation statistics + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_video_pres_stats +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include + +#include "pdraw_video_pres_stats.hpp" + +namespace Pdraw { + + +VideoPresStats::VideoPresStats(void) : + timestamp(0), presentationFrameCount(0), + presentationTimestampDeltaIntegral(0), + presentationTimestampDeltaIntegralSq(0), + presentationTimingErrorIntegral(0), + presentationTimingErrorIntegralSq(0), + presentationEstimatedLatencyIntegral(0), + presentationEstimatedLatencyIntegralSq(0), + playerLatencyIntegral(0), playerLatencyIntegralSq(0), + estimatedLatencyPrecisionIntegral(0) +{ +} + + +int VideoPresStats::writeMsg(struct pomp_msg *msg, uint32_t msgid) +{ + ULOG_ERRNO_RETURN_ERR_IF(msg == nullptr, EINVAL); + + return pomp_msg_write(msg, + msgid, + "%" PRIu64 "%" PRIu32 "%" PRIu64 "%" PRIu64 + "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 + "%" PRIu64 "%" PRIu64 "%" PRIu64, + timestamp, + presentationFrameCount, + presentationTimestampDeltaIntegral, + presentationTimestampDeltaIntegralSq, + presentationTimingErrorIntegral, + presentationTimingErrorIntegralSq, + presentationEstimatedLatencyIntegral, + presentationEstimatedLatencyIntegralSq, + playerLatencyIntegral, + playerLatencyIntegralSq, + estimatedLatencyPrecisionIntegral); +} + + +int VideoPresStats::readMsg(const struct pomp_msg *msg) +{ + ULOG_ERRNO_RETURN_ERR_IF(msg == nullptr, EINVAL); + + return pomp_msg_read(msg, + "%" PRIu64 "%" PRIu32 "%" PRIu64 "%" PRIu64 + "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 + "%" PRIu64 "%" PRIu64 "%" PRIu64, + ×tamp, + &presentationFrameCount, + &presentationTimestampDeltaIntegral, + &presentationTimestampDeltaIntegralSq, + &presentationTimingErrorIntegral, + &presentationTimingErrorIntegralSq, + &presentationEstimatedLatencyIntegral, + &presentationEstimatedLatencyIntegralSq, + &playerLatencyIntegral, + &playerLatencyIntegralSq, + &estimatedLatencyPrecisionIntegral); +} + +} /* namespace Pdraw */ diff --git a/libpdraw/src/pdraw_video_pres_stats.hpp b/libpdraw/src/pdraw_video_pres_stats.hpp index 38b22d0..4876347 100644 --- a/libpdraw/src/pdraw_video_pres_stats.hpp +++ b/libpdraw/src/pdraw_video_pres_stats.hpp @@ -1,108 +1,108 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * Video presentation statistics - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _PDRAW_VIDEO_PRES_STATS_HPP_ -#define _PDRAW_VIDEO_PRES_STATS_HPP_ - -#include - -#include - - -namespace Pdraw { - -/** - * Video presentation statistics - */ -class VideoPresStats { -public: - VideoPresStats(void); - - ~VideoPresStats(void) {} - - /* Write video presentation statistics to a pomp_msg */ - int writeMsg(struct pomp_msg *msg, uint32_t msgid); - - /* Read video presentation statistics from a pomp_msg */ - int readMsg(const struct pomp_msg *msg); - - /* Timestamp associated with the video statistics (us, monotonic); - * This must be set on the receiver side to a monotonic timestamp on - * the sender's clock (e.g. a frame capture timestamp) */ - uint64_t timestamp; - - /* Presentation frame counter i.e. frames that reach presentation; this - * value can be used with the integral time values below to compute - * average values over a time period */ - uint32_t presentationFrameCount; - - /* Presentation frame timestamp delta integral value; timestamp delta is - * the time difference between two consecutive presentation frames - * acquisition timestamps */ - uint64_t presentationTimestampDeltaIntegral; - - /* Presentation frame timestamp delta squared integral value */ - uint64_t presentationTimestampDeltaIntegralSq; - - /* Frame presentation timing error integral value; the timing error - * is the absolute difference between acquisition timestamp delta - * and presentation timestamp delta for two consecutive presentation - * frames */ - uint64_t presentationTimingErrorIntegral; - - /* Frame presentation timing error squared integral value */ - uint64_t presentationTimingErrorIntegralSq; - - /* Frame estimated latency integral value; the estimated latency is the - * difference between a frame presentation timestamp and acquisition - * timestamp using an estimation of the clock difference between the - * sender and the receiver */ - uint64_t presentationEstimatedLatencyIntegral; - - /* Frame estimated latency squared integral value */ - uint64_t presentationEstimatedLatencyIntegralSq; - - /* Player-side frame latency integral value; the player latency is the - * difference between a frame presentation timestamp and the output - * timestamp of the frame from the reeciver */ - uint64_t playerLatencyIntegral; - - /* Player-side frame latency squared integral value */ - uint64_t playerLatencyIntegralSq; - - /* Estimated latency precision integral value; this is the precision of - * the estimation of the clock difference between the sender and the - * receiver */ - uint64_t estimatedLatencyPrecisionIntegral; -}; - -} /* namespace Pdraw */ - -#endif /* !_PDRAW_VIDEO_PRES_STATS_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer Library + * Video presentation statistics + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _PDRAW_VIDEO_PRES_STATS_HPP_ +#define _PDRAW_VIDEO_PRES_STATS_HPP_ + +#include + +#include + + +namespace Pdraw { + +/** + * Video presentation statistics + */ +class VideoPresStats { +public: + VideoPresStats(void); + + ~VideoPresStats(void) {} + + /* Write video presentation statistics to a pomp_msg */ + int writeMsg(struct pomp_msg *msg, uint32_t msgid); + + /* Read video presentation statistics from a pomp_msg */ + int readMsg(const struct pomp_msg *msg); + + /* Timestamp associated with the video statistics (us, monotonic); + * This must be set on the receiver side to a monotonic timestamp on + * the sender's clock (e.g. a frame capture timestamp) */ + uint64_t timestamp; + + /* Presentation frame counter i.e. frames that reach presentation; this + * value can be used with the integral time values below to compute + * average values over a time period */ + uint32_t presentationFrameCount; + + /* Presentation frame timestamp delta integral value; timestamp delta is + * the time difference between two consecutive presentation frames + * acquisition timestamps */ + uint64_t presentationTimestampDeltaIntegral; + + /* Presentation frame timestamp delta squared integral value */ + uint64_t presentationTimestampDeltaIntegralSq; + + /* Frame presentation timing error integral value; the timing error + * is the absolute difference between acquisition timestamp delta + * and presentation timestamp delta for two consecutive presentation + * frames */ + uint64_t presentationTimingErrorIntegral; + + /* Frame presentation timing error squared integral value */ + uint64_t presentationTimingErrorIntegralSq; + + /* Frame estimated latency integral value; the estimated latency is the + * difference between a frame presentation timestamp and acquisition + * timestamp using an estimation of the clock difference between the + * sender and the receiver */ + uint64_t presentationEstimatedLatencyIntegral; + + /* Frame estimated latency squared integral value */ + uint64_t presentationEstimatedLatencyIntegralSq; + + /* Player-side frame latency integral value; the player latency is the + * difference between a frame presentation timestamp and the output + * timestamp of the frame from the reeciver */ + uint64_t playerLatencyIntegral; + + /* Player-side frame latency squared integral value */ + uint64_t playerLatencyIntegralSq; + + /* Estimated latency precision integral value; this is the precision of + * the estimation of the clock difference between the sender and the + * receiver */ + uint64_t estimatedLatencyPrecisionIntegral; +}; + +} /* namespace Pdraw */ + +#endif /* !_PDRAW_VIDEO_PRES_STATS_HPP_ */ diff --git a/libpdraw/src/pdraw_wrapper.cpp b/libpdraw/src/pdraw_wrapper.cpp index 921c011..5e43bf1 100644 --- a/libpdraw/src/pdraw_wrapper.cpp +++ b/libpdraw/src/pdraw_wrapper.cpp @@ -1,1786 +1,1786 @@ -/** - * Parrot Drones Awesome Video Viewer Library - * C wrapper functions - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ULOG_TAG pdraw_wrapper -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -#include "pdraw_session.hpp" -#include "pdraw_utils.hpp" - -#include -#include - -#include - -#include - - -/* codecheck_ignore[COMPLEX_MACRO] */ -#define ENUM_CASE(_prefix, _name) \ - case _prefix##_name: \ - return #_name - - -class PdrawListener : public Pdraw::IPdraw::Listener { -public: - PdrawListener(struct pdraw *pdraw, - const struct pdraw_cbs *cbs, - void *userdata) : - mPdraw(pdraw), - mCbs(*cbs), mUserdata(userdata) - { - } - - ~PdrawListener() {} - - void stopResponse(Pdraw::IPdraw *pdraw, int status) - { - if (mCbs.stop_resp) { - (*mCbs.stop_resp)(mPdraw, status, mUserdata); - } - } - - void onMediaAdded(Pdraw::IPdraw *pdraw, - const struct pdraw_media_info *info) - { - if (mCbs.media_added) { - (*mCbs.media_added)(mPdraw, info, mUserdata); - } - } - - void onMediaRemoved(Pdraw::IPdraw *pdraw, - const struct pdraw_media_info *info) - { - if (mCbs.media_removed) { - (*mCbs.media_removed)(mPdraw, info, mUserdata); - } - } - - void onSocketCreated(Pdraw::IPdraw *pdraw, int fd) - { - if (mCbs.socket_created) - (*mCbs.socket_created)(mPdraw, fd, mUserdata); - } - -private: - struct pdraw *mPdraw; - struct pdraw_cbs mCbs; - void *mUserdata; -}; - - -class PdrawDemuxerListener : public Pdraw::IPdraw::IDemuxer::Listener { -public: - PdrawDemuxerListener(struct pdraw *pdraw, - const struct pdraw_demuxer_cbs *cbs, - void *userdata) : - mPdraw(pdraw), - mCbs(*cbs), mUserdata(userdata), mDemuxer(nullptr) - { - } - - ~PdrawDemuxerListener() {} - - void demuxerOpenResponse(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IDemuxer *demuxer, - int status) - { - if (mCbs.open_resp) { - (*mCbs.open_resp)( - mPdraw, - reinterpret_cast( - demuxer), - status, - mUserdata); - } - } - - - void demuxerCloseResponse(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IDemuxer *demuxer, - int status) - { - if (mCbs.close_resp) { - (*mCbs.close_resp)( - mPdraw, - reinterpret_cast( - demuxer), - status, - mUserdata); - } - } - - void onDemuxerUnrecoverableError(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IDemuxer *demuxer) - { - if (mCbs.unrecoverable_error) { - (*mCbs.unrecoverable_error)( - mPdraw, - reinterpret_cast( - demuxer), - mUserdata); - } - } - - int demuxerSelectMedia(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IDemuxer *demuxer, - const struct pdraw_demuxer_media *medias, - size_t count) - { - if (mCbs.select_media) { - return (*mCbs.select_media)( - mPdraw, - reinterpret_cast( - demuxer), - medias, - count, - mUserdata); - } - return -ENOSYS; - } - - void demuxerReadyToPlay(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IDemuxer *demuxer, - bool ready) - { - if (mCbs.ready_to_play) { - (*mCbs.ready_to_play)( - mPdraw, - reinterpret_cast( - demuxer), - ready ? 1 : 0, - mUserdata); - } - } - - void onDemuxerEndOfRange(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IDemuxer *demuxer, - uint64_t timestamp) - { - if (mCbs.end_of_range) { - (*mCbs.end_of_range)( - mPdraw, - reinterpret_cast( - demuxer), - timestamp, - mUserdata); - } - } - - void demuxerPlayResponse(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp, - float speed) - { - if (mCbs.play_resp) { - (*mCbs.play_resp)( - mPdraw, - reinterpret_cast( - demuxer), - status, - timestamp, - speed, - mUserdata); - } - } - - void demuxerPauseResponse(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp) - { - if (mCbs.pause_resp) { - (*mCbs.pause_resp)( - mPdraw, - reinterpret_cast( - demuxer), - status, - timestamp, - mUserdata); - } - } - - void demuxerSeekResponse(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp, - float speed) - { - if (mCbs.seek_resp) { - (*mCbs.seek_resp)( - mPdraw, - reinterpret_cast( - demuxer), - status, - timestamp, - speed, - mUserdata); - } - } - - Pdraw::IPdraw::IDemuxer *getDemuxer() - { - return mDemuxer; - } - - void setDemuxer(Pdraw::IPdraw::IDemuxer *demuxer) - { - mDemuxer = demuxer; - } - -private: - struct pdraw *mPdraw; - struct pdraw_demuxer_cbs mCbs; - void *mUserdata; - Pdraw::IPdraw::IDemuxer *mDemuxer; -}; - - -class PdrawVideoRendererListener - : public Pdraw::IPdraw::IVideoRenderer::Listener { -public: - PdrawVideoRendererListener(struct pdraw *pdraw, - const struct pdraw_video_renderer_cbs *cbs, - void *userdata) : - mPdraw(pdraw), - mCbs(*cbs), mUserdata(userdata), mRenderer(nullptr) - { - } - - ~PdrawVideoRendererListener() {} - - void onVideoRendererMediaAdded(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IVideoRenderer *renderer, - const struct pdraw_media_info *info) - { - if (mCbs.media_added) - (*mCbs.media_added)( - mPdraw, - reinterpret_cast( - renderer), - info, - mUserdata); - } - - void - onVideoRendererMediaRemoved(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IVideoRenderer *renderer, - const struct pdraw_media_info *info) - { - if (mCbs.media_removed) - (*mCbs.media_removed)( - mPdraw, - reinterpret_cast( - renderer), - info, - mUserdata); - } - - void onVideoRenderReady(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IVideoRenderer *renderer) - { - if (mCbs.render_ready) - (*mCbs.render_ready)( - mPdraw, - reinterpret_cast( - renderer), - mUserdata); - } - - int loadVideoTexture(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IVideoRenderer *renderer, - unsigned int textureWidth, - unsigned int textureHeight, - const struct pdraw_media_info *mediaInfo, - struct mbuf_raw_video_frame *frame, - const void *frameUserdata, - size_t frameUserdataLen) - { - if (mCbs.load_texture == nullptr) - return -ENOSYS; - if ((renderer == nullptr) || (mediaInfo == nullptr) || - (frame == nullptr)) - return -EINVAL; - return (*mCbs.load_texture)( - mPdraw, - reinterpret_cast( - renderer), - textureWidth, - textureHeight, - mediaInfo, - frame, - frameUserdata, - frameUserdataLen, - mUserdata); - } - - int renderVideoOverlay(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IVideoRenderer *renderer, - const struct pdraw_rect *renderPos, - const struct pdraw_rect *contentPos, - const float *viewMat, - const float *projMat, - const struct pdraw_media_info *mediaInfo, - struct vmeta_frame *frameMeta, - const struct pdraw_video_frame_extra *frameExtra) - { - if (mCbs.render_overlay == nullptr) - return -ENOSYS; - if ((renderer == nullptr) || (renderPos == nullptr) || - (contentPos == nullptr) || (viewMat == nullptr) || - (projMat == nullptr) || (mediaInfo == nullptr)) - return -EINVAL; - (*mCbs.render_overlay)( - mPdraw, - reinterpret_cast( - renderer), - renderPos, - contentPos, - viewMat, - projMat, - mediaInfo, - frameMeta, - frameExtra, - mUserdata); - return 0; - } - - Pdraw::IPdraw::IVideoRenderer *getVideoRenderer() - { - return mRenderer; - } - - void setVideoRenderer(Pdraw::IPdraw::IVideoRenderer *renderer) - { - mRenderer = renderer; - } - -private: - struct pdraw *mPdraw; - struct pdraw_video_renderer_cbs mCbs; - void *mUserdata; - Pdraw::IPdraw::IVideoRenderer *mRenderer; -}; - - -class PdrawCodedVideoSinkListener - : public Pdraw::IPdraw::ICodedVideoSink::Listener { -public: - PdrawCodedVideoSinkListener( - struct pdraw *pdraw, - const struct pdraw_coded_video_sink_cbs *cbs, - void *userdata) : - mPdraw(pdraw), - mCbs(*cbs), mUserdata(userdata), mSink(nullptr) - { - } - - ~PdrawCodedVideoSinkListener() {} - - void onCodedVideoSinkFlush(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::ICodedVideoSink *sink) - { - if (mCbs.flush) - (*mCbs.flush)( - mPdraw, - reinterpret_cast< - struct pdraw_coded_video_sink *>(sink), - mUserdata); - } - - Pdraw::IPdraw::ICodedVideoSink *getCodedVideoSink() - { - return mSink; - } - - void setCodedVideoSink(Pdraw::IPdraw::ICodedVideoSink *sink) - { - mSink = sink; - } - -private: - struct pdraw *mPdraw; - struct pdraw_coded_video_sink_cbs mCbs; - void *mUserdata; - Pdraw::IPdraw::ICodedVideoSink *mSink; -}; - - -class PdrawRawVideoSinkListener - : public Pdraw::IPdraw::IRawVideoSink::Listener { -public: - PdrawRawVideoSinkListener(struct pdraw *pdraw, - const struct pdraw_raw_video_sink_cbs *cbs, - void *userdata) : - mPdraw(pdraw), - mCbs(*cbs), mUserdata(userdata), mSink(nullptr) - { - } - - ~PdrawRawVideoSinkListener() {} - - void onRawVideoSinkFlush(Pdraw::IPdraw *pdraw, - Pdraw::IPdraw::IRawVideoSink *sink) - { - if (mCbs.flush) - (*mCbs.flush)( - mPdraw, - reinterpret_cast( - sink), - mUserdata); - } - - Pdraw::IPdraw::IRawVideoSink *getRawVideoSink() - { - return mSink; - } - - void setRawVideoSink(Pdraw::IPdraw::IRawVideoSink *sink) - { - mSink = sink; - } - -private: - struct pdraw *mPdraw; - struct pdraw_raw_video_sink_cbs mCbs; - void *mUserdata; - Pdraw::IPdraw::IRawVideoSink *mSink; -}; - - -struct pdraw { - Pdraw::IPdraw *pdraw; - PdrawListener *listener; - pthread_mutex_t mutex; - std::vector *demuxerListeners; - std::vector *videoRendererListeners; - std::vector *codedVideoSinkListeners; - std::vector *rawVideoSinkListeners; -}; - - -int pdraw_new(struct pomp_loop *loop, - const struct pdraw_cbs *cbs, - void *userdata, - struct pdraw **ret_obj) -{ - int ret = 0; - struct pdraw *pdraw; - - if (loop == nullptr) - return -EINVAL; - if (cbs == nullptr) - return -EINVAL; - if (ret_obj == nullptr) - return -EINVAL; - - pdraw = (struct pdraw *)calloc(1, sizeof(*pdraw)); - if (pdraw == nullptr) - return -ENOMEM; - - ret = pthread_mutex_init(&pdraw->mutex, nullptr); - if (ret != 0) { - free(pdraw); - return -ret; - } - - pdraw->demuxerListeners = new std::vector(); - if (pdraw->demuxerListeners == nullptr) { - ret = -ENOMEM; - goto error; - } - - pdraw->videoRendererListeners = - new std::vector(); - if (pdraw->videoRendererListeners == nullptr) { - ret = -ENOMEM; - goto error; - } - - pdraw->codedVideoSinkListeners = - new std::vector(); - if (pdraw->codedVideoSinkListeners == nullptr) { - ret = -ENOMEM; - goto error; - } - - pdraw->rawVideoSinkListeners = - new std::vector(); - if (pdraw->rawVideoSinkListeners == nullptr) { - ret = -ENOMEM; - goto error; - } - - pdraw->listener = new PdrawListener(pdraw, cbs, userdata); - if (pdraw->listener == nullptr) { - ret = -ENOMEM; - goto error; - } - - pdraw->pdraw = new Pdraw::Session(loop, pdraw->listener); - if (pdraw->pdraw == nullptr) { - ret = -ENOMEM; - goto error; - } - - *ret_obj = pdraw; - return 0; - -error: - pdraw_destroy(pdraw); - return ret; -} - - -int pdraw_destroy(struct pdraw *pdraw) -{ - if (pdraw == nullptr) - return -EINVAL; - if (pdraw->pdraw != nullptr) - delete pdraw->pdraw; - if (pdraw->listener != nullptr) - delete pdraw->listener; - - - if (pdraw->demuxerListeners != nullptr) { - std::vector::iterator l = - pdraw->demuxerListeners->begin(); - while (l != pdraw->demuxerListeners->end()) { - if (*l != nullptr) - delete (*l); - l++; - } - pdraw->demuxerListeners->clear(); - delete pdraw->demuxerListeners; - } - - if (pdraw->codedVideoSinkListeners != nullptr) { - std::vector::iterator l = - pdraw->codedVideoSinkListeners->begin(); - while (l != pdraw->codedVideoSinkListeners->end()) { - if (*l != nullptr) - delete (*l); - l++; - } - pdraw->codedVideoSinkListeners->clear(); - delete pdraw->codedVideoSinkListeners; - } - - if (pdraw->rawVideoSinkListeners != nullptr) { - std::vector::iterator l = - pdraw->rawVideoSinkListeners->begin(); - while (l != pdraw->rawVideoSinkListeners->end()) { - if (*l != nullptr) - delete (*l); - l++; - } - pdraw->rawVideoSinkListeners->clear(); - delete pdraw->rawVideoSinkListeners; - } - - pthread_mutex_lock(&pdraw->mutex); - if (pdraw->videoRendererListeners != nullptr) { - std::vector::iterator r = - pdraw->videoRendererListeners->begin(); - while (r != pdraw->videoRendererListeners->end()) { - if (*r != nullptr) - delete (*r); - r++; - } - pdraw->videoRendererListeners->clear(); - delete pdraw->videoRendererListeners; - } - pthread_mutex_unlock(&pdraw->mutex); - pthread_mutex_destroy(&pdraw->mutex); - - free(pdraw); - return 0; -} - - -int pdraw_stop(struct pdraw *pdraw) -{ - if (pdraw == nullptr) - return -EINVAL; - - return pdraw->pdraw->stop(); -} - - -int pdraw_demuxer_new_from_url(struct pdraw *pdraw, - const char *url, - const struct pdraw_demuxer_cbs *cbs, - void *userdata, - struct pdraw_demuxer **ret_obj) -{ - return pdraw_demuxer_new_from_url_on_mux( - pdraw, url, nullptr, cbs, userdata, ret_obj); -} - - -int pdraw_demuxer_new_single_stream(struct pdraw *pdraw, - const char *local_addr, - uint16_t local_stream_port, - uint16_t local_control_port, - const char *remote_addr, - uint16_t remote_stream_port, - uint16_t remote_control_port, - const struct pdraw_demuxer_cbs *cbs, - void *userdata, - struct pdraw_demuxer **ret_obj) -{ - int res; - Pdraw::IPdraw::IDemuxer *demuxer = nullptr; - - if (pdraw == nullptr) - return -EINVAL; - if (local_addr == nullptr) - return -EINVAL; - if (remote_addr == nullptr) - return -EINVAL; - if (cbs == nullptr) - return -EINVAL; - if (ret_obj == nullptr) - return -EINVAL; - - PdrawDemuxerListener *demuxerListener = - new PdrawDemuxerListener(pdraw, cbs, userdata); - if (demuxerListener == nullptr) { - ULOGE("failed to create demuxer listener"); - return -ENOMEM; - } - - std::string local(local_addr); - std::string remote(remote_addr); - res = pdraw->pdraw->createDemuxer(local, - local_stream_port, - local_control_port, - remote, - remote_stream_port, - remote_control_port, - demuxerListener, - &demuxer); - if (res < 0) { - delete demuxerListener; - return res; - } - - demuxerListener->setDemuxer(demuxer); - pdraw->demuxerListeners->push_back(demuxerListener); - - *ret_obj = reinterpret_cast(demuxer); - return 0; -} - - -int pdraw_demuxer_new_from_url_on_mux(struct pdraw *pdraw, - const char *url, - struct mux_ctx *mux, - const struct pdraw_demuxer_cbs *cbs, - void *userdata, - struct pdraw_demuxer **ret_obj) -{ - int res; - Pdraw::IPdraw::IDemuxer *demuxer = nullptr; - - if (pdraw == nullptr) - return -EINVAL; - if (url == nullptr) - return -EINVAL; - /* Note: deliberately not testing the mux pointer, as - * pdraw_demuxer_new_from_url() calls this function with - * a NULL mux pointer */ - if (cbs == nullptr) - return -EINVAL; - if (ret_obj == nullptr) - return -EINVAL; - - PdrawDemuxerListener *demuxerListener = - new PdrawDemuxerListener(pdraw, cbs, userdata); - if (demuxerListener == nullptr) { - ULOGE("failed to create demuxer listener"); - return -ENOMEM; - } - - std::string u(url); - res = pdraw->pdraw->createDemuxer(u, mux, demuxerListener, &demuxer); - if (res < 0) { - delete demuxerListener; - return res; - } - - demuxerListener->setDemuxer(demuxer); - pdraw->demuxerListeners->push_back(demuxerListener); - - *ret_obj = reinterpret_cast(demuxer); - return 0; -} - - -int pdraw_demuxer_destroy(struct pdraw *pdraw, struct pdraw_demuxer *demuxer) -{ - std::vector::iterator l; - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - if (pdraw == nullptr) - return -EINVAL; - if (demuxer == nullptr) - return -EINVAL; - - l = pdraw->demuxerListeners->begin(); - while (l != pdraw->demuxerListeners->end()) { - if ((*l)->getDemuxer() != d) { - l++; - continue; - } - delete *l; - pdraw->demuxerListeners->erase(l); - break; - } - - delete d; - - return 0; -} - - -int pdraw_demuxer_close(struct pdraw *pdraw, struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - if (pdraw == nullptr) - return -EINVAL; - if (demuxer == nullptr) - return -EINVAL; - - return d->close(); -} - - -uint16_t -pdraw_demuxer_get_single_stream_local_stream_port(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - if (pdraw == nullptr) - return 0; - if (demuxer == nullptr) - return 0; - - return d->getSingleStreamLocalStreamPort(); -} - - -uint16_t pdraw_demuxer_get_single_stream_local_control_port( - struct pdraw *pdraw, - struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - if (pdraw == nullptr) - return 0; - if (demuxer == nullptr) - return 0; - - return d->getSingleStreamLocalControlPort(); -} - - -int pdraw_demuxer_is_ready_to_play(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - if (pdraw == nullptr) - return -EINVAL; - if (demuxer == nullptr) - return -EINVAL; - - return (d->isReadyToPlay()) ? 1 : 0; -} - - -int pdraw_demuxer_is_paused(struct pdraw *pdraw, struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - if (pdraw == nullptr) - return -EINVAL; - if (demuxer == nullptr) - return -EINVAL; - - return (d->isPaused()) ? 1 : 0; -} - - -int pdraw_demuxer_play(struct pdraw *pdraw, struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - if (pdraw == nullptr) - return -EINVAL; - if (demuxer == nullptr) - return -EINVAL; - - return d->play(); -} - - -int pdraw_demuxer_play_with_speed(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - float speed) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - if (pdraw == nullptr) - return -EINVAL; - if (demuxer == nullptr) - return -EINVAL; - - return d->play(speed); -} - - -int pdraw_demuxer_pause(struct pdraw *pdraw, struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - if (pdraw == nullptr) - return -EINVAL; - if (demuxer == nullptr) - return -EINVAL; - - return d->pause(); -} - - -int pdraw_demuxer_previous_frame(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - if (pdraw == nullptr) - return -EINVAL; - if (demuxer == nullptr) - return -EINVAL; - - return d->previousFrame(); -} - - -int pdraw_demuxer_next_frame(struct pdraw *pdraw, struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - if (pdraw == nullptr) - return -EINVAL; - if (demuxer == nullptr) - return -EINVAL; - - return d->nextFrame(); -} - - -int pdraw_demuxer_seek(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - int64_t delta, - int exact) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - if (pdraw == nullptr) - return -EINVAL; - if (demuxer == nullptr) - return -EINVAL; - - return d->seek(delta, exact ? true : false); -} - - -int pdraw_demuxer_seek_forward(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - uint64_t delta, - int exact) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - if (pdraw == nullptr) - return -EINVAL; - if (demuxer == nullptr) - return -EINVAL; - - return d->seekForward(delta, exact ? true : false); -} - - -int pdraw_demuxer_seek_back(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - uint64_t delta, - int exact) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - if (pdraw == nullptr) - return -EINVAL; - if (demuxer == nullptr) - return -EINVAL; - - return d->seekBack(delta, exact ? true : false); -} - - -int pdraw_demuxer_seek_to(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer, - uint64_t timestamp, - int exact) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - if (pdraw == nullptr) - return -EINVAL; - if (demuxer == nullptr) - return -EINVAL; - - return d->seekTo(timestamp, exact ? true : false); -} - - -uint64_t pdraw_demuxer_get_duration(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - if (pdraw == nullptr) - return 0; - if (demuxer == nullptr) - return 0; - - return d->getDuration(); -} - - -uint64_t pdraw_demuxer_get_current_time(struct pdraw *pdraw, - struct pdraw_demuxer *demuxer) -{ - Pdraw::IPdraw::IDemuxer *d = - reinterpret_cast(demuxer); - - if (pdraw == nullptr) - return 0; - if (demuxer == nullptr) - return 0; - - return d->getCurrentTime(); -} - - -int pdraw_muxer_new(struct pdraw *pdraw, - const char *url, - struct pdraw_muxer **ret_obj) -{ - if (pdraw == nullptr) - return -EINVAL; - if (url == nullptr) - return -EINVAL; - if (ret_obj == nullptr) - return -EINVAL; - -#if MUXER_TEST - int res; - Pdraw::IPdraw::IMuxer *muxer = nullptr; - - std::string u(url); - res = pdraw->pdraw->createMuxer(u, &muxer); - if (res < 0) - return res; - - *ret_obj = reinterpret_cast(muxer); - return 0; -#else - /* Not supported yet */ - return -ENOSYS; -#endif -} - - -int pdraw_muxer_destroy(struct pdraw *pdraw, struct pdraw_muxer *muxer) -{ - Pdraw::IPdraw::IMuxer *m = - reinterpret_cast(muxer); - - if (pdraw == nullptr) - return -EINVAL; - if (muxer == nullptr) - return -EINVAL; - - delete m; - - return 0; -} - - -int pdraw_muxer_add_media(struct pdraw *pdraw, - struct pdraw_muxer *muxer, - unsigned int media_id, - const struct pdraw_muxer_video_media_params *params) -{ - Pdraw::IPdraw::IMuxer *m = - reinterpret_cast(muxer); - - if (pdraw == nullptr) - return -EINVAL; - if (muxer == nullptr) - return -EINVAL; - - return m->addMedia(media_id, params); -} - - -int pdraw_video_renderer_new(struct pdraw *pdraw, - unsigned int media_id, - const struct pdraw_rect *render_pos, - const struct pdraw_video_renderer_params *params, - const struct pdraw_video_renderer_cbs *cbs, - void *userdata, - struct pdraw_video_renderer **ret_obj) -{ - return pdraw_video_renderer_new_egl(pdraw, - media_id, - render_pos, - params, - cbs, - userdata, - nullptr, - ret_obj); -} - - -int pdraw_video_renderer_new_egl( - struct pdraw *pdraw, - unsigned int media_id, - const struct pdraw_rect *render_pos, - const struct pdraw_video_renderer_params *params, - const struct pdraw_video_renderer_cbs *cbs, - void *userdata, - struct egl_display *egl_display, - struct pdraw_video_renderer **ret_obj) -{ - int ret = 0; - Pdraw::IPdraw::IVideoRenderer *renderer = nullptr; - if (pdraw == nullptr) - return -EINVAL; - if (ret_obj == nullptr) - return -EINVAL; - - pthread_mutex_lock(&pdraw->mutex); - PdrawVideoRendererListener *l = - new PdrawVideoRendererListener(pdraw, cbs, userdata); - if (l == nullptr) { - ULOGE("failed to create video renderer listener"); - ret = -ENOMEM; - goto error; - } - - ret = pdraw->pdraw->createVideoRenderer( - media_id, render_pos, params, l, &renderer, egl_display); - if (ret < 0) { - delete l; - goto error; - } - - pdraw->videoRendererListeners->push_back(l); - l->setVideoRenderer(renderer); - pthread_mutex_unlock(&pdraw->mutex); - - *ret_obj = reinterpret_cast(renderer); - return 0; - -error: - pthread_mutex_unlock(&pdraw->mutex); - return ret; -} - - -int pdraw_video_renderer_destroy(struct pdraw *pdraw, - struct pdraw_video_renderer *renderer) -{ - std::vector::iterator l; - Pdraw::IPdraw::IVideoRenderer *rnd = - reinterpret_cast(renderer); - - if (pdraw == nullptr) - return -EINVAL; - if (renderer == nullptr) - return -EINVAL; - - pthread_mutex_lock(&pdraw->mutex); - l = pdraw->videoRendererListeners->begin(); - while (l != pdraw->videoRendererListeners->end()) { - if ((*l)->getVideoRenderer() != rnd) { - l++; - continue; - } - - delete *l; - pdraw->videoRendererListeners->erase(l); - break; - } - pthread_mutex_unlock(&pdraw->mutex); - - delete rnd; - - return 0; -} - - -int pdraw_video_renderer_resize(struct pdraw *pdraw, - struct pdraw_video_renderer *renderer, - const struct pdraw_rect *render_pos) -{ - Pdraw::IPdraw::IVideoRenderer *rnd = - reinterpret_cast(renderer); - - if (pdraw == nullptr) - return -EINVAL; - if (renderer == nullptr) - return -EINVAL; - - return rnd->resize(render_pos); -} - - -int pdraw_video_renderer_set_media_id(struct pdraw *pdraw, - struct pdraw_video_renderer *renderer, - unsigned int media_id) -{ - Pdraw::IPdraw::IVideoRenderer *rnd = - reinterpret_cast(renderer); - - if (pdraw == nullptr) - return -EINVAL; - if (renderer == nullptr) - return -EINVAL; - - return rnd->setMediaId(media_id); -} - - -unsigned int -pdraw_video_renderer_get_media_id(struct pdraw *pdraw, - struct pdraw_video_renderer *renderer) -{ - Pdraw::IPdraw::IVideoRenderer *rnd = - reinterpret_cast(renderer); - - if (pdraw == nullptr) - return 0; - if (renderer == nullptr) - return 0; - - return rnd->getMediaId(); -} - - -int pdraw_video_renderer_set_params( - struct pdraw *pdraw, - struct pdraw_video_renderer *renderer, - const struct pdraw_video_renderer_params *params) -{ - Pdraw::IPdraw::IVideoRenderer *rnd = - reinterpret_cast(renderer); - - if (pdraw == nullptr) - return -EINVAL; - if (renderer == nullptr) - return -EINVAL; - - return rnd->setParams(params); -} - - -int pdraw_video_renderer_get_params(struct pdraw *pdraw, - struct pdraw_video_renderer *renderer, - struct pdraw_video_renderer_params *params) -{ - Pdraw::IPdraw::IVideoRenderer *rnd = - reinterpret_cast(renderer); - - if (pdraw == nullptr) - return -EINVAL; - if (renderer == nullptr) - return -EINVAL; - - return rnd->getParams(params); -} - - -int pdraw_video_renderer_render(struct pdraw *pdraw, - struct pdraw_video_renderer *renderer, - struct pdraw_rect *content_pos) -{ - Pdraw::IPdraw::IVideoRenderer *rnd = - reinterpret_cast(renderer); - - if (pdraw == nullptr) - return -EINVAL; - if (renderer == nullptr) - return -EINVAL; - - return rnd->render(content_pos, nullptr, nullptr); -} - - -int pdraw_video_renderer_render_mat(struct pdraw *pdraw, - struct pdraw_video_renderer *renderer, - struct pdraw_rect *content_pos, - const float *view_mat, - const float *proj_mat) -{ - Pdraw::IPdraw::IVideoRenderer *rnd = - reinterpret_cast(renderer); - - if (pdraw == nullptr) - return -EINVAL; - if (renderer == nullptr) - return -EINVAL; - - return rnd->render(content_pos, view_mat, proj_mat); -} - - -int pdraw_coded_video_sink_new(struct pdraw *pdraw, - unsigned int media_id, - const struct pdraw_video_sink_params *params, - const struct pdraw_coded_video_sink_cbs *cbs, - void *userdata, - struct pdraw_coded_video_sink **ret_obj) -{ - int res; - Pdraw::IPdraw::ICodedVideoSink *sink = nullptr; - - if (pdraw == nullptr) - return -EINVAL; - if (params == nullptr) - return -EINVAL; - if ((cbs == nullptr) || (cbs->flush == nullptr)) - return -EINVAL; - if (ret_obj == nullptr) - return -EINVAL; - - PdrawCodedVideoSinkListener *videoSinkListener = - new PdrawCodedVideoSinkListener(pdraw, cbs, userdata); - if (videoSinkListener == nullptr) { - ULOGE("failed to create coded video sink listener"); - return -ENOMEM; - } - - res = pdraw->pdraw->createCodedVideoSink( - media_id, params, videoSinkListener, &sink); - if (res < 0) { - delete videoSinkListener; - return res; - } - - videoSinkListener->setCodedVideoSink(sink); - pdraw->codedVideoSinkListeners->push_back(videoSinkListener); - - *ret_obj = reinterpret_cast(sink); - return 0; -} - - -int pdraw_coded_video_sink_destroy(struct pdraw *pdraw, - struct pdraw_coded_video_sink *sink) -{ - std::vector::iterator l; - Pdraw::IPdraw::ICodedVideoSink *s = - reinterpret_cast(sink); - - if (pdraw == nullptr) - return -EINVAL; - if (sink == nullptr) - return -EINVAL; - - l = pdraw->codedVideoSinkListeners->begin(); - while (l != pdraw->codedVideoSinkListeners->end()) { - if ((*l)->getCodedVideoSink() != s) { - l++; - continue; - } - delete *l; - pdraw->codedVideoSinkListeners->erase(l); - break; - } - - delete s; - - return 0; -} - - -int pdraw_coded_video_sink_resync(struct pdraw *pdraw, - struct pdraw_coded_video_sink *sink) -{ - Pdraw::IPdraw::ICodedVideoSink *s = - reinterpret_cast(sink); - - if (pdraw == nullptr) - return -EINVAL; - if (sink == nullptr) - return -EINVAL; - - return s->resync(); -} - - -struct mbuf_coded_video_frame_queue * -pdraw_coded_video_sink_get_queue(struct pdraw *pdraw, - struct pdraw_coded_video_sink *sink) -{ - Pdraw::IPdraw::ICodedVideoSink *s = - reinterpret_cast(sink); - - if (pdraw == nullptr) - return nullptr; - if (sink == nullptr) - return nullptr; - - return s->getQueue(); -} - - -int pdraw_coded_video_sink_queue_flushed(struct pdraw *pdraw, - struct pdraw_coded_video_sink *sink) -{ - Pdraw::IPdraw::ICodedVideoSink *s = - reinterpret_cast(sink); - - if (pdraw == nullptr) - return -EINVAL; - if (sink == nullptr) - return -EINVAL; - - return s->queueFlushed(); -} - - -int pdraw_raw_video_sink_new(struct pdraw *pdraw, - unsigned int media_id, - const struct pdraw_video_sink_params *params, - const struct pdraw_raw_video_sink_cbs *cbs, - void *userdata, - struct pdraw_raw_video_sink **ret_obj) -{ - int res; - Pdraw::IPdraw::IRawVideoSink *sink = nullptr; - - if (pdraw == nullptr) - return -EINVAL; - if (params == nullptr) - return -EINVAL; - if ((cbs == nullptr) || (cbs->flush == nullptr)) - return -EINVAL; - if (ret_obj == nullptr) - return -EINVAL; - - PdrawRawVideoSinkListener *videoSinkListener = - new PdrawRawVideoSinkListener(pdraw, cbs, userdata); - if (videoSinkListener == nullptr) { - ULOGE("failed to create raw video sink listener"); - return -ENOMEM; - } - - res = pdraw->pdraw->createRawVideoSink( - media_id, params, videoSinkListener, &sink); - if (res < 0) { - delete videoSinkListener; - return res; - } - - videoSinkListener->setRawVideoSink(sink); - pdraw->rawVideoSinkListeners->push_back(videoSinkListener); - - *ret_obj = reinterpret_cast(sink); - return 0; -} - - -int pdraw_raw_video_sink_destroy(struct pdraw *pdraw, - struct pdraw_raw_video_sink *sink) -{ - std::vector::iterator l; - Pdraw::IPdraw::IRawVideoSink *s = - reinterpret_cast(sink); - - if (pdraw == nullptr) - return -EINVAL; - if (sink == nullptr) - return -EINVAL; - - l = pdraw->rawVideoSinkListeners->begin(); - while (l != pdraw->rawVideoSinkListeners->end()) { - if ((*l)->getRawVideoSink() != s) { - l++; - continue; - } - delete *l; - pdraw->rawVideoSinkListeners->erase(l); - break; - } - - delete s; - - return 0; -} - - -struct mbuf_raw_video_frame_queue * -pdraw_raw_video_sink_get_queue(struct pdraw *pdraw, - struct pdraw_raw_video_sink *sink) -{ - Pdraw::IPdraw::IRawVideoSink *s = - reinterpret_cast(sink); - - if (pdraw == nullptr) - return nullptr; - if (sink == nullptr) - return nullptr; - - return s->getQueue(); -} - - -int pdraw_raw_video_sink_queue_flushed(struct pdraw *pdraw, - struct pdraw_raw_video_sink *sink) -{ - Pdraw::IPdraw::IRawVideoSink *s = - reinterpret_cast(sink); - - if (pdraw == nullptr) - return -EINVAL; - if (sink == nullptr) - return -EINVAL; - - return s->queueFlushed(); -} - - -int pdraw_get_friendly_name_setting(struct pdraw *pdraw, char *str, size_t len) -{ - if (pdraw == nullptr) - return -EINVAL; - - std::string fn; - pdraw->pdraw->getFriendlyNameSetting(&fn); - if ((str) && (fn.length() >= len)) - return -ENOBUFS; - - if (str) - strcpy(str, fn.c_str()); - return 0; -} - - -int pdraw_set_friendly_name_setting(struct pdraw *pdraw, - const char *friendly_name) -{ - if (pdraw == nullptr) - return -EINVAL; - - std::string fn(friendly_name); - pdraw->pdraw->setFriendlyNameSetting(fn); - return 0; -} - - -int pdraw_get_serial_number_setting(struct pdraw *pdraw, char *str, size_t len) -{ - if (pdraw == nullptr) - return -EINVAL; - - std::string sn; - pdraw->pdraw->getSerialNumberSetting(&sn); - if ((str) && (sn.length() >= len)) - return -ENOBUFS; - - if (str) - strcpy(str, sn.c_str()); - return 0; -} - - -int pdraw_set_serial_number_setting(struct pdraw *pdraw, - const char *serial_number) -{ - if (pdraw == nullptr) - return -EINVAL; - - std::string sn(serial_number); - pdraw->pdraw->setSerialNumberSetting(sn); - return 0; -} - - -int pdraw_get_software_version_setting(struct pdraw *pdraw, - char *str, - size_t len) -{ - if (pdraw == nullptr) - return -EINVAL; - - std::string sv; - pdraw->pdraw->getSoftwareVersionSetting(&sv); - if ((str) && (sv.length() >= len)) - return -ENOBUFS; - - if (str) - strcpy(str, sv.c_str()); - return 0; -} - - -int pdraw_set_software_version_setting(struct pdraw *pdraw, - const char *software_version) -{ - if (pdraw == nullptr) - return -EINVAL; - - std::string sv(software_version); - pdraw->pdraw->setSoftwareVersionSetting(sv); - return 0; -} - - -enum pdraw_pipeline_mode pdraw_get_pipeline_mode_setting(struct pdraw *pdraw) -{ - if (pdraw == nullptr) - return (enum pdraw_pipeline_mode)(-EINVAL); - - return pdraw->pdraw->getPipelineModeSetting(); -} - - -int pdraw_set_pipeline_mode_setting(struct pdraw *pdraw, - enum pdraw_pipeline_mode mode) -{ - if (pdraw == nullptr) - return -EINVAL; - - pdraw->pdraw->setPipelineModeSetting(mode); - return 0; -} - - -int pdraw_get_display_screen_settings(struct pdraw *pdraw, - float *xdpi, - float *ydpi, - float *device_margin_top, - float *device_margin_bottom, - float *device_margin_left, - float *device_margin_right) -{ - if (pdraw == nullptr) - return -EINVAL; - - pdraw->pdraw->getDisplayScreenSettings(xdpi, - ydpi, - device_margin_top, - device_margin_bottom, - device_margin_left, - device_margin_right); - return 0; -} - - -int pdraw_set_display_screen_settings(struct pdraw *pdraw, - float xdpi, - float ydpi, - float device_margin_top, - float device_margin_bottom, - float device_margin_left, - float device_margin_right) -{ - if (pdraw == nullptr) - return -EINVAL; - - pdraw->pdraw->setDisplayScreenSettings(xdpi, - ydpi, - device_margin_top, - device_margin_bottom, - device_margin_left, - device_margin_right); - return 0; -} - - -enum pdraw_hmd_model pdraw_get_hmd_model_setting(struct pdraw *pdraw) -{ - if (pdraw == nullptr) - return (enum pdraw_hmd_model)(-EINVAL); - - return pdraw->pdraw->getHmdModelSetting(); -} - - -int pdraw_set_hmd_model_setting(struct pdraw *pdraw, - enum pdraw_hmd_model hmd_model) -{ - if (pdraw == nullptr) - return -EINVAL; - - pdraw->pdraw->setHmdModelSetting(hmd_model); - return 0; -} - - -int pdraw_set_android_jvm(struct pdraw *pdraw, void *jvm) -{ - if (pdraw == nullptr) - return -EINVAL; - - pdraw->pdraw->setAndroidJvm(jvm); - return 0; -} - - -int pdraw_dump_pipeline(struct pdraw *pdraw, const char *file_name) -{ - if ((pdraw == nullptr) || (file_name == nullptr)) - return -EINVAL; - - std::string f(file_name); - return pdraw->pdraw->dumpPipeline(f); -} - - -const char *pdraw_hmd_model_str(enum pdraw_hmd_model val) -{ - return pdraw_hmdModelStr(val); -} - - -const char *pdraw_pipeline_mode_str(enum pdraw_pipeline_mode val) -{ - return pdraw_pipelineModeStr(val); -} - - -const char *pdraw_playback_type_str(enum pdraw_playback_type val) -{ - return pdraw_playbackTypeStr(val); -} - - -const char *pdraw_media_type_str(enum pdraw_media_type val) -{ - return pdraw_mediaTypeStr(val); -} - - -const char *pdraw_video_type_str(enum pdraw_video_type val) -{ - return pdraw_videoTypeStr(val); -} - - -const char *pdraw_histogram_channel_str(enum pdraw_histogram_channel val) -{ - return pdraw_histogramChannelStr(val); -} - - -const char *pdraw_video_renderer_scheduling_mode_str( - enum pdraw_video_renderer_scheduling_mode val) -{ - return pdraw_videoRendererSchedulingModeStr(val); -} - - -const char * -pdraw_video_renderer_fill_mode_str(enum pdraw_video_renderer_fill_mode val) -{ - return pdraw_videoRendererFillModeStr(val); -} - - -const char *pdraw_video_renderer_transition_flag_str( - enum pdraw_video_renderer_transition_flag val) -{ - return pdraw_videoRendererTransitionFlagStr(val); -} - - -int pdraw_video_frame_to_json_str(const struct pdraw_video_frame *frame, - struct vmeta_frame *metadata, - char *str, - unsigned int len) -{ - return pdraw_frameMetadataToJsonStr(frame, metadata, str, len); -} - - -int pdraw_video_frame_to_json(const struct pdraw_video_frame *frame, - struct vmeta_frame *metadata, - struct json_object *jobj) -{ - return pdraw_frameMetadataToJson(frame, metadata, jobj); -} - - -struct pdraw_media_info * -pdraw_media_info_dup(const struct pdraw_media_info *src) -{ - return pdraw_mediaInfoDup(src); -} - - -void pdraw_media_info_free(struct pdraw_media_info *media_info) -{ - return pdraw_mediaInfoFree(media_info); -} +/** + * Parrot Drones Awesome Video Viewer Library + * C wrapper functions + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 ULOG_TAG pdraw_wrapper +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +#include "pdraw_session.hpp" +#include "pdraw_utils.hpp" + +#include +#include + +#include + +#include + + +/* codecheck_ignore[COMPLEX_MACRO] */ +#define ENUM_CASE(_prefix, _name) \ + case _prefix##_name: \ + return #_name + + +class PdrawListener : public Pdraw::IPdraw::Listener { +public: + PdrawListener(struct pdraw *pdraw, + const struct pdraw_cbs *cbs, + void *userdata) : + mPdraw(pdraw), + mCbs(*cbs), mUserdata(userdata) + { + } + + ~PdrawListener() {} + + void stopResponse(Pdraw::IPdraw *pdraw, int status) + { + if (mCbs.stop_resp) { + (*mCbs.stop_resp)(mPdraw, status, mUserdata); + } + } + + void onMediaAdded(Pdraw::IPdraw *pdraw, + const struct pdraw_media_info *info) + { + if (mCbs.media_added) { + (*mCbs.media_added)(mPdraw, info, mUserdata); + } + } + + void onMediaRemoved(Pdraw::IPdraw *pdraw, + const struct pdraw_media_info *info) + { + if (mCbs.media_removed) { + (*mCbs.media_removed)(mPdraw, info, mUserdata); + } + } + + void onSocketCreated(Pdraw::IPdraw *pdraw, int fd) + { + if (mCbs.socket_created) + (*mCbs.socket_created)(mPdraw, fd, mUserdata); + } + +private: + struct pdraw *mPdraw; + struct pdraw_cbs mCbs; + void *mUserdata; +}; + + +class PdrawDemuxerListener : public Pdraw::IPdraw::IDemuxer::Listener { +public: + PdrawDemuxerListener(struct pdraw *pdraw, + const struct pdraw_demuxer_cbs *cbs, + void *userdata) : + mPdraw(pdraw), + mCbs(*cbs), mUserdata(userdata), mDemuxer(nullptr) + { + } + + ~PdrawDemuxerListener() {} + + void demuxerOpenResponse(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IDemuxer *demuxer, + int status) + { + if (mCbs.open_resp) { + (*mCbs.open_resp)( + mPdraw, + reinterpret_cast( + demuxer), + status, + mUserdata); + } + } + + + void demuxerCloseResponse(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IDemuxer *demuxer, + int status) + { + if (mCbs.close_resp) { + (*mCbs.close_resp)( + mPdraw, + reinterpret_cast( + demuxer), + status, + mUserdata); + } + } + + void onDemuxerUnrecoverableError(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IDemuxer *demuxer) + { + if (mCbs.unrecoverable_error) { + (*mCbs.unrecoverable_error)( + mPdraw, + reinterpret_cast( + demuxer), + mUserdata); + } + } + + int demuxerSelectMedia(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IDemuxer *demuxer, + const struct pdraw_demuxer_media *medias, + size_t count) + { + if (mCbs.select_media) { + return (*mCbs.select_media)( + mPdraw, + reinterpret_cast( + demuxer), + medias, + count, + mUserdata); + } + return -ENOSYS; + } + + void demuxerReadyToPlay(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IDemuxer *demuxer, + bool ready) + { + if (mCbs.ready_to_play) { + (*mCbs.ready_to_play)( + mPdraw, + reinterpret_cast( + demuxer), + ready ? 1 : 0, + mUserdata); + } + } + + void onDemuxerEndOfRange(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IDemuxer *demuxer, + uint64_t timestamp) + { + if (mCbs.end_of_range) { + (*mCbs.end_of_range)( + mPdraw, + reinterpret_cast( + demuxer), + timestamp, + mUserdata); + } + } + + void demuxerPlayResponse(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp, + float speed) + { + if (mCbs.play_resp) { + (*mCbs.play_resp)( + mPdraw, + reinterpret_cast( + demuxer), + status, + timestamp, + speed, + mUserdata); + } + } + + void demuxerPauseResponse(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp) + { + if (mCbs.pause_resp) { + (*mCbs.pause_resp)( + mPdraw, + reinterpret_cast( + demuxer), + status, + timestamp, + mUserdata); + } + } + + void demuxerSeekResponse(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp, + float speed) + { + if (mCbs.seek_resp) { + (*mCbs.seek_resp)( + mPdraw, + reinterpret_cast( + demuxer), + status, + timestamp, + speed, + mUserdata); + } + } + + Pdraw::IPdraw::IDemuxer *getDemuxer() + { + return mDemuxer; + } + + void setDemuxer(Pdraw::IPdraw::IDemuxer *demuxer) + { + mDemuxer = demuxer; + } + +private: + struct pdraw *mPdraw; + struct pdraw_demuxer_cbs mCbs; + void *mUserdata; + Pdraw::IPdraw::IDemuxer *mDemuxer; +}; + + +class PdrawVideoRendererListener + : public Pdraw::IPdraw::IVideoRenderer::Listener { +public: + PdrawVideoRendererListener(struct pdraw *pdraw, + const struct pdraw_video_renderer_cbs *cbs, + void *userdata) : + mPdraw(pdraw), + mCbs(*cbs), mUserdata(userdata), mRenderer(nullptr) + { + } + + ~PdrawVideoRendererListener() {} + + void onVideoRendererMediaAdded(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IVideoRenderer *renderer, + const struct pdraw_media_info *info) + { + if (mCbs.media_added) + (*mCbs.media_added)( + mPdraw, + reinterpret_cast( + renderer), + info, + mUserdata); + } + + void + onVideoRendererMediaRemoved(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IVideoRenderer *renderer, + const struct pdraw_media_info *info) + { + if (mCbs.media_removed) + (*mCbs.media_removed)( + mPdraw, + reinterpret_cast( + renderer), + info, + mUserdata); + } + + void onVideoRenderReady(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IVideoRenderer *renderer) + { + if (mCbs.render_ready) + (*mCbs.render_ready)( + mPdraw, + reinterpret_cast( + renderer), + mUserdata); + } + + int loadVideoTexture(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IVideoRenderer *renderer, + unsigned int textureWidth, + unsigned int textureHeight, + const struct pdraw_media_info *mediaInfo, + struct mbuf_raw_video_frame *frame, + const void *frameUserdata, + size_t frameUserdataLen) + { + if (mCbs.load_texture == nullptr) + return -ENOSYS; + if ((renderer == nullptr) || (mediaInfo == nullptr) || + (frame == nullptr)) + return -EINVAL; + return (*mCbs.load_texture)( + mPdraw, + reinterpret_cast( + renderer), + textureWidth, + textureHeight, + mediaInfo, + frame, + frameUserdata, + frameUserdataLen, + mUserdata); + } + + int renderVideoOverlay(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IVideoRenderer *renderer, + const struct pdraw_rect *renderPos, + const struct pdraw_rect *contentPos, + const float *viewMat, + const float *projMat, + const struct pdraw_media_info *mediaInfo, + struct vmeta_frame *frameMeta, + const struct pdraw_video_frame_extra *frameExtra) + { + if (mCbs.render_overlay == nullptr) + return -ENOSYS; + if ((renderer == nullptr) || (renderPos == nullptr) || + (contentPos == nullptr) || (viewMat == nullptr) || + (projMat == nullptr) || (mediaInfo == nullptr)) + return -EINVAL; + (*mCbs.render_overlay)( + mPdraw, + reinterpret_cast( + renderer), + renderPos, + contentPos, + viewMat, + projMat, + mediaInfo, + frameMeta, + frameExtra, + mUserdata); + return 0; + } + + Pdraw::IPdraw::IVideoRenderer *getVideoRenderer() + { + return mRenderer; + } + + void setVideoRenderer(Pdraw::IPdraw::IVideoRenderer *renderer) + { + mRenderer = renderer; + } + +private: + struct pdraw *mPdraw; + struct pdraw_video_renderer_cbs mCbs; + void *mUserdata; + Pdraw::IPdraw::IVideoRenderer *mRenderer; +}; + + +class PdrawCodedVideoSinkListener + : public Pdraw::IPdraw::ICodedVideoSink::Listener { +public: + PdrawCodedVideoSinkListener( + struct pdraw *pdraw, + const struct pdraw_coded_video_sink_cbs *cbs, + void *userdata) : + mPdraw(pdraw), + mCbs(*cbs), mUserdata(userdata), mSink(nullptr) + { + } + + ~PdrawCodedVideoSinkListener() {} + + void onCodedVideoSinkFlush(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::ICodedVideoSink *sink) + { + if (mCbs.flush) + (*mCbs.flush)( + mPdraw, + reinterpret_cast< + struct pdraw_coded_video_sink *>(sink), + mUserdata); + } + + Pdraw::IPdraw::ICodedVideoSink *getCodedVideoSink() + { + return mSink; + } + + void setCodedVideoSink(Pdraw::IPdraw::ICodedVideoSink *sink) + { + mSink = sink; + } + +private: + struct pdraw *mPdraw; + struct pdraw_coded_video_sink_cbs mCbs; + void *mUserdata; + Pdraw::IPdraw::ICodedVideoSink *mSink; +}; + + +class PdrawRawVideoSinkListener + : public Pdraw::IPdraw::IRawVideoSink::Listener { +public: + PdrawRawVideoSinkListener(struct pdraw *pdraw, + const struct pdraw_raw_video_sink_cbs *cbs, + void *userdata) : + mPdraw(pdraw), + mCbs(*cbs), mUserdata(userdata), mSink(nullptr) + { + } + + ~PdrawRawVideoSinkListener() {} + + void onRawVideoSinkFlush(Pdraw::IPdraw *pdraw, + Pdraw::IPdraw::IRawVideoSink *sink) + { + if (mCbs.flush) + (*mCbs.flush)( + mPdraw, + reinterpret_cast( + sink), + mUserdata); + } + + Pdraw::IPdraw::IRawVideoSink *getRawVideoSink() + { + return mSink; + } + + void setRawVideoSink(Pdraw::IPdraw::IRawVideoSink *sink) + { + mSink = sink; + } + +private: + struct pdraw *mPdraw; + struct pdraw_raw_video_sink_cbs mCbs; + void *mUserdata; + Pdraw::IPdraw::IRawVideoSink *mSink; +}; + + +struct pdraw { + Pdraw::IPdraw *pdraw; + PdrawListener *listener; + pthread_mutex_t mutex; + std::vector *demuxerListeners; + std::vector *videoRendererListeners; + std::vector *codedVideoSinkListeners; + std::vector *rawVideoSinkListeners; +}; + + +int pdraw_new(struct pomp_loop *loop, + const struct pdraw_cbs *cbs, + void *userdata, + struct pdraw **ret_obj) +{ + int ret = 0; + struct pdraw *pdraw; + + if (loop == nullptr) + return -EINVAL; + if (cbs == nullptr) + return -EINVAL; + if (ret_obj == nullptr) + return -EINVAL; + + pdraw = (struct pdraw *)calloc(1, sizeof(*pdraw)); + if (pdraw == nullptr) + return -ENOMEM; + + ret = pthread_mutex_init(&pdraw->mutex, nullptr); + if (ret != 0) { + free(pdraw); + return -ret; + } + + pdraw->demuxerListeners = new std::vector(); + if (pdraw->demuxerListeners == nullptr) { + ret = -ENOMEM; + goto error; + } + + pdraw->videoRendererListeners = + new std::vector(); + if (pdraw->videoRendererListeners == nullptr) { + ret = -ENOMEM; + goto error; + } + + pdraw->codedVideoSinkListeners = + new std::vector(); + if (pdraw->codedVideoSinkListeners == nullptr) { + ret = -ENOMEM; + goto error; + } + + pdraw->rawVideoSinkListeners = + new std::vector(); + if (pdraw->rawVideoSinkListeners == nullptr) { + ret = -ENOMEM; + goto error; + } + + pdraw->listener = new PdrawListener(pdraw, cbs, userdata); + if (pdraw->listener == nullptr) { + ret = -ENOMEM; + goto error; + } + + pdraw->pdraw = new Pdraw::Session(loop, pdraw->listener); + if (pdraw->pdraw == nullptr) { + ret = -ENOMEM; + goto error; + } + + *ret_obj = pdraw; + return 0; + +error: + pdraw_destroy(pdraw); + return ret; +} + + +int pdraw_destroy(struct pdraw *pdraw) +{ + if (pdraw == nullptr) + return -EINVAL; + if (pdraw->pdraw != nullptr) + delete pdraw->pdraw; + if (pdraw->listener != nullptr) + delete pdraw->listener; + + + if (pdraw->demuxerListeners != nullptr) { + std::vector::iterator l = + pdraw->demuxerListeners->begin(); + while (l != pdraw->demuxerListeners->end()) { + if (*l != nullptr) + delete (*l); + l++; + } + pdraw->demuxerListeners->clear(); + delete pdraw->demuxerListeners; + } + + if (pdraw->codedVideoSinkListeners != nullptr) { + std::vector::iterator l = + pdraw->codedVideoSinkListeners->begin(); + while (l != pdraw->codedVideoSinkListeners->end()) { + if (*l != nullptr) + delete (*l); + l++; + } + pdraw->codedVideoSinkListeners->clear(); + delete pdraw->codedVideoSinkListeners; + } + + if (pdraw->rawVideoSinkListeners != nullptr) { + std::vector::iterator l = + pdraw->rawVideoSinkListeners->begin(); + while (l != pdraw->rawVideoSinkListeners->end()) { + if (*l != nullptr) + delete (*l); + l++; + } + pdraw->rawVideoSinkListeners->clear(); + delete pdraw->rawVideoSinkListeners; + } + + pthread_mutex_lock(&pdraw->mutex); + if (pdraw->videoRendererListeners != nullptr) { + std::vector::iterator r = + pdraw->videoRendererListeners->begin(); + while (r != pdraw->videoRendererListeners->end()) { + if (*r != nullptr) + delete (*r); + r++; + } + pdraw->videoRendererListeners->clear(); + delete pdraw->videoRendererListeners; + } + pthread_mutex_unlock(&pdraw->mutex); + pthread_mutex_destroy(&pdraw->mutex); + + free(pdraw); + return 0; +} + + +int pdraw_stop(struct pdraw *pdraw) +{ + if (pdraw == nullptr) + return -EINVAL; + + return pdraw->pdraw->stop(); +} + + +int pdraw_demuxer_new_from_url(struct pdraw *pdraw, + const char *url, + const struct pdraw_demuxer_cbs *cbs, + void *userdata, + struct pdraw_demuxer **ret_obj) +{ + return pdraw_demuxer_new_from_url_on_mux( + pdraw, url, nullptr, cbs, userdata, ret_obj); +} + + +int pdraw_demuxer_new_single_stream(struct pdraw *pdraw, + const char *local_addr, + uint16_t local_stream_port, + uint16_t local_control_port, + const char *remote_addr, + uint16_t remote_stream_port, + uint16_t remote_control_port, + const struct pdraw_demuxer_cbs *cbs, + void *userdata, + struct pdraw_demuxer **ret_obj) +{ + int res; + Pdraw::IPdraw::IDemuxer *demuxer = nullptr; + + if (pdraw == nullptr) + return -EINVAL; + if (local_addr == nullptr) + return -EINVAL; + if (remote_addr == nullptr) + return -EINVAL; + if (cbs == nullptr) + return -EINVAL; + if (ret_obj == nullptr) + return -EINVAL; + + PdrawDemuxerListener *demuxerListener = + new PdrawDemuxerListener(pdraw, cbs, userdata); + if (demuxerListener == nullptr) { + ULOGE("failed to create demuxer listener"); + return -ENOMEM; + } + + std::string local(local_addr); + std::string remote(remote_addr); + res = pdraw->pdraw->createDemuxer(local, + local_stream_port, + local_control_port, + remote, + remote_stream_port, + remote_control_port, + demuxerListener, + &demuxer); + if (res < 0) { + delete demuxerListener; + return res; + } + + demuxerListener->setDemuxer(demuxer); + pdraw->demuxerListeners->push_back(demuxerListener); + + *ret_obj = reinterpret_cast(demuxer); + return 0; +} + + +int pdraw_demuxer_new_from_url_on_mux(struct pdraw *pdraw, + const char *url, + struct mux_ctx *mux, + const struct pdraw_demuxer_cbs *cbs, + void *userdata, + struct pdraw_demuxer **ret_obj) +{ + int res; + Pdraw::IPdraw::IDemuxer *demuxer = nullptr; + + if (pdraw == nullptr) + return -EINVAL; + if (url == nullptr) + return -EINVAL; + /* Note: deliberately not testing the mux pointer, as + * pdraw_demuxer_new_from_url() calls this function with + * a NULL mux pointer */ + if (cbs == nullptr) + return -EINVAL; + if (ret_obj == nullptr) + return -EINVAL; + + PdrawDemuxerListener *demuxerListener = + new PdrawDemuxerListener(pdraw, cbs, userdata); + if (demuxerListener == nullptr) { + ULOGE("failed to create demuxer listener"); + return -ENOMEM; + } + + std::string u(url); + res = pdraw->pdraw->createDemuxer(u, mux, demuxerListener, &demuxer); + if (res < 0) { + delete demuxerListener; + return res; + } + + demuxerListener->setDemuxer(demuxer); + pdraw->demuxerListeners->push_back(demuxerListener); + + *ret_obj = reinterpret_cast(demuxer); + return 0; +} + + +int pdraw_demuxer_destroy(struct pdraw *pdraw, struct pdraw_demuxer *demuxer) +{ + std::vector::iterator l; + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + if (pdraw == nullptr) + return -EINVAL; + if (demuxer == nullptr) + return -EINVAL; + + l = pdraw->demuxerListeners->begin(); + while (l != pdraw->demuxerListeners->end()) { + if ((*l)->getDemuxer() != d) { + l++; + continue; + } + delete *l; + pdraw->demuxerListeners->erase(l); + break; + } + + delete d; + + return 0; +} + + +int pdraw_demuxer_close(struct pdraw *pdraw, struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + if (pdraw == nullptr) + return -EINVAL; + if (demuxer == nullptr) + return -EINVAL; + + return d->close(); +} + + +uint16_t +pdraw_demuxer_get_single_stream_local_stream_port(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + if (pdraw == nullptr) + return 0; + if (demuxer == nullptr) + return 0; + + return d->getSingleStreamLocalStreamPort(); +} + + +uint16_t pdraw_demuxer_get_single_stream_local_control_port( + struct pdraw *pdraw, + struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + if (pdraw == nullptr) + return 0; + if (demuxer == nullptr) + return 0; + + return d->getSingleStreamLocalControlPort(); +} + + +int pdraw_demuxer_is_ready_to_play(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + if (pdraw == nullptr) + return -EINVAL; + if (demuxer == nullptr) + return -EINVAL; + + return (d->isReadyToPlay()) ? 1 : 0; +} + + +int pdraw_demuxer_is_paused(struct pdraw *pdraw, struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + if (pdraw == nullptr) + return -EINVAL; + if (demuxer == nullptr) + return -EINVAL; + + return (d->isPaused()) ? 1 : 0; +} + + +int pdraw_demuxer_play(struct pdraw *pdraw, struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + if (pdraw == nullptr) + return -EINVAL; + if (demuxer == nullptr) + return -EINVAL; + + return d->play(); +} + + +int pdraw_demuxer_play_with_speed(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + float speed) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + if (pdraw == nullptr) + return -EINVAL; + if (demuxer == nullptr) + return -EINVAL; + + return d->play(speed); +} + + +int pdraw_demuxer_pause(struct pdraw *pdraw, struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + if (pdraw == nullptr) + return -EINVAL; + if (demuxer == nullptr) + return -EINVAL; + + return d->pause(); +} + + +int pdraw_demuxer_previous_frame(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + if (pdraw == nullptr) + return -EINVAL; + if (demuxer == nullptr) + return -EINVAL; + + return d->previousFrame(); +} + + +int pdraw_demuxer_next_frame(struct pdraw *pdraw, struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + if (pdraw == nullptr) + return -EINVAL; + if (demuxer == nullptr) + return -EINVAL; + + return d->nextFrame(); +} + + +int pdraw_demuxer_seek(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + int64_t delta, + int exact) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + if (pdraw == nullptr) + return -EINVAL; + if (demuxer == nullptr) + return -EINVAL; + + return d->seek(delta, exact ? true : false); +} + + +int pdraw_demuxer_seek_forward(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + uint64_t delta, + int exact) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + if (pdraw == nullptr) + return -EINVAL; + if (demuxer == nullptr) + return -EINVAL; + + return d->seekForward(delta, exact ? true : false); +} + + +int pdraw_demuxer_seek_back(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + uint64_t delta, + int exact) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + if (pdraw == nullptr) + return -EINVAL; + if (demuxer == nullptr) + return -EINVAL; + + return d->seekBack(delta, exact ? true : false); +} + + +int pdraw_demuxer_seek_to(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer, + uint64_t timestamp, + int exact) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + if (pdraw == nullptr) + return -EINVAL; + if (demuxer == nullptr) + return -EINVAL; + + return d->seekTo(timestamp, exact ? true : false); +} + + +uint64_t pdraw_demuxer_get_duration(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + if (pdraw == nullptr) + return 0; + if (demuxer == nullptr) + return 0; + + return d->getDuration(); +} + + +uint64_t pdraw_demuxer_get_current_time(struct pdraw *pdraw, + struct pdraw_demuxer *demuxer) +{ + Pdraw::IPdraw::IDemuxer *d = + reinterpret_cast(demuxer); + + if (pdraw == nullptr) + return 0; + if (demuxer == nullptr) + return 0; + + return d->getCurrentTime(); +} + + +int pdraw_muxer_new(struct pdraw *pdraw, + const char *url, + struct pdraw_muxer **ret_obj) +{ + if (pdraw == nullptr) + return -EINVAL; + if (url == nullptr) + return -EINVAL; + if (ret_obj == nullptr) + return -EINVAL; + +#if MUXER_TEST + int res; + Pdraw::IPdraw::IMuxer *muxer = nullptr; + + std::string u(url); + res = pdraw->pdraw->createMuxer(u, &muxer); + if (res < 0) + return res; + + *ret_obj = reinterpret_cast(muxer); + return 0; +#else + /* Not supported yet */ + return -ENOSYS; +#endif +} + + +int pdraw_muxer_destroy(struct pdraw *pdraw, struct pdraw_muxer *muxer) +{ + Pdraw::IPdraw::IMuxer *m = + reinterpret_cast(muxer); + + if (pdraw == nullptr) + return -EINVAL; + if (muxer == nullptr) + return -EINVAL; + + delete m; + + return 0; +} + + +int pdraw_muxer_add_media(struct pdraw *pdraw, + struct pdraw_muxer *muxer, + unsigned int media_id, + const struct pdraw_muxer_video_media_params *params) +{ + Pdraw::IPdraw::IMuxer *m = + reinterpret_cast(muxer); + + if (pdraw == nullptr) + return -EINVAL; + if (muxer == nullptr) + return -EINVAL; + + return m->addMedia(media_id, params); +} + + +int pdraw_video_renderer_new(struct pdraw *pdraw, + unsigned int media_id, + const struct pdraw_rect *render_pos, + const struct pdraw_video_renderer_params *params, + const struct pdraw_video_renderer_cbs *cbs, + void *userdata, + struct pdraw_video_renderer **ret_obj) +{ + return pdraw_video_renderer_new_egl(pdraw, + media_id, + render_pos, + params, + cbs, + userdata, + nullptr, + ret_obj); +} + + +int pdraw_video_renderer_new_egl( + struct pdraw *pdraw, + unsigned int media_id, + const struct pdraw_rect *render_pos, + const struct pdraw_video_renderer_params *params, + const struct pdraw_video_renderer_cbs *cbs, + void *userdata, + struct egl_display *egl_display, + struct pdraw_video_renderer **ret_obj) +{ + int ret = 0; + Pdraw::IPdraw::IVideoRenderer *renderer = nullptr; + if (pdraw == nullptr) + return -EINVAL; + if (ret_obj == nullptr) + return -EINVAL; + + pthread_mutex_lock(&pdraw->mutex); + PdrawVideoRendererListener *l = + new PdrawVideoRendererListener(pdraw, cbs, userdata); + if (l == nullptr) { + ULOGE("failed to create video renderer listener"); + ret = -ENOMEM; + goto error; + } + + ret = pdraw->pdraw->createVideoRenderer( + media_id, render_pos, params, l, &renderer, egl_display); + if (ret < 0) { + delete l; + goto error; + } + + pdraw->videoRendererListeners->push_back(l); + l->setVideoRenderer(renderer); + pthread_mutex_unlock(&pdraw->mutex); + + *ret_obj = reinterpret_cast(renderer); + return 0; + +error: + pthread_mutex_unlock(&pdraw->mutex); + return ret; +} + + +int pdraw_video_renderer_destroy(struct pdraw *pdraw, + struct pdraw_video_renderer *renderer) +{ + std::vector::iterator l; + Pdraw::IPdraw::IVideoRenderer *rnd = + reinterpret_cast(renderer); + + if (pdraw == nullptr) + return -EINVAL; + if (renderer == nullptr) + return -EINVAL; + + pthread_mutex_lock(&pdraw->mutex); + l = pdraw->videoRendererListeners->begin(); + while (l != pdraw->videoRendererListeners->end()) { + if ((*l)->getVideoRenderer() != rnd) { + l++; + continue; + } + + delete *l; + pdraw->videoRendererListeners->erase(l); + break; + } + pthread_mutex_unlock(&pdraw->mutex); + + delete rnd; + + return 0; +} + + +int pdraw_video_renderer_resize(struct pdraw *pdraw, + struct pdraw_video_renderer *renderer, + const struct pdraw_rect *render_pos) +{ + Pdraw::IPdraw::IVideoRenderer *rnd = + reinterpret_cast(renderer); + + if (pdraw == nullptr) + return -EINVAL; + if (renderer == nullptr) + return -EINVAL; + + return rnd->resize(render_pos); +} + + +int pdraw_video_renderer_set_media_id(struct pdraw *pdraw, + struct pdraw_video_renderer *renderer, + unsigned int media_id) +{ + Pdraw::IPdraw::IVideoRenderer *rnd = + reinterpret_cast(renderer); + + if (pdraw == nullptr) + return -EINVAL; + if (renderer == nullptr) + return -EINVAL; + + return rnd->setMediaId(media_id); +} + + +unsigned int +pdraw_video_renderer_get_media_id(struct pdraw *pdraw, + struct pdraw_video_renderer *renderer) +{ + Pdraw::IPdraw::IVideoRenderer *rnd = + reinterpret_cast(renderer); + + if (pdraw == nullptr) + return 0; + if (renderer == nullptr) + return 0; + + return rnd->getMediaId(); +} + + +int pdraw_video_renderer_set_params( + struct pdraw *pdraw, + struct pdraw_video_renderer *renderer, + const struct pdraw_video_renderer_params *params) +{ + Pdraw::IPdraw::IVideoRenderer *rnd = + reinterpret_cast(renderer); + + if (pdraw == nullptr) + return -EINVAL; + if (renderer == nullptr) + return -EINVAL; + + return rnd->setParams(params); +} + + +int pdraw_video_renderer_get_params(struct pdraw *pdraw, + struct pdraw_video_renderer *renderer, + struct pdraw_video_renderer_params *params) +{ + Pdraw::IPdraw::IVideoRenderer *rnd = + reinterpret_cast(renderer); + + if (pdraw == nullptr) + return -EINVAL; + if (renderer == nullptr) + return -EINVAL; + + return rnd->getParams(params); +} + + +int pdraw_video_renderer_render(struct pdraw *pdraw, + struct pdraw_video_renderer *renderer, + struct pdraw_rect *content_pos) +{ + Pdraw::IPdraw::IVideoRenderer *rnd = + reinterpret_cast(renderer); + + if (pdraw == nullptr) + return -EINVAL; + if (renderer == nullptr) + return -EINVAL; + + return rnd->render(content_pos, nullptr, nullptr); +} + + +int pdraw_video_renderer_render_mat(struct pdraw *pdraw, + struct pdraw_video_renderer *renderer, + struct pdraw_rect *content_pos, + const float *view_mat, + const float *proj_mat) +{ + Pdraw::IPdraw::IVideoRenderer *rnd = + reinterpret_cast(renderer); + + if (pdraw == nullptr) + return -EINVAL; + if (renderer == nullptr) + return -EINVAL; + + return rnd->render(content_pos, view_mat, proj_mat); +} + + +int pdraw_coded_video_sink_new(struct pdraw *pdraw, + unsigned int media_id, + const struct pdraw_video_sink_params *params, + const struct pdraw_coded_video_sink_cbs *cbs, + void *userdata, + struct pdraw_coded_video_sink **ret_obj) +{ + int res; + Pdraw::IPdraw::ICodedVideoSink *sink = nullptr; + + if (pdraw == nullptr) + return -EINVAL; + if (params == nullptr) + return -EINVAL; + if ((cbs == nullptr) || (cbs->flush == nullptr)) + return -EINVAL; + if (ret_obj == nullptr) + return -EINVAL; + + PdrawCodedVideoSinkListener *videoSinkListener = + new PdrawCodedVideoSinkListener(pdraw, cbs, userdata); + if (videoSinkListener == nullptr) { + ULOGE("failed to create coded video sink listener"); + return -ENOMEM; + } + + res = pdraw->pdraw->createCodedVideoSink( + media_id, params, videoSinkListener, &sink); + if (res < 0) { + delete videoSinkListener; + return res; + } + + videoSinkListener->setCodedVideoSink(sink); + pdraw->codedVideoSinkListeners->push_back(videoSinkListener); + + *ret_obj = reinterpret_cast(sink); + return 0; +} + + +int pdraw_coded_video_sink_destroy(struct pdraw *pdraw, + struct pdraw_coded_video_sink *sink) +{ + std::vector::iterator l; + Pdraw::IPdraw::ICodedVideoSink *s = + reinterpret_cast(sink); + + if (pdraw == nullptr) + return -EINVAL; + if (sink == nullptr) + return -EINVAL; + + l = pdraw->codedVideoSinkListeners->begin(); + while (l != pdraw->codedVideoSinkListeners->end()) { + if ((*l)->getCodedVideoSink() != s) { + l++; + continue; + } + delete *l; + pdraw->codedVideoSinkListeners->erase(l); + break; + } + + delete s; + + return 0; +} + + +int pdraw_coded_video_sink_resync(struct pdraw *pdraw, + struct pdraw_coded_video_sink *sink) +{ + Pdraw::IPdraw::ICodedVideoSink *s = + reinterpret_cast(sink); + + if (pdraw == nullptr) + return -EINVAL; + if (sink == nullptr) + return -EINVAL; + + return s->resync(); +} + + +struct mbuf_coded_video_frame_queue * +pdraw_coded_video_sink_get_queue(struct pdraw *pdraw, + struct pdraw_coded_video_sink *sink) +{ + Pdraw::IPdraw::ICodedVideoSink *s = + reinterpret_cast(sink); + + if (pdraw == nullptr) + return nullptr; + if (sink == nullptr) + return nullptr; + + return s->getQueue(); +} + + +int pdraw_coded_video_sink_queue_flushed(struct pdraw *pdraw, + struct pdraw_coded_video_sink *sink) +{ + Pdraw::IPdraw::ICodedVideoSink *s = + reinterpret_cast(sink); + + if (pdraw == nullptr) + return -EINVAL; + if (sink == nullptr) + return -EINVAL; + + return s->queueFlushed(); +} + + +int pdraw_raw_video_sink_new(struct pdraw *pdraw, + unsigned int media_id, + const struct pdraw_video_sink_params *params, + const struct pdraw_raw_video_sink_cbs *cbs, + void *userdata, + struct pdraw_raw_video_sink **ret_obj) +{ + int res; + Pdraw::IPdraw::IRawVideoSink *sink = nullptr; + + if (pdraw == nullptr) + return -EINVAL; + if (params == nullptr) + return -EINVAL; + if ((cbs == nullptr) || (cbs->flush == nullptr)) + return -EINVAL; + if (ret_obj == nullptr) + return -EINVAL; + + PdrawRawVideoSinkListener *videoSinkListener = + new PdrawRawVideoSinkListener(pdraw, cbs, userdata); + if (videoSinkListener == nullptr) { + ULOGE("failed to create raw video sink listener"); + return -ENOMEM; + } + + res = pdraw->pdraw->createRawVideoSink( + media_id, params, videoSinkListener, &sink); + if (res < 0) { + delete videoSinkListener; + return res; + } + + videoSinkListener->setRawVideoSink(sink); + pdraw->rawVideoSinkListeners->push_back(videoSinkListener); + + *ret_obj = reinterpret_cast(sink); + return 0; +} + + +int pdraw_raw_video_sink_destroy(struct pdraw *pdraw, + struct pdraw_raw_video_sink *sink) +{ + std::vector::iterator l; + Pdraw::IPdraw::IRawVideoSink *s = + reinterpret_cast(sink); + + if (pdraw == nullptr) + return -EINVAL; + if (sink == nullptr) + return -EINVAL; + + l = pdraw->rawVideoSinkListeners->begin(); + while (l != pdraw->rawVideoSinkListeners->end()) { + if ((*l)->getRawVideoSink() != s) { + l++; + continue; + } + delete *l; + pdraw->rawVideoSinkListeners->erase(l); + break; + } + + delete s; + + return 0; +} + + +struct mbuf_raw_video_frame_queue * +pdraw_raw_video_sink_get_queue(struct pdraw *pdraw, + struct pdraw_raw_video_sink *sink) +{ + Pdraw::IPdraw::IRawVideoSink *s = + reinterpret_cast(sink); + + if (pdraw == nullptr) + return nullptr; + if (sink == nullptr) + return nullptr; + + return s->getQueue(); +} + + +int pdraw_raw_video_sink_queue_flushed(struct pdraw *pdraw, + struct pdraw_raw_video_sink *sink) +{ + Pdraw::IPdraw::IRawVideoSink *s = + reinterpret_cast(sink); + + if (pdraw == nullptr) + return -EINVAL; + if (sink == nullptr) + return -EINVAL; + + return s->queueFlushed(); +} + + +int pdraw_get_friendly_name_setting(struct pdraw *pdraw, char *str, size_t len) +{ + if (pdraw == nullptr) + return -EINVAL; + + std::string fn; + pdraw->pdraw->getFriendlyNameSetting(&fn); + if ((str) && (fn.length() >= len)) + return -ENOBUFS; + + if (str) + strcpy(str, fn.c_str()); + return 0; +} + + +int pdraw_set_friendly_name_setting(struct pdraw *pdraw, + const char *friendly_name) +{ + if (pdraw == nullptr) + return -EINVAL; + + std::string fn(friendly_name); + pdraw->pdraw->setFriendlyNameSetting(fn); + return 0; +} + + +int pdraw_get_serial_number_setting(struct pdraw *pdraw, char *str, size_t len) +{ + if (pdraw == nullptr) + return -EINVAL; + + std::string sn; + pdraw->pdraw->getSerialNumberSetting(&sn); + if ((str) && (sn.length() >= len)) + return -ENOBUFS; + + if (str) + strcpy(str, sn.c_str()); + return 0; +} + + +int pdraw_set_serial_number_setting(struct pdraw *pdraw, + const char *serial_number) +{ + if (pdraw == nullptr) + return -EINVAL; + + std::string sn(serial_number); + pdraw->pdraw->setSerialNumberSetting(sn); + return 0; +} + + +int pdraw_get_software_version_setting(struct pdraw *pdraw, + char *str, + size_t len) +{ + if (pdraw == nullptr) + return -EINVAL; + + std::string sv; + pdraw->pdraw->getSoftwareVersionSetting(&sv); + if ((str) && (sv.length() >= len)) + return -ENOBUFS; + + if (str) + strcpy(str, sv.c_str()); + return 0; +} + + +int pdraw_set_software_version_setting(struct pdraw *pdraw, + const char *software_version) +{ + if (pdraw == nullptr) + return -EINVAL; + + std::string sv(software_version); + pdraw->pdraw->setSoftwareVersionSetting(sv); + return 0; +} + + +enum pdraw_pipeline_mode pdraw_get_pipeline_mode_setting(struct pdraw *pdraw) +{ + if (pdraw == nullptr) + return (enum pdraw_pipeline_mode)(-EINVAL); + + return pdraw->pdraw->getPipelineModeSetting(); +} + + +int pdraw_set_pipeline_mode_setting(struct pdraw *pdraw, + enum pdraw_pipeline_mode mode) +{ + if (pdraw == nullptr) + return -EINVAL; + + pdraw->pdraw->setPipelineModeSetting(mode); + return 0; +} + + +int pdraw_get_display_screen_settings(struct pdraw *pdraw, + float *xdpi, + float *ydpi, + float *device_margin_top, + float *device_margin_bottom, + float *device_margin_left, + float *device_margin_right) +{ + if (pdraw == nullptr) + return -EINVAL; + + pdraw->pdraw->getDisplayScreenSettings(xdpi, + ydpi, + device_margin_top, + device_margin_bottom, + device_margin_left, + device_margin_right); + return 0; +} + + +int pdraw_set_display_screen_settings(struct pdraw *pdraw, + float xdpi, + float ydpi, + float device_margin_top, + float device_margin_bottom, + float device_margin_left, + float device_margin_right) +{ + if (pdraw == nullptr) + return -EINVAL; + + pdraw->pdraw->setDisplayScreenSettings(xdpi, + ydpi, + device_margin_top, + device_margin_bottom, + device_margin_left, + device_margin_right); + return 0; +} + + +enum pdraw_hmd_model pdraw_get_hmd_model_setting(struct pdraw *pdraw) +{ + if (pdraw == nullptr) + return (enum pdraw_hmd_model)(-EINVAL); + + return pdraw->pdraw->getHmdModelSetting(); +} + + +int pdraw_set_hmd_model_setting(struct pdraw *pdraw, + enum pdraw_hmd_model hmd_model) +{ + if (pdraw == nullptr) + return -EINVAL; + + pdraw->pdraw->setHmdModelSetting(hmd_model); + return 0; +} + + +int pdraw_set_android_jvm(struct pdraw *pdraw, void *jvm) +{ + if (pdraw == nullptr) + return -EINVAL; + + pdraw->pdraw->setAndroidJvm(jvm); + return 0; +} + + +int pdraw_dump_pipeline(struct pdraw *pdraw, const char *file_name) +{ + if ((pdraw == nullptr) || (file_name == nullptr)) + return -EINVAL; + + std::string f(file_name); + return pdraw->pdraw->dumpPipeline(f); +} + + +const char *pdraw_hmd_model_str(enum pdraw_hmd_model val) +{ + return pdraw_hmdModelStr(val); +} + + +const char *pdraw_pipeline_mode_str(enum pdraw_pipeline_mode val) +{ + return pdraw_pipelineModeStr(val); +} + + +const char *pdraw_playback_type_str(enum pdraw_playback_type val) +{ + return pdraw_playbackTypeStr(val); +} + + +const char *pdraw_media_type_str(enum pdraw_media_type val) +{ + return pdraw_mediaTypeStr(val); +} + + +const char *pdraw_video_type_str(enum pdraw_video_type val) +{ + return pdraw_videoTypeStr(val); +} + + +const char *pdraw_histogram_channel_str(enum pdraw_histogram_channel val) +{ + return pdraw_histogramChannelStr(val); +} + + +const char *pdraw_video_renderer_scheduling_mode_str( + enum pdraw_video_renderer_scheduling_mode val) +{ + return pdraw_videoRendererSchedulingModeStr(val); +} + + +const char * +pdraw_video_renderer_fill_mode_str(enum pdraw_video_renderer_fill_mode val) +{ + return pdraw_videoRendererFillModeStr(val); +} + + +const char *pdraw_video_renderer_transition_flag_str( + enum pdraw_video_renderer_transition_flag val) +{ + return pdraw_videoRendererTransitionFlagStr(val); +} + + +int pdraw_video_frame_to_json_str(const struct pdraw_video_frame *frame, + struct vmeta_frame *metadata, + char *str, + unsigned int len) +{ + return pdraw_frameMetadataToJsonStr(frame, metadata, str, len); +} + + +int pdraw_video_frame_to_json(const struct pdraw_video_frame *frame, + struct vmeta_frame *metadata, + struct json_object *jobj) +{ + return pdraw_frameMetadataToJson(frame, metadata, jobj); +} + + +struct pdraw_media_info * +pdraw_media_info_dup(const struct pdraw_media_info *src) +{ + return pdraw_mediaInfoDup(src); +} + + +void pdraw_media_info_free(struct pdraw_media_info *media_info) +{ + return pdraw_mediaInfoFree(media_info); +} diff --git a/qpdraw/atom.mk b/qpdraw/atom.mk index ab3a9a9..03cd925 100644 --- a/qpdraw/atom.mk +++ b/qpdraw/atom.mk @@ -1,23 +1,23 @@ - -LOCAL_PATH := $(call my-dir) - -ifeq ("$(TARGET_OS)","linux") -ifneq ("$(TARGET_OS_FLAVOUR)","android") - -include $(CLEAR_VARS) - -LOCAL_MODULE := qpdraw -LOCAL_CATEGORY_PATH := libs -LOCAL_DESCRIPTION := Parrot Drones Awesome Video Viewer Qt library -LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include -LOCAL_CXXFLAGS := -std=c++11 -LOCAL_LIBRARIES := \ - libpdraw-backend \ - libulog -LOCAL_DEPENDS_MODULES := qt5-base -LOCAL_EXPORT_LDLIBS := -lqpdraw - -include $(BUILD_QMAKE) - -endif -endif + +LOCAL_PATH := $(call my-dir) + +ifeq ("$(TARGET_OS)","linux") +ifneq ("$(TARGET_OS_FLAVOUR)","android") + +include $(CLEAR_VARS) + +LOCAL_MODULE := qpdraw +LOCAL_CATEGORY_PATH := libs +LOCAL_DESCRIPTION := Parrot Drones Awesome Video Viewer Qt library +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_CXXFLAGS := -std=c++11 +LOCAL_LIBRARIES := \ + libpdraw-backend \ + libulog +LOCAL_DEPENDS_MODULES := qt5-base +LOCAL_EXPORT_LDLIBS := -lqpdraw + +include $(BUILD_QMAKE) + +endif +endif diff --git a/qpdraw/include/pdraw/qpdraw.hpp b/qpdraw/include/pdraw/qpdraw.hpp index 2ea5c67..9631134 100644 --- a/qpdraw/include/pdraw/qpdraw.hpp +++ b/qpdraw/include/pdraw/qpdraw.hpp @@ -1,125 +1,125 @@ -/** - * Parrot Drones Awesome Video Viewer - * Qt PDrAW object - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _QPDRAW_HPP_ -#define _QPDRAW_HPP_ - -#include - -#include - -namespace QPdraw { - -/* Forward declarations */ -namespace Internal { -class QPdrawPriv; -} - - -class QPdraw : public QObject { - Q_OBJECT - -public: - explicit QPdraw(QObject *parent = nullptr); - - ~QPdraw(); - - /** - * Start a QPdraw instance. - * This function must be called after the object creation prior to - * calling any other function. - * @return 0 on success, negative errno value in case of error - */ - int start(void); - - /** - * Stop a QPdraw instance. - * This function must be called prior to destroying the instance. - * @return 0 on success, negative errno value in case of error - */ - int stop(void); - - /** - * Get the internal PDrAW instance pointer. - * This function returns a pointer to the internal PDrAW instance. - * @return a pointer on the internal PDrAW instance on success, - * null in case of error - */ - intptr_t getInternal(void); - - /** - * Get the internal event loop. - * This function returns a pointer to the event loop used internally. - * @return a pointer on the internal loop on success, - * null in case of error - */ - struct pomp_loop *getLoop(void); - -signals: - /** - * Stop response signal, called when a stop operation is complete or - * has failed. The status parameter is the stop operation status: 0 on - * success, or a negative errno value in case of error. - * @param status: 0 on success, negative errno value in case of error - */ - void stopResponse(int status); - - /** - * Media added signal, called when a media has been added internally in - * the PDrAW pipeline. Medias are for example YUV or H.264 video medias. - * The info structure gives the media identifier that can be used to - * create a video sink on this media. - * @param info: information on the media - */ - void onMediaAdded(struct pdraw_media_info info); - - /** - * Media removed signal, called when a media has been removed internally - * from the PDrAW pipeline. Medias are for example YUV or H.264 video - * medias. When a media is removed, any video sink created on this media - * must then be stopped. - * @param info: information on the media - */ - void onMediaRemoved(struct pdraw_media_info info); - - /** - * Socket creation signal, called immediately after a socket creation - * with its file descriptor as parameter. - * @param fd: socket file descriptor - */ - void onSocketCreated(int fd); - -private: - Internal::QPdrawPriv *mPriv; -}; - -} /* namespace QPdraw */ - -#endif /* !_QPDRAW_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer + * Qt PDrAW object + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _QPDRAW_HPP_ +#define _QPDRAW_HPP_ + +#include + +#include + +namespace QPdraw { + +/* Forward declarations */ +namespace Internal { +class QPdrawPriv; +} + + +class QPdraw : public QObject { + Q_OBJECT + +public: + explicit QPdraw(QObject *parent = nullptr); + + ~QPdraw(); + + /** + * Start a QPdraw instance. + * This function must be called after the object creation prior to + * calling any other function. + * @return 0 on success, negative errno value in case of error + */ + int start(void); + + /** + * Stop a QPdraw instance. + * This function must be called prior to destroying the instance. + * @return 0 on success, negative errno value in case of error + */ + int stop(void); + + /** + * Get the internal PDrAW instance pointer. + * This function returns a pointer to the internal PDrAW instance. + * @return a pointer on the internal PDrAW instance on success, + * null in case of error + */ + intptr_t getInternal(void); + + /** + * Get the internal event loop. + * This function returns a pointer to the event loop used internally. + * @return a pointer on the internal loop on success, + * null in case of error + */ + struct pomp_loop *getLoop(void); + +signals: + /** + * Stop response signal, called when a stop operation is complete or + * has failed. The status parameter is the stop operation status: 0 on + * success, or a negative errno value in case of error. + * @param status: 0 on success, negative errno value in case of error + */ + void stopResponse(int status); + + /** + * Media added signal, called when a media has been added internally in + * the PDrAW pipeline. Medias are for example YUV or H.264 video medias. + * The info structure gives the media identifier that can be used to + * create a video sink on this media. + * @param info: information on the media + */ + void onMediaAdded(struct pdraw_media_info info); + + /** + * Media removed signal, called when a media has been removed internally + * from the PDrAW pipeline. Medias are for example YUV or H.264 video + * medias. When a media is removed, any video sink created on this media + * must then be stopped. + * @param info: information on the media + */ + void onMediaRemoved(struct pdraw_media_info info); + + /** + * Socket creation signal, called immediately after a socket creation + * with its file descriptor as parameter. + * @param fd: socket file descriptor + */ + void onSocketCreated(int fd); + +private: + Internal::QPdrawPriv *mPriv; +}; + +} /* namespace QPdraw */ + +#endif /* !_QPDRAW_HPP_ */ diff --git a/qpdraw/include/pdraw/qpdraw_demuxer.hpp b/qpdraw/include/pdraw/qpdraw_demuxer.hpp index 00f06fe..ea1e671 100644 --- a/qpdraw/include/pdraw/qpdraw_demuxer.hpp +++ b/qpdraw/include/pdraw/qpdraw_demuxer.hpp @@ -1,485 +1,485 @@ -/** - * Parrot Drones Awesome Video Viewer - * Qt PDrAW demuxer object - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _QPDRAW_DEMUXER_HPP_ -#define _QPDRAW_DEMUXER_HPP_ - -#include - -#include -#include - -namespace QPdraw { - -/* Forward declarations */ -namespace Internal { -class QPdrawDemuxerPriv; -} - - -class QPdrawDemuxer : public QObject { - Q_OBJECT - -public: - explicit QPdrawDemuxer(QPdraw *parent); - - ~QPdrawDemuxer(); - - /** - * Open a demuxer on a URL (stream or local file). - * The URL can be either an RTSP URL (starting with "rtsp://") or a - * local file path (either absolute or relative). - * The function returns before the actual opening is done. If the - * function returns 0, the openResponse() signal will be emitted once - * the open operation is successful (0 status) or has failed (negative - * errno status). If the function returns a negative errno value - * (immediate failure), the openResponse() signal will not be emitted. - * Once a demuxer is no longer used, it must be closed and then - * destroyed (@see the close() function). - * @param url: URL of the resource to open - * @return 0 on success, negative errno value in case of error - */ - int open(const std::string &url); - - /** - * Open a demuxer on a single stream. - * This function opens an RTP/AVP stream. No session management is done: - * it is the application's responsibility to handle the ports - * negociation with the sender. If null local ports are given as - * parameter, the effective port numbers used can be retrieved using the - * getSingleStreamLocalStreamPort() for the RTP port and - * getSingleStreamLocalControlPort() for the RTCP port functions. - * If the localAddr parameter is left empty, any local network interface - * will be used. The remoteAddr, remoteStreamPort and remoteControlPort - * parameters can be left null/empty if unknown; they will be known once - * the stream is being received. - * The function returns before the actual opening is done. If the - * function returns 0, the openResponse() signal will be emitted once - * the open operation is successful (0 status) or has failed (negative - * errno status). If the function returns a negative errno value - * (immediate failure), the openResponse() signal will not be emitted. - * Once a demuxer is no longer used, it must be closed and then - * destroyed (@see the close() function). - * @param url: URL of the resource to open - * @return 0 on success, negative errno value in case of error - */ - int open(const std::string &localAddr, - uint16_t localStreamPort, - uint16_t localControlPort, - const std::string &remoteAddr, - uint16_t remoteStreamPort, - uint16_t remoteControlPort); - - /** - * Open a demuxer on a stream URL through a mux channel. - * The URL must be an RTSP URL (starting with "rtsp://"). Mux channels - * are used to transfer data between a SkyController remote and a - * smartphone through USB; see Parrot's libmux for more information. - * No concurrent sessions can run on the mux channel; therefore the user - * must take care of limiting the number of PDrAW instances and demuxer - * objects running on the mux channel to only one. - * The function returns before the actual opening is done. If the - * function returns 0, the openResponse() signal will be emitted once - * the open operation is successful (0 status) or has failed (negative - * errno status). If the function returns a negative errno value - * (immediate failure), the openResponse() signal will not be emitted. - * Once a demuxer is no longer used, it must be closed and then - * destroyed (@see the close() function). - * @param url: URL of the resource to open - * @return 0 on success, negative errno value in case of error - */ - int open(const std::string &url, struct mux_ctx *mux); - - /** - * Close a demuxer. - * This function closes a previously opened demuxer. The function - * returns before the actual closing is done. If the function returns 0, - * the closeResponse() signal will be emitted once the close is - * successful (0 status) or has failed (negative errno status). If the - * function returns a negative errno value (immediate failure), the - * closeResponse() signal will not be emitted. After a successful close, - * the demuxer must be destroyed. - * @return 0 on success, negative errno value in case of error - */ - int close(void); - - /** - * Get the single stream local stream port. - * This function returns the local stream (RTP) port currently in use - * after a succesful open operation on a single stream (RTP/AVP) or an - * RTSP URL. If no open operation has been done, or if an open operation - * has been done on a mux channel, 0 is returned. This is useful when - * opening a single stream with null local ports to let PDrAW open - * sockets on any available port. - * @return the stream port on success, 0 in case of error - */ - uint16_t getSingleStreamLocalStreamPort(void); - - /** - * Get the single stream local control port. - * This function returns the local control (RTCP) port currently in use - * after a succesful open operation on a single stream (RTP/AVP) or an - * RTSP URL. If no open operation has been done, or if an open operation - * has been done on a mux channel, 0 is returned. This is useful when - * opening a single stream with null local ports to let PDrAW open - * sockets on any available port. - * @return the stream port on success, 0 in case of error - */ - uint16_t getSingleStreamLocalControlPort(void); - - /** - * Get the ready to play status. - * This function returns true if a successful open operation has been - * completed and if the playback is ready to start, false otherwise. One - * case when a successful open operation is complete but the playback is - * not ready is when connected to a SkyController's RTSP server but the - * SkyController itself is not yet connected to a drone. The value - * returned by this function is identical to the ready parameter passed - * to the readyToPlay() listener function when it is called. - * @return the ready to play status on success, false in case of error - */ - bool isReadyToPlay(void); - - /** - * Get the pause status. - * This function returns true if the playback is currently paused, false - * otherwise. - * @return the pause status on success, false in case of error - */ - bool isPaused(void); - - /** - * Play at the given speed. - * This function starts the playback of the video. The speed parameter - * is optional and defaults to 1.0. If the speed parameter is negative, - * the video is played backward. If the speed is greater than or equal - * to PDRAW_PLAY_SPEED_MAX, the speed is ignored and the video is played - * at the maximum speed achievable. If the speed is less than or equal - * to -PDRAW_PLAY_SPEED_MAX, the speed is ignored and the video is - * played backward at the maximum speed achievable. A 0.0 speed - * has the same effet as calling the pause() function. On a live - * stream, the speed parameter has no effect. The function returns - * before the actual operation is done. If the function returns 0, the - * playResponse() signal will be emitted once the play is successful (0 - * status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the - * playResponse() signal will not be emitted. - * @param speed: playback speed (0.0 means pause, negative value - * means play backward) - * @return 0 on success, negative errno value in case of error - */ - int play(float speed = 1.0f); - - /** - * Pause the playback. - * This function suspends the playback of the video. The session is not - * closed and the playback can be resumed using the play() function. The - * function returns before the actual operation is done. If the function - * returns 0, the pauseResponse() signal will be emitted once the pause - * is successful (0 status) or has failed (negative errno status). If - * the function returns a negative errno value (immediate failure), the - * pauseResponse() signal will not be emitted. - * @return 0 on success, negative errno value in case of error - */ - int pause(void); - - /** - * Go to previous frame in frame-by-frame playback. - * This function plays the previous frame while the playback is paused. - * If the playback is not currently paused an error is returned. - * Frame-by-frame is only available on local replays (MP4 records). The - * function returns before the actual operation is done. If the function - * returns 0, the seekResponse() signal will be emitted once the seek is - * successful (0 status) or has failed (negative errno status). If the - * function returns a negative errno value (immediate failure), the - * seekResponse() signal will not be emitted. - * @return 0 on success, negative errno value in case of error - */ - int previousFrame(void); - - /** - * Go to next frame in frame-by-frame playback. - * This function plays the next frame while the playback is paused. If - * the playback is not currently paused an error is returned. - * Frame-by-frame is only available on local replays (MP4 records). The - * function returns before the actual operation is done. If the function - * returns 0, the seekResponse() signal will be emitted once the seek is - * successful (0 status) or has failed (negative errno status). If the - * function returns a negative errno value (immediate failure), the - * seekResponse() signal will not be emitted. - * @return 0 on success, negative errno value in case of error - */ - int nextFrame(void); - - /** - * Seek forward or backward. - * This function seeks forward (positive delta) or backward (negative - * delta). The delta parameter is in microseconds. When exact is false - * the seek is done to the nearest synchronization sample preceeding the - * delta, otherwise the seek is done to the sample nearest to the delta. - * Seeking is only available on replays (either local or streamed), not - * on live streams. The function returns before the actual operation is - * done. If the function returns 0, the seekResponse() signal will be - * emitted once the seek is successful (0 status) or has failed - * (negative errno status). If the function returns a negative errno - * value (immediate failure), the seekResponse() signal will not be - * emitted. - * @param delta: time delta in microseconds (positive or - * negative) - * @param exact: true means seek to the sample closest to the - * delta, false means seek to the nearest - * synchronization sample preceeding the delta - * @return 0 on success, negative errno value in case of error - */ - int seek(int64_t delta, bool exact = false); - - /** - * Seek forward. - * This function seeks forward by delta microseconds. It has the same - * behavior as calling seek() with a positive delta. When exact is false - * the seek is done to the nearest synchronization sample preceeding the - * delta, otherwise the seek is done to the sample nearest to the delta. - * Seeking is only available on replays (either local or streamed), not - * on live streams. The function returns before the actual operation is - * done. If the function returns 0, the seekResponse() signal will be - * emitted once the seek is successful (0 status) or has failed - * (negative errno status). If the function returns a negative errno - * value (immediate failure), the seekResponse() signal will not be - * emitted. - * @param delta: positive time delta forward in microseconds - * @param exact: true means seek to the sample closest to the - * delta, false means seek to the nearest - * synchronization sample preceeding the delta - * @return 0 on success, negative errno value in case of error - */ - int seekForward(uint64_t delta, bool exact = false); - - /** - * Seek backward. - * This function seeks backward by delta microseconds (postive). It has - * the same behavior as calling seek() with a negative delta. When exact - * is false the seek is done to the nearest synchronization sample - * preceeding the delta, otherwise the seek is done to the sample - * nearest to the delta. Seeking is only available on replays (either - * local or streamed), not on live streams. The function returns before - * the actual operation is done. If the function returns 0, the - * seekResponse() signal will be emitted once the seek is successful - * (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the - * seekResponse() signal will not be emitted. - * @param delta: positive time delta backward in microseconds - * @param exact: true means seek to the sample closest to the - * delta, false means seek to the nearest - * synchronization sample preceeding the delta - * @return 0 on success, negative errno value in case of error - */ - int seekBack(uint64_t delta, bool exact = false); - - /** - * Seek to a given time. - * This function seeks to the given play timestamp in microseconds. When - * exact is false the seek is done to the nearest synchronization sample - * preceeding the timestamp, otherwise the seek is done to the sample - * nearest to the timestamp. Seeking is only available on replays - * (either local or streamed), not on live streams. The function returns - * before the actual operation is done. If the function returns 0, the - * seekResponse() signal will be emitted once the seek is successful - * (0 status) or has failed (negative errno status). If the function - * returns a negative errno value (immediate failure), the - * seekResponse() signal will not be emitted. - * @param timestamp: play timestamp in microseconds - * @param exact: true means seek to the sample closest to the - * timestamp, false means seek to the nearest - * synchronization sample preceeding the timestamp - * @return 0 on success, negative errno value in case of error - */ - int seekTo(uint64_t timestamp, bool exact = false); - - /** - * Get the playback duration. - * This function returns the playback duration in microseconds. - * The duration is only available on replays (either local or streamed), - * not on live streams. - * @return the duration in microseconds on success, - * 0 in case of error - */ - uint64_t getDuration(void); - - /** - * Get the playback current time. - * This function returns the current playback position in microseconds. - * On replays (either local or streamed) this is the position between 0 - * and the duration; on live streams this is the time since the start of - * the stream session. - * @return the current time in microseconds on success, - * 0 in case of error - */ - uint64_t getCurrentTime(void); - -signals: - /** - * Open response signal, called when an open operation is complete or - * has failed. The status parameter is the open operation status: 0 on - * success, or a negative errno value in case of error. - * If this function reports an error, the close() function must be - * called and one must wait for the demuxerCloseResponse() listener - * function to be called prior to destroying the demuxer. - * @param status: 0 on success, negative errno value in - * case of error - */ - void openResponse(int status); - - /** - * Close response signal, called when a close operation is complete or - * has failed. The status parameter is the close operation status: 0 on - * success, or a negative errno value in case of error. - * @param status: 0 on success, negative errno value in - * case of error - */ - void closeResponse(int status); - - /** - * Unrecoverable error signal, called when a previously opened demuxer - * is no longer running. When this function is called, the demuxer is no - * longer running; the close() function must be called and one must wait - * for the demuxerCloseResponse() listener function to be called prior - * to destroying the demuxer. - */ - void onUnrecoverableError(void); - - /** - * Demuxer media selection signal, emitted with a list of video medias - * found from which the application must choose one or more to process - * in the pipeline. The retVal parameter must be a bitfield of the - * identifiers of the chosen medias (from the pdraw_demuxer_media - * structure), or 0 to choose the default media. If the retVal value is - * -ENOSYS, the callback is considered not implemented and the default - * media is chosen. If the retVal value is -ECANCELED no media is chosen - * and the open operation is aborted. If the retVal value is another - * negative errno or an invalid media identifier the openResponse() - * signal will be emitted if an open operation is in progress, or the - * onUnrecoverableError() signal otherwise. - * Note: this signal must be connected using a Qt::DirectConnection, - * to ensure that the slot is executed synchronously and that the - * retVal parameter is set before this function returns. - * @param medias: array of demuxer media - * @param count: demuxer media array element count - * @param retVal: a bitfield of the identifiers of the chosen chosen - * medias, 0 or -ENOSYS to choose the default media, - * -ECANCELED to choose no media and abort the open - * operation, or another negative errno value in case - * of error - */ - void selectMedia(const struct pdraw_demuxer_media *medias, - unsigned int count, - int *retVal); - - /** - * Ready to play signal, emitted when the playback is ready to start. - * This signal is emitted to indicate that the demuxer is ready to - * process play operations. Generally the demuxer is ready to play as - * soon as the openResponse() signal has been emitted with a success - * status. One case when the open operation was successful and the - * playback is not ready is when connected to a SkyController's RTSP - * server but the SkyController itself is not yet connected to a drone. - * Similarly, when connected to a drone's stream through - * a SkyController's RTSP server, if the drone is - * disconnected from the SkyController, this function - * will be called with a false value in the ready - * parameter. - * @param ready: true if the session is ready to play, - * false otherwise - */ - void readyToPlay(bool ready); - - /** - * End of range signal, emitted when the playback is suspended after - * having reached the end of the playback duration. This signal is only - * emitted for replays (either local or streamed), not for live streams. - * The timestamp parameter is the current play time in microseconds at - * the moment the playback is suspended. - * @param timestamp: current playback time in - * microseconds - */ - void onEndOfRange(quint64 timestamp); - - /** - * Play response signal, emitted when a play operation is complete (the - * playback has started) or has failed. The status parameter is the play - * operation status: 0 on success, or a negative errno value in case of - * error. The timestamp parameter is the current play time in - * microseconds at the moment the playback is started. The speed - * parameter is the current playback speed; a negative value means - * playing backward. - * @param status: 0 on success, negative errno value - * in case of error - * @param timestamp: current playback time in - * microseconds - * @param speed: current playback speed, negative means - * backward - */ - void playResponse(int status, quint64 timestamp, float speed); - - /** - * Pause response signal, emitted when a pause operation is complete - * (the playback is suspended) or has failed. The status parameter is - * the pause operation status: 0 on success, or a negative errno value - * in case of error. The timestamp parameter is the current play time in - * microseconds at the moment the playback is paused. - * @param status: 0 on success, negative errno value - * in case of error - * @param timestamp: current playback time in - * microseconds - */ - void pauseResponse(int status, quint64 timestamp); - - /** - * Seek response signal, emitted when a seek operation is complete or - * has failed. The status parameter is the seek operation status: 0 on - * success, or a negative errno value in case of error. The timestamp - * parameter is the current play time in microseconds after seeking. The - * speed parameter is the current playback speed; a negative value means - * playing backward. - * @param status: 0 on success, negative errno value - * in case of error - * @param timestamp: current playback time in - * microseconds - * @param speed: current playback speed, negative means - * backward - */ - void seekResponse(int status, quint64 timestamp, float speed); - -private: - Internal::QPdrawDemuxerPriv *mPriv; -}; - -} /* namespace QPdraw */ - -#endif /* !_QPDRAW_DEMUXER_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer + * Qt PDrAW demuxer object + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _QPDRAW_DEMUXER_HPP_ +#define _QPDRAW_DEMUXER_HPP_ + +#include + +#include +#include + +namespace QPdraw { + +/* Forward declarations */ +namespace Internal { +class QPdrawDemuxerPriv; +} + + +class QPdrawDemuxer : public QObject { + Q_OBJECT + +public: + explicit QPdrawDemuxer(QPdraw *parent); + + ~QPdrawDemuxer(); + + /** + * Open a demuxer on a URL (stream or local file). + * The URL can be either an RTSP URL (starting with "rtsp://") or a + * local file path (either absolute or relative). + * The function returns before the actual opening is done. If the + * function returns 0, the openResponse() signal will be emitted once + * the open operation is successful (0 status) or has failed (negative + * errno status). If the function returns a negative errno value + * (immediate failure), the openResponse() signal will not be emitted. + * Once a demuxer is no longer used, it must be closed and then + * destroyed (@see the close() function). + * @param url: URL of the resource to open + * @return 0 on success, negative errno value in case of error + */ + int open(const std::string &url); + + /** + * Open a demuxer on a single stream. + * This function opens an RTP/AVP stream. No session management is done: + * it is the application's responsibility to handle the ports + * negociation with the sender. If null local ports are given as + * parameter, the effective port numbers used can be retrieved using the + * getSingleStreamLocalStreamPort() for the RTP port and + * getSingleStreamLocalControlPort() for the RTCP port functions. + * If the localAddr parameter is left empty, any local network interface + * will be used. The remoteAddr, remoteStreamPort and remoteControlPort + * parameters can be left null/empty if unknown; they will be known once + * the stream is being received. + * The function returns before the actual opening is done. If the + * function returns 0, the openResponse() signal will be emitted once + * the open operation is successful (0 status) or has failed (negative + * errno status). If the function returns a negative errno value + * (immediate failure), the openResponse() signal will not be emitted. + * Once a demuxer is no longer used, it must be closed and then + * destroyed (@see the close() function). + * @param url: URL of the resource to open + * @return 0 on success, negative errno value in case of error + */ + int open(const std::string &localAddr, + uint16_t localStreamPort, + uint16_t localControlPort, + const std::string &remoteAddr, + uint16_t remoteStreamPort, + uint16_t remoteControlPort); + + /** + * Open a demuxer on a stream URL through a mux channel. + * The URL must be an RTSP URL (starting with "rtsp://"). Mux channels + * are used to transfer data between a SkyController remote and a + * smartphone through USB; see Parrot's libmux for more information. + * No concurrent sessions can run on the mux channel; therefore the user + * must take care of limiting the number of PDrAW instances and demuxer + * objects running on the mux channel to only one. + * The function returns before the actual opening is done. If the + * function returns 0, the openResponse() signal will be emitted once + * the open operation is successful (0 status) or has failed (negative + * errno status). If the function returns a negative errno value + * (immediate failure), the openResponse() signal will not be emitted. + * Once a demuxer is no longer used, it must be closed and then + * destroyed (@see the close() function). + * @param url: URL of the resource to open + * @return 0 on success, negative errno value in case of error + */ + int open(const std::string &url, struct mux_ctx *mux); + + /** + * Close a demuxer. + * This function closes a previously opened demuxer. The function + * returns before the actual closing is done. If the function returns 0, + * the closeResponse() signal will be emitted once the close is + * successful (0 status) or has failed (negative errno status). If the + * function returns a negative errno value (immediate failure), the + * closeResponse() signal will not be emitted. After a successful close, + * the demuxer must be destroyed. + * @return 0 on success, negative errno value in case of error + */ + int close(void); + + /** + * Get the single stream local stream port. + * This function returns the local stream (RTP) port currently in use + * after a succesful open operation on a single stream (RTP/AVP) or an + * RTSP URL. If no open operation has been done, or if an open operation + * has been done on a mux channel, 0 is returned. This is useful when + * opening a single stream with null local ports to let PDrAW open + * sockets on any available port. + * @return the stream port on success, 0 in case of error + */ + uint16_t getSingleStreamLocalStreamPort(void); + + /** + * Get the single stream local control port. + * This function returns the local control (RTCP) port currently in use + * after a succesful open operation on a single stream (RTP/AVP) or an + * RTSP URL. If no open operation has been done, or if an open operation + * has been done on a mux channel, 0 is returned. This is useful when + * opening a single stream with null local ports to let PDrAW open + * sockets on any available port. + * @return the stream port on success, 0 in case of error + */ + uint16_t getSingleStreamLocalControlPort(void); + + /** + * Get the ready to play status. + * This function returns true if a successful open operation has been + * completed and if the playback is ready to start, false otherwise. One + * case when a successful open operation is complete but the playback is + * not ready is when connected to a SkyController's RTSP server but the + * SkyController itself is not yet connected to a drone. The value + * returned by this function is identical to the ready parameter passed + * to the readyToPlay() listener function when it is called. + * @return the ready to play status on success, false in case of error + */ + bool isReadyToPlay(void); + + /** + * Get the pause status. + * This function returns true if the playback is currently paused, false + * otherwise. + * @return the pause status on success, false in case of error + */ + bool isPaused(void); + + /** + * Play at the given speed. + * This function starts the playback of the video. The speed parameter + * is optional and defaults to 1.0. If the speed parameter is negative, + * the video is played backward. If the speed is greater than or equal + * to PDRAW_PLAY_SPEED_MAX, the speed is ignored and the video is played + * at the maximum speed achievable. If the speed is less than or equal + * to -PDRAW_PLAY_SPEED_MAX, the speed is ignored and the video is + * played backward at the maximum speed achievable. A 0.0 speed + * has the same effet as calling the pause() function. On a live + * stream, the speed parameter has no effect. The function returns + * before the actual operation is done. If the function returns 0, the + * playResponse() signal will be emitted once the play is successful (0 + * status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the + * playResponse() signal will not be emitted. + * @param speed: playback speed (0.0 means pause, negative value + * means play backward) + * @return 0 on success, negative errno value in case of error + */ + int play(float speed = 1.0f); + + /** + * Pause the playback. + * This function suspends the playback of the video. The session is not + * closed and the playback can be resumed using the play() function. The + * function returns before the actual operation is done. If the function + * returns 0, the pauseResponse() signal will be emitted once the pause + * is successful (0 status) or has failed (negative errno status). If + * the function returns a negative errno value (immediate failure), the + * pauseResponse() signal will not be emitted. + * @return 0 on success, negative errno value in case of error + */ + int pause(void); + + /** + * Go to previous frame in frame-by-frame playback. + * This function plays the previous frame while the playback is paused. + * If the playback is not currently paused an error is returned. + * Frame-by-frame is only available on local replays (MP4 records). The + * function returns before the actual operation is done. If the function + * returns 0, the seekResponse() signal will be emitted once the seek is + * successful (0 status) or has failed (negative errno status). If the + * function returns a negative errno value (immediate failure), the + * seekResponse() signal will not be emitted. + * @return 0 on success, negative errno value in case of error + */ + int previousFrame(void); + + /** + * Go to next frame in frame-by-frame playback. + * This function plays the next frame while the playback is paused. If + * the playback is not currently paused an error is returned. + * Frame-by-frame is only available on local replays (MP4 records). The + * function returns before the actual operation is done. If the function + * returns 0, the seekResponse() signal will be emitted once the seek is + * successful (0 status) or has failed (negative errno status). If the + * function returns a negative errno value (immediate failure), the + * seekResponse() signal will not be emitted. + * @return 0 on success, negative errno value in case of error + */ + int nextFrame(void); + + /** + * Seek forward or backward. + * This function seeks forward (positive delta) or backward (negative + * delta). The delta parameter is in microseconds. When exact is false + * the seek is done to the nearest synchronization sample preceeding the + * delta, otherwise the seek is done to the sample nearest to the delta. + * Seeking is only available on replays (either local or streamed), not + * on live streams. The function returns before the actual operation is + * done. If the function returns 0, the seekResponse() signal will be + * emitted once the seek is successful (0 status) or has failed + * (negative errno status). If the function returns a negative errno + * value (immediate failure), the seekResponse() signal will not be + * emitted. + * @param delta: time delta in microseconds (positive or + * negative) + * @param exact: true means seek to the sample closest to the + * delta, false means seek to the nearest + * synchronization sample preceeding the delta + * @return 0 on success, negative errno value in case of error + */ + int seek(int64_t delta, bool exact = false); + + /** + * Seek forward. + * This function seeks forward by delta microseconds. It has the same + * behavior as calling seek() with a positive delta. When exact is false + * the seek is done to the nearest synchronization sample preceeding the + * delta, otherwise the seek is done to the sample nearest to the delta. + * Seeking is only available on replays (either local or streamed), not + * on live streams. The function returns before the actual operation is + * done. If the function returns 0, the seekResponse() signal will be + * emitted once the seek is successful (0 status) or has failed + * (negative errno status). If the function returns a negative errno + * value (immediate failure), the seekResponse() signal will not be + * emitted. + * @param delta: positive time delta forward in microseconds + * @param exact: true means seek to the sample closest to the + * delta, false means seek to the nearest + * synchronization sample preceeding the delta + * @return 0 on success, negative errno value in case of error + */ + int seekForward(uint64_t delta, bool exact = false); + + /** + * Seek backward. + * This function seeks backward by delta microseconds (postive). It has + * the same behavior as calling seek() with a negative delta. When exact + * is false the seek is done to the nearest synchronization sample + * preceeding the delta, otherwise the seek is done to the sample + * nearest to the delta. Seeking is only available on replays (either + * local or streamed), not on live streams. The function returns before + * the actual operation is done. If the function returns 0, the + * seekResponse() signal will be emitted once the seek is successful + * (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the + * seekResponse() signal will not be emitted. + * @param delta: positive time delta backward in microseconds + * @param exact: true means seek to the sample closest to the + * delta, false means seek to the nearest + * synchronization sample preceeding the delta + * @return 0 on success, negative errno value in case of error + */ + int seekBack(uint64_t delta, bool exact = false); + + /** + * Seek to a given time. + * This function seeks to the given play timestamp in microseconds. When + * exact is false the seek is done to the nearest synchronization sample + * preceeding the timestamp, otherwise the seek is done to the sample + * nearest to the timestamp. Seeking is only available on replays + * (either local or streamed), not on live streams. The function returns + * before the actual operation is done. If the function returns 0, the + * seekResponse() signal will be emitted once the seek is successful + * (0 status) or has failed (negative errno status). If the function + * returns a negative errno value (immediate failure), the + * seekResponse() signal will not be emitted. + * @param timestamp: play timestamp in microseconds + * @param exact: true means seek to the sample closest to the + * timestamp, false means seek to the nearest + * synchronization sample preceeding the timestamp + * @return 0 on success, negative errno value in case of error + */ + int seekTo(uint64_t timestamp, bool exact = false); + + /** + * Get the playback duration. + * This function returns the playback duration in microseconds. + * The duration is only available on replays (either local or streamed), + * not on live streams. + * @return the duration in microseconds on success, + * 0 in case of error + */ + uint64_t getDuration(void); + + /** + * Get the playback current time. + * This function returns the current playback position in microseconds. + * On replays (either local or streamed) this is the position between 0 + * and the duration; on live streams this is the time since the start of + * the stream session. + * @return the current time in microseconds on success, + * 0 in case of error + */ + uint64_t getCurrentTime(void); + +signals: + /** + * Open response signal, called when an open operation is complete or + * has failed. The status parameter is the open operation status: 0 on + * success, or a negative errno value in case of error. + * If this function reports an error, the close() function must be + * called and one must wait for the demuxerCloseResponse() listener + * function to be called prior to destroying the demuxer. + * @param status: 0 on success, negative errno value in + * case of error + */ + void openResponse(int status); + + /** + * Close response signal, called when a close operation is complete or + * has failed. The status parameter is the close operation status: 0 on + * success, or a negative errno value in case of error. + * @param status: 0 on success, negative errno value in + * case of error + */ + void closeResponse(int status); + + /** + * Unrecoverable error signal, called when a previously opened demuxer + * is no longer running. When this function is called, the demuxer is no + * longer running; the close() function must be called and one must wait + * for the demuxerCloseResponse() listener function to be called prior + * to destroying the demuxer. + */ + void onUnrecoverableError(void); + + /** + * Demuxer media selection signal, emitted with a list of video medias + * found from which the application must choose one or more to process + * in the pipeline. The retVal parameter must be a bitfield of the + * identifiers of the chosen medias (from the pdraw_demuxer_media + * structure), or 0 to choose the default media. If the retVal value is + * -ENOSYS, the callback is considered not implemented and the default + * media is chosen. If the retVal value is -ECANCELED no media is chosen + * and the open operation is aborted. If the retVal value is another + * negative errno or an invalid media identifier the openResponse() + * signal will be emitted if an open operation is in progress, or the + * onUnrecoverableError() signal otherwise. + * Note: this signal must be connected using a Qt::DirectConnection, + * to ensure that the slot is executed synchronously and that the + * retVal parameter is set before this function returns. + * @param medias: array of demuxer media + * @param count: demuxer media array element count + * @param retVal: a bitfield of the identifiers of the chosen chosen + * medias, 0 or -ENOSYS to choose the default media, + * -ECANCELED to choose no media and abort the open + * operation, or another negative errno value in case + * of error + */ + void selectMedia(const struct pdraw_demuxer_media *medias, + unsigned int count, + int *retVal); + + /** + * Ready to play signal, emitted when the playback is ready to start. + * This signal is emitted to indicate that the demuxer is ready to + * process play operations. Generally the demuxer is ready to play as + * soon as the openResponse() signal has been emitted with a success + * status. One case when the open operation was successful and the + * playback is not ready is when connected to a SkyController's RTSP + * server but the SkyController itself is not yet connected to a drone. + * Similarly, when connected to a drone's stream through + * a SkyController's RTSP server, if the drone is + * disconnected from the SkyController, this function + * will be called with a false value in the ready + * parameter. + * @param ready: true if the session is ready to play, + * false otherwise + */ + void readyToPlay(bool ready); + + /** + * End of range signal, emitted when the playback is suspended after + * having reached the end of the playback duration. This signal is only + * emitted for replays (either local or streamed), not for live streams. + * The timestamp parameter is the current play time in microseconds at + * the moment the playback is suspended. + * @param timestamp: current playback time in + * microseconds + */ + void onEndOfRange(quint64 timestamp); + + /** + * Play response signal, emitted when a play operation is complete (the + * playback has started) or has failed. The status parameter is the play + * operation status: 0 on success, or a negative errno value in case of + * error. The timestamp parameter is the current play time in + * microseconds at the moment the playback is started. The speed + * parameter is the current playback speed; a negative value means + * playing backward. + * @param status: 0 on success, negative errno value + * in case of error + * @param timestamp: current playback time in + * microseconds + * @param speed: current playback speed, negative means + * backward + */ + void playResponse(int status, quint64 timestamp, float speed); + + /** + * Pause response signal, emitted when a pause operation is complete + * (the playback is suspended) or has failed. The status parameter is + * the pause operation status: 0 on success, or a negative errno value + * in case of error. The timestamp parameter is the current play time in + * microseconds at the moment the playback is paused. + * @param status: 0 on success, negative errno value + * in case of error + * @param timestamp: current playback time in + * microseconds + */ + void pauseResponse(int status, quint64 timestamp); + + /** + * Seek response signal, emitted when a seek operation is complete or + * has failed. The status parameter is the seek operation status: 0 on + * success, or a negative errno value in case of error. The timestamp + * parameter is the current play time in microseconds after seeking. The + * speed parameter is the current playback speed; a negative value means + * playing backward. + * @param status: 0 on success, negative errno value + * in case of error + * @param timestamp: current playback time in + * microseconds + * @param speed: current playback speed, negative means + * backward + */ + void seekResponse(int status, quint64 timestamp, float speed); + +private: + Internal::QPdrawDemuxerPriv *mPriv; +}; + +} /* namespace QPdraw */ + +#endif /* !_QPDRAW_DEMUXER_HPP_ */ diff --git a/qpdraw/include/pdraw/qpdraw_widget.hpp b/qpdraw/include/pdraw/qpdraw_widget.hpp index a974428..6b16659 100644 --- a/qpdraw/include/pdraw/qpdraw_widget.hpp +++ b/qpdraw/include/pdraw/qpdraw_widget.hpp @@ -1,192 +1,192 @@ -/** - * Parrot Drones Awesome Video Viewer - * Qt PDrAW widget - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _QPDRAW_WIDGET_HPP_ -#define _QPDRAW_WIDGET_HPP_ - -#include - -#include -#include - -#include - -#include -#include - -namespace QPdraw { - -/* Forward declarations */ -namespace Internal { -class QPdrawWidgetPriv; -} - - -class QPdrawWidget : public QOpenGLWidget { - Q_OBJECT - -public: - explicit QPdrawWidget(QWidget *parent = nullptr); - - ~QPdrawWidget(); - - /** - * Start a QPdraw widget. - * This function creates a QPdraw widget on a media of the given media - * id; if the media id is zero the first raw media encountered is used. - * This function must be called after the object creation prior to - * calling any other function. - * @param pdraw: information on the media - * @param mediaId: identifier of the raw media to render (from a - * pdraw_media_info structure); if zero the first - * raw media found is used for rendering - * @return 0 on success, negative errno value in case of error - */ - void start(QPdraw *pdraw, unsigned int mediaId); - - /** - * Start a QPdraw widget with rendering position and params. - * This function creates a QPdraw widget on a media of the given media - * id; if the media id is zero the first raw media encountered is used. - * The rendering position and rendering parameters are also provided. - * This function must be called after the object creation prior to - * calling any other function. - * @param pdraw: information on the media - * @param mediaId: identifier of the raw media to render (from a - * pdraw_media_info structure); if zero the first - * raw media found is used for rendering - * @param renderPos: rendering position and size - * @param params: renderer parameters - * @return 0 on success, negative errno value in case of error - */ - void start(QPdraw *pdraw, - unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params); - - /** - * Stop a QPdraw widget. - * This function must be called prior to destroying the instance. - * @return 0 on success, negative errno value in case of error - */ - void stop(); - - /** - * Get the widget rendering rectangle. - * @return the rendering rectangle - */ - QRect getVideoRect() const; - -signals: - - /** - * Media added signal, called when a media has been added internally to - * the renderer. Medias are raw video medias. - * @param info: information on the media - */ - void mediaAdded(const struct pdraw_media_info info); - - /** - * Media removed signal, called when a media has been removed internally - * from the renderer. Medias are raw video medias. - * @param info: information on the media - */ - void mediaRemoved(const struct pdraw_media_info info); - - /** - * External texture loading signal. This signal is emitted before the - * rendering of the video frame in order to override the frame loading - * as a texture. This can be used to transform the video frames outside - * of PDrAW before resuming the rendering. This signal is emitted from - * the rendering thread. If no implementation of this function is - * required by the application, the retVal value must be set to -ENOSYS - * (before checking input values, so that implementation can be tested - * with all arguments null). - * @param textureWidth: texture width in pixels - * @param textureHeight: texture height in pixels - * @param sessionInfo: session information - * @param sessionMeta: session metadata - * @param frame: frame information - * @param frameUserdata: frame user data buffer - * @param frameUserdataLen: frame user data buffer size - * in bytes - * @param retVal: return value to be filled by the application - */ - void loadVideoTexture(unsigned int textureWidth, - unsigned int textureHeight, - const struct pdraw_media_info *mediaInfo, - struct mbuf_raw_video_frame *frame, - const void *frameUserdata, - unsigned int frameUserdataLen, - int *retVal); - - /** - * Overlay rendering signal. This signal is emitted after the rendering - * of the video frame in order to render an application overlay on top - * of the video. When HMD distorsion correction is enabled in the - * renderer, it is applied after the overlay rendering. This signal is - * emitted from the rendering thread. If no implementation of this - * function is required by the application, the retVal value must be set - * to -ENOSYS (before checking input values, so that implementation can - * be tested with all arguments null). - * @param renderPos: rendering position - * @param contentPos: video content position - * @param viewMat: 4x4 view matrix - * @param projMat: 4x4 projection matrix - * @param sessionInfo: session information - * @param sessionMeta: session metadata - * @param frameMeta: frame metadata - * @param frameExtra: frame extra information - * @param retVal: return value to be filled by the application - */ - void - renderVideoOverlay(const struct pdraw_rect *renderPos, - const struct pdraw_rect *contentPos, - const float *viewMat, - const float *projMat, - const struct pdraw_media_info *mediaInfo, - struct vmeta_frame *frameMeta, - const struct pdraw_video_frame_extra *frameExtra, - int *retVal); - -protected: - void initializeGL(); - - void resizeGL(int w, int h); - - void paintGL(); - -private: - Internal::QPdrawWidgetPriv *mPriv; -}; - -} /* namespace QPdraw */ - -#endif /* !_QPDRAW_WIDGET_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer + * Qt PDrAW widget + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _QPDRAW_WIDGET_HPP_ +#define _QPDRAW_WIDGET_HPP_ + +#include + +#include +#include + +#include + +#include +#include + +namespace QPdraw { + +/* Forward declarations */ +namespace Internal { +class QPdrawWidgetPriv; +} + + +class QPdrawWidget : public QOpenGLWidget { + Q_OBJECT + +public: + explicit QPdrawWidget(QWidget *parent = nullptr); + + ~QPdrawWidget(); + + /** + * Start a QPdraw widget. + * This function creates a QPdraw widget on a media of the given media + * id; if the media id is zero the first raw media encountered is used. + * This function must be called after the object creation prior to + * calling any other function. + * @param pdraw: information on the media + * @param mediaId: identifier of the raw media to render (from a + * pdraw_media_info structure); if zero the first + * raw media found is used for rendering + * @return 0 on success, negative errno value in case of error + */ + void start(QPdraw *pdraw, unsigned int mediaId); + + /** + * Start a QPdraw widget with rendering position and params. + * This function creates a QPdraw widget on a media of the given media + * id; if the media id is zero the first raw media encountered is used. + * The rendering position and rendering parameters are also provided. + * This function must be called after the object creation prior to + * calling any other function. + * @param pdraw: information on the media + * @param mediaId: identifier of the raw media to render (from a + * pdraw_media_info structure); if zero the first + * raw media found is used for rendering + * @param renderPos: rendering position and size + * @param params: renderer parameters + * @return 0 on success, negative errno value in case of error + */ + void start(QPdraw *pdraw, + unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params); + + /** + * Stop a QPdraw widget. + * This function must be called prior to destroying the instance. + * @return 0 on success, negative errno value in case of error + */ + void stop(); + + /** + * Get the widget rendering rectangle. + * @return the rendering rectangle + */ + QRect getVideoRect() const; + +signals: + + /** + * Media added signal, called when a media has been added internally to + * the renderer. Medias are raw video medias. + * @param info: information on the media + */ + void mediaAdded(const struct pdraw_media_info info); + + /** + * Media removed signal, called when a media has been removed internally + * from the renderer. Medias are raw video medias. + * @param info: information on the media + */ + void mediaRemoved(const struct pdraw_media_info info); + + /** + * External texture loading signal. This signal is emitted before the + * rendering of the video frame in order to override the frame loading + * as a texture. This can be used to transform the video frames outside + * of PDrAW before resuming the rendering. This signal is emitted from + * the rendering thread. If no implementation of this function is + * required by the application, the retVal value must be set to -ENOSYS + * (before checking input values, so that implementation can be tested + * with all arguments null). + * @param textureWidth: texture width in pixels + * @param textureHeight: texture height in pixels + * @param sessionInfo: session information + * @param sessionMeta: session metadata + * @param frame: frame information + * @param frameUserdata: frame user data buffer + * @param frameUserdataLen: frame user data buffer size + * in bytes + * @param retVal: return value to be filled by the application + */ + void loadVideoTexture(unsigned int textureWidth, + unsigned int textureHeight, + const struct pdraw_media_info *mediaInfo, + struct mbuf_raw_video_frame *frame, + const void *frameUserdata, + unsigned int frameUserdataLen, + int *retVal); + + /** + * Overlay rendering signal. This signal is emitted after the rendering + * of the video frame in order to render an application overlay on top + * of the video. When HMD distorsion correction is enabled in the + * renderer, it is applied after the overlay rendering. This signal is + * emitted from the rendering thread. If no implementation of this + * function is required by the application, the retVal value must be set + * to -ENOSYS (before checking input values, so that implementation can + * be tested with all arguments null). + * @param renderPos: rendering position + * @param contentPos: video content position + * @param viewMat: 4x4 view matrix + * @param projMat: 4x4 projection matrix + * @param sessionInfo: session information + * @param sessionMeta: session metadata + * @param frameMeta: frame metadata + * @param frameExtra: frame extra information + * @param retVal: return value to be filled by the application + */ + void + renderVideoOverlay(const struct pdraw_rect *renderPos, + const struct pdraw_rect *contentPos, + const float *viewMat, + const float *projMat, + const struct pdraw_media_info *mediaInfo, + struct vmeta_frame *frameMeta, + const struct pdraw_video_frame_extra *frameExtra, + int *retVal); + +protected: + void initializeGL(); + + void resizeGL(int w, int h); + + void paintGL(); + +private: + Internal::QPdrawWidgetPriv *mPriv; +}; + +} /* namespace QPdraw */ + +#endif /* !_QPDRAW_WIDGET_HPP_ */ diff --git a/qpdraw/qpdraw.pro b/qpdraw/qpdraw.pro index bed9ee4..77523f4 100644 --- a/qpdraw/qpdraw.pro +++ b/qpdraw/qpdraw.pro @@ -1,22 +1,22 @@ -TEMPLATE = lib - -TARGET = qpdraw - -SOURCES += \ - src/qpdraw.cpp \ - src/qpdraw_demuxer.cpp \ - src/qpdraw_widget.cpp - -HEADERS += \ - include/pdraw/qpdraw.hpp \ - include/pdraw/qpdraw_demuxer.hpp \ - include/pdraw/qpdraw_widget.hpp \ - src/qpdraw_priv.hpp \ - src/qpdraw_demuxer_priv.hpp \ - src/qpdraw_widget_priv.hpp - -exists($${OUT_PWD}/alchemy.pri) { - include($${OUT_PWD}/alchemy.pri) -} else: exists($${PWD}/alchemy.pri) { - include($${PWD}/alchemy.pri) -} +TEMPLATE = lib + +TARGET = qpdraw + +SOURCES += \ + src/qpdraw.cpp \ + src/qpdraw_demuxer.cpp \ + src/qpdraw_widget.cpp + +HEADERS += \ + include/pdraw/qpdraw.hpp \ + include/pdraw/qpdraw_demuxer.hpp \ + include/pdraw/qpdraw_widget.hpp \ + src/qpdraw_priv.hpp \ + src/qpdraw_demuxer_priv.hpp \ + src/qpdraw_widget_priv.hpp + +exists($${OUT_PWD}/alchemy.pri) { + include($${OUT_PWD}/alchemy.pri) +} else: exists($${PWD}/alchemy.pri) { + include($${PWD}/alchemy.pri) +} diff --git a/qpdraw/src/qpdraw.cpp b/qpdraw/src/qpdraw.cpp index 396f34c..2b75760 100644 --- a/qpdraw/src/qpdraw.cpp +++ b/qpdraw/src/qpdraw.cpp @@ -1,169 +1,169 @@ -/** - * Parrot Drones Awesome Video Viewer - * Qt PDrAW object - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "qpdraw_priv.hpp" - -#define ULOG_TAG qpdraw -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -namespace QPdraw { -namespace Internal { - - -QPdrawPriv::QPdrawPriv(QPdraw *parent) : mParent(parent), mPdraw(nullptr) -{ - int res; - - res = createPdrawBackend(this, &mPdraw); - if (res < 0) { - ULOG_ERRNO("createPdrawBackend", -res); - goto error; - } - - return; - -error: - if (mPdraw != nullptr) { - delete mPdraw; - mPdraw = nullptr; - } -} - - -QPdrawPriv::~QPdrawPriv() -{ - if (mPdraw != nullptr) { - delete mPdraw; - mPdraw = nullptr; - } -} - - -int QPdrawPriv::start(void) -{ - return mPdraw->start(); -} - - -int QPdrawPriv::stop(void) -{ - return mPdraw->stop(); -} - - -intptr_t QPdrawPriv::getInternal(void) -{ - return reinterpret_cast(mPdraw); -} - - -struct pomp_loop *QPdrawPriv::getLoop(void) -{ - return mPdraw->getLoop(); -} - - -void QPdrawPriv::stopResponse(IPdraw *pdraw, int status) -{ - Q_UNUSED(pdraw); - - emit mParent->stopResponse(status); -} - - -void QPdrawPriv::onMediaAdded(IPdraw *pdraw, - const struct pdraw_media_info *info) -{ - Q_UNUSED(pdraw); - - struct pdraw_media_info info_copy = *info; - emit mParent->onMediaAdded(info_copy); -} - - -void QPdrawPriv::onMediaRemoved(IPdraw *pdraw, - const struct pdraw_media_info *info) -{ - Q_UNUSED(pdraw); - - struct pdraw_media_info info_copy = *info; - emit mParent->onMediaRemoved(info_copy); -} - - -void QPdrawPriv::onSocketCreated(IPdraw *pdraw, int fd) -{ - Q_UNUSED(pdraw); - - emit mParent->onSocketCreated(fd); -} - -} /* namespace Internal */ - - -QPdraw::QPdraw(QObject *parent) : QObject(parent) -{ - qRegisterMetaType("pdraw_media_info"); - - mPriv = new Internal::QPdrawPriv(this); -} - - -QPdraw::~QPdraw() -{ - delete mPriv; -} - - -int QPdraw::start(void) -{ - return mPriv->start(); -} - - -int QPdraw::stop(void) -{ - return mPriv->stop(); -} - - -intptr_t QPdraw::getInternal(void) -{ - return mPriv->getInternal(); -} - - -struct pomp_loop *QPdraw::getLoop(void) -{ - return mPriv->getLoop(); -} - -} /* namespace QPdraw */ +/** + * Parrot Drones Awesome Video Viewer + * Qt PDrAW object + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 "qpdraw_priv.hpp" + +#define ULOG_TAG qpdraw +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +namespace QPdraw { +namespace Internal { + + +QPdrawPriv::QPdrawPriv(QPdraw *parent) : mParent(parent), mPdraw(nullptr) +{ + int res; + + res = createPdrawBackend(this, &mPdraw); + if (res < 0) { + ULOG_ERRNO("createPdrawBackend", -res); + goto error; + } + + return; + +error: + if (mPdraw != nullptr) { + delete mPdraw; + mPdraw = nullptr; + } +} + + +QPdrawPriv::~QPdrawPriv() +{ + if (mPdraw != nullptr) { + delete mPdraw; + mPdraw = nullptr; + } +} + + +int QPdrawPriv::start(void) +{ + return mPdraw->start(); +} + + +int QPdrawPriv::stop(void) +{ + return mPdraw->stop(); +} + + +intptr_t QPdrawPriv::getInternal(void) +{ + return reinterpret_cast(mPdraw); +} + + +struct pomp_loop *QPdrawPriv::getLoop(void) +{ + return mPdraw->getLoop(); +} + + +void QPdrawPriv::stopResponse(IPdraw *pdraw, int status) +{ + Q_UNUSED(pdraw); + + emit mParent->stopResponse(status); +} + + +void QPdrawPriv::onMediaAdded(IPdraw *pdraw, + const struct pdraw_media_info *info) +{ + Q_UNUSED(pdraw); + + struct pdraw_media_info info_copy = *info; + emit mParent->onMediaAdded(info_copy); +} + + +void QPdrawPriv::onMediaRemoved(IPdraw *pdraw, + const struct pdraw_media_info *info) +{ + Q_UNUSED(pdraw); + + struct pdraw_media_info info_copy = *info; + emit mParent->onMediaRemoved(info_copy); +} + + +void QPdrawPriv::onSocketCreated(IPdraw *pdraw, int fd) +{ + Q_UNUSED(pdraw); + + emit mParent->onSocketCreated(fd); +} + +} /* namespace Internal */ + + +QPdraw::QPdraw(QObject *parent) : QObject(parent) +{ + qRegisterMetaType("pdraw_media_info"); + + mPriv = new Internal::QPdrawPriv(this); +} + + +QPdraw::~QPdraw() +{ + delete mPriv; +} + + +int QPdraw::start(void) +{ + return mPriv->start(); +} + + +int QPdraw::stop(void) +{ + return mPriv->stop(); +} + + +intptr_t QPdraw::getInternal(void) +{ + return mPriv->getInternal(); +} + + +struct pomp_loop *QPdraw::getLoop(void) +{ + return mPriv->getLoop(); +} + +} /* namespace QPdraw */ diff --git a/qpdraw/src/qpdraw_demuxer.cpp b/qpdraw/src/qpdraw_demuxer.cpp index d7a492c..309d580 100644 --- a/qpdraw/src/qpdraw_demuxer.cpp +++ b/qpdraw/src/qpdraw_demuxer.cpp @@ -1,492 +1,492 @@ -/** - * Parrot Drones Awesome Video Viewer - * Qt PDrAW demuxer object - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "qpdraw_demuxer_priv.hpp" - -#define ULOG_TAG qpdraw_demuxer -#include -ULOG_DECLARE_TAG(ULOG_TAG); - - -Q_DECLARE_METATYPE(struct pdraw_demuxer_media); - - -namespace QPdraw { -namespace Internal { - - -QPdrawDemuxerPriv::QPdrawDemuxerPriv(QPdrawDemuxer *parent) : - mParent(parent), mDemuxer(nullptr), mClosing(false) -{ -} - - -QPdrawDemuxerPriv::~QPdrawDemuxerPriv() -{ - if (mDemuxer != nullptr) - delete mDemuxer; -} - - -IPdraw *QPdrawDemuxerPriv::getPdrawInternal() -{ - ULOG_ERRNO_RETURN_VAL_IF(mParent == nullptr, EPROTO, nullptr); - ULOG_ERRNO_RETURN_VAL_IF(mParent->parent() == nullptr, EPROTO, nullptr); - QPdraw *qpdraw = reinterpret_cast(mParent->parent()); - ULOG_ERRNO_RETURN_VAL_IF(qpdraw == nullptr, EPROTO, nullptr); - return reinterpret_cast(qpdraw->getInternal()); -} - - -int QPdrawDemuxerPriv::open(const std::string &url) -{ - ULOG_ERRNO_RETURN_ERR_IF(mDemuxer != nullptr, EBUSY); - - IPdraw *pdrawInternal = getPdrawInternal(); - ULOG_ERRNO_RETURN_ERR_IF(pdrawInternal == nullptr, EPROTO); - - return pdrawInternal->createDemuxer(url, this, &mDemuxer); -} - - -int QPdrawDemuxerPriv::open(const std::string &localAddr, - uint16_t localStreamPort, - uint16_t localControlPort, - const std::string &remoteAddr, - uint16_t remoteStreamPort, - uint16_t remoteControlPort) -{ - ULOG_ERRNO_RETURN_ERR_IF(mDemuxer != nullptr, EBUSY); - - IPdraw *pdrawInternal = getPdrawInternal(); - ULOG_ERRNO_RETURN_ERR_IF(pdrawInternal == nullptr, EPROTO); - - return pdrawInternal->createDemuxer(localAddr, - localStreamPort, - localControlPort, - remoteAddr, - remoteStreamPort, - remoteControlPort, - this, - &mDemuxer); -} - - -int QPdrawDemuxerPriv::open(const std::string &url, struct mux_ctx *mux) -{ - ULOG_ERRNO_RETURN_ERR_IF(mDemuxer != nullptr, EBUSY); - - IPdraw *pdrawInternal = getPdrawInternal(); - ULOG_ERRNO_RETURN_ERR_IF(pdrawInternal == nullptr, EPROTO); - - return pdrawInternal->createDemuxer(url, mux, this, &mDemuxer); -} - - -int QPdrawDemuxerPriv::close(void) -{ - ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EINVAL); - - int res = mDemuxer->close(); - if (res == 0) - mClosing = true; - return res; -} - - -uint16_t QPdrawDemuxerPriv::getSingleStreamLocalStreamPort(void) -{ - ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EINVAL, 0); - ULOG_ERRNO_RETURN_VAL_IF(mClosing, EPERM, 0); - - return mDemuxer->getSingleStreamLocalStreamPort(); -} - - -uint16_t QPdrawDemuxerPriv::getSingleStreamLocalControlPort(void) -{ - ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EINVAL, 0); - ULOG_ERRNO_RETURN_VAL_IF(mClosing, EPERM, 0); - - return mDemuxer->getSingleStreamLocalControlPort(); -} - - -bool QPdrawDemuxerPriv::isReadyToPlay(void) -{ - ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EINVAL, false); - ULOG_ERRNO_RETURN_VAL_IF(mClosing, EPERM, false); - - return mDemuxer->isReadyToPlay(); -} - - -bool QPdrawDemuxerPriv::isPaused(void) -{ - ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EINVAL, false); - ULOG_ERRNO_RETURN_VAL_IF(mClosing, EPERM, false); - - return mDemuxer->isPaused(); -} - - -int QPdrawDemuxerPriv::play(float speed) -{ - ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(mClosing, EPERM); - - return mDemuxer->play(speed); -} - - -int QPdrawDemuxerPriv::pause(void) -{ - ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(mClosing, EPERM); - - return mDemuxer->pause(); -} - - -int QPdrawDemuxerPriv::previousFrame(void) -{ - ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(mClosing, EPERM); - - return mDemuxer->previousFrame(); -} - - -int QPdrawDemuxerPriv::nextFrame(void) -{ - ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(mClosing, EPERM); - - return mDemuxer->nextFrame(); -} - - -int QPdrawDemuxerPriv::seek(int64_t delta, bool exact) -{ - ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(mClosing, EPERM); - - return mDemuxer->seek(delta, exact); -} - - -int QPdrawDemuxerPriv::seekForward(uint64_t delta, bool exact) -{ - ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(mClosing, EPERM); - - return mDemuxer->seekForward(delta, exact); -} - - -int QPdrawDemuxerPriv::seekBack(uint64_t delta, bool exact) -{ - ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(mClosing, EPERM); - - return mDemuxer->seekBack(delta, exact); -} - - -int QPdrawDemuxerPriv::seekTo(uint64_t timestamp, bool exact) -{ - ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EINVAL); - ULOG_ERRNO_RETURN_ERR_IF(mClosing, EPERM); - - return mDemuxer->seekTo(timestamp, exact); -} - - -uint64_t QPdrawDemuxerPriv::getDuration(void) -{ - ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EINVAL, 0); - ULOG_ERRNO_RETURN_VAL_IF(mClosing, EPERM, 0); - - return mDemuxer->getDuration(); -} - - -uint64_t QPdrawDemuxerPriv::getCurrentTime(void) -{ - ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EINVAL, 0); - ULOG_ERRNO_RETURN_VAL_IF(mClosing, EPERM, 0); - - return mDemuxer->getCurrentTime(); -} - - -void QPdrawDemuxerPriv::demuxerOpenResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status) -{ - Q_UNUSED(pdraw); - Q_UNUSED(demuxer); - - emit mParent->openResponse(status); -} - - -void QPdrawDemuxerPriv::demuxerCloseResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status) -{ - Q_UNUSED(pdraw); - Q_UNUSED(demuxer); - - emit mParent->closeResponse(status); -} - - -void QPdrawDemuxerPriv::onDemuxerUnrecoverableError(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer) -{ - Q_UNUSED(pdraw); - Q_UNUSED(demuxer); - - emit mParent->onUnrecoverableError(); -} - - -int QPdrawDemuxerPriv::demuxerSelectMedia( - IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - const struct pdraw_demuxer_media *medias, - size_t count) -{ - int ret = -ENOSYS; - - Q_UNUSED(pdraw); - Q_UNUSED(demuxer); - - emit mParent->selectMedia(medias, (unsigned int)count, &ret); - - return ret; -} - - -void QPdrawDemuxerPriv::demuxerReadyToPlay(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - bool ready) -{ - Q_UNUSED(pdraw); - Q_UNUSED(demuxer); - - emit mParent->readyToPlay(ready); -} - - -void QPdrawDemuxerPriv::onDemuxerEndOfRange(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - uint64_t timestamp) -{ - Q_UNUSED(pdraw); - Q_UNUSED(demuxer); - - emit mParent->onEndOfRange(timestamp); -} - - -void QPdrawDemuxerPriv::demuxerPlayResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp, - float speed) -{ - Q_UNUSED(pdraw); - Q_UNUSED(demuxer); - - emit mParent->playResponse(status, timestamp, speed); -} - - -void QPdrawDemuxerPriv::demuxerPauseResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp) -{ - Q_UNUSED(pdraw); - Q_UNUSED(demuxer); - - emit mParent->pauseResponse(status, timestamp); -} - - -void QPdrawDemuxerPriv::demuxerSeekResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp, - float speed) -{ - Q_UNUSED(pdraw); - Q_UNUSED(demuxer); - - emit mParent->seekResponse(status, timestamp, speed); -} - -} /* namespace Internal */ - - -QPdrawDemuxer::QPdrawDemuxer(QPdraw *parent) : QObject(parent) -{ - qRegisterMetaType("pdraw_demuxer_media"); - - mPriv = new Internal::QPdrawDemuxerPriv(this); -} - - -QPdrawDemuxer::~QPdrawDemuxer() -{ - delete mPriv; -} - - -int QPdrawDemuxer::open(const std::string &url) -{ - return mPriv->open(url); -} - - -int QPdrawDemuxer::open(const std::string &localAddr, - uint16_t localStreamPort, - uint16_t localControlPort, - const std::string &remoteAddr, - uint16_t remoteStreamPort, - uint16_t remoteControlPort) -{ - return mPriv->open(localAddr, - localStreamPort, - localControlPort, - remoteAddr, - remoteStreamPort, - remoteControlPort); -} - - -int QPdrawDemuxer::open(const std::string &url, struct mux_ctx *mux) -{ - return mPriv->open(url, mux); -} - - -int QPdrawDemuxer::close(void) -{ - return mPriv->close(); -} - - -uint16_t QPdrawDemuxer::getSingleStreamLocalStreamPort(void) -{ - return mPriv->getSingleStreamLocalStreamPort(); -} - - -uint16_t QPdrawDemuxer::getSingleStreamLocalControlPort(void) -{ - return mPriv->getSingleStreamLocalControlPort(); -} - - -bool QPdrawDemuxer::isReadyToPlay(void) -{ - return mPriv->isReadyToPlay(); -} - - -bool QPdrawDemuxer::isPaused(void) -{ - return mPriv->isPaused(); -} - - -int QPdrawDemuxer::play(float speed) -{ - return mPriv->play(speed); -} - - -int QPdrawDemuxer::pause(void) -{ - return mPriv->pause(); -} - - -int QPdrawDemuxer::previousFrame(void) -{ - return mPriv->previousFrame(); -} - - -int QPdrawDemuxer::nextFrame(void) -{ - return mPriv->nextFrame(); -} - - -int QPdrawDemuxer::seek(int64_t delta, bool exact) -{ - return mPriv->seek(delta, exact); -} - - -int QPdrawDemuxer::seekForward(uint64_t delta, bool exact) -{ - return mPriv->seekForward(delta, exact); -} - - -int QPdrawDemuxer::seekBack(uint64_t delta, bool exact) -{ - return mPriv->seekBack(delta, exact); -} - - -int QPdrawDemuxer::seekTo(uint64_t timestamp, bool exact) -{ - return mPriv->seekTo(timestamp, exact); -} - - -uint64_t QPdrawDemuxer::getDuration(void) -{ - return mPriv->getDuration(); -} - - -uint64_t QPdrawDemuxer::getCurrentTime(void) -{ - return mPriv->getCurrentTime(); -} - -} /* namespace QPdraw */ +/** + * Parrot Drones Awesome Video Viewer + * Qt PDrAW demuxer object + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 "qpdraw_demuxer_priv.hpp" + +#define ULOG_TAG qpdraw_demuxer +#include +ULOG_DECLARE_TAG(ULOG_TAG); + + +Q_DECLARE_METATYPE(struct pdraw_demuxer_media); + + +namespace QPdraw { +namespace Internal { + + +QPdrawDemuxerPriv::QPdrawDemuxerPriv(QPdrawDemuxer *parent) : + mParent(parent), mDemuxer(nullptr), mClosing(false) +{ +} + + +QPdrawDemuxerPriv::~QPdrawDemuxerPriv() +{ + if (mDemuxer != nullptr) + delete mDemuxer; +} + + +IPdraw *QPdrawDemuxerPriv::getPdrawInternal() +{ + ULOG_ERRNO_RETURN_VAL_IF(mParent == nullptr, EPROTO, nullptr); + ULOG_ERRNO_RETURN_VAL_IF(mParent->parent() == nullptr, EPROTO, nullptr); + QPdraw *qpdraw = reinterpret_cast(mParent->parent()); + ULOG_ERRNO_RETURN_VAL_IF(qpdraw == nullptr, EPROTO, nullptr); + return reinterpret_cast(qpdraw->getInternal()); +} + + +int QPdrawDemuxerPriv::open(const std::string &url) +{ + ULOG_ERRNO_RETURN_ERR_IF(mDemuxer != nullptr, EBUSY); + + IPdraw *pdrawInternal = getPdrawInternal(); + ULOG_ERRNO_RETURN_ERR_IF(pdrawInternal == nullptr, EPROTO); + + return pdrawInternal->createDemuxer(url, this, &mDemuxer); +} + + +int QPdrawDemuxerPriv::open(const std::string &localAddr, + uint16_t localStreamPort, + uint16_t localControlPort, + const std::string &remoteAddr, + uint16_t remoteStreamPort, + uint16_t remoteControlPort) +{ + ULOG_ERRNO_RETURN_ERR_IF(mDemuxer != nullptr, EBUSY); + + IPdraw *pdrawInternal = getPdrawInternal(); + ULOG_ERRNO_RETURN_ERR_IF(pdrawInternal == nullptr, EPROTO); + + return pdrawInternal->createDemuxer(localAddr, + localStreamPort, + localControlPort, + remoteAddr, + remoteStreamPort, + remoteControlPort, + this, + &mDemuxer); +} + + +int QPdrawDemuxerPriv::open(const std::string &url, struct mux_ctx *mux) +{ + ULOG_ERRNO_RETURN_ERR_IF(mDemuxer != nullptr, EBUSY); + + IPdraw *pdrawInternal = getPdrawInternal(); + ULOG_ERRNO_RETURN_ERR_IF(pdrawInternal == nullptr, EPROTO); + + return pdrawInternal->createDemuxer(url, mux, this, &mDemuxer); +} + + +int QPdrawDemuxerPriv::close(void) +{ + ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EINVAL); + + int res = mDemuxer->close(); + if (res == 0) + mClosing = true; + return res; +} + + +uint16_t QPdrawDemuxerPriv::getSingleStreamLocalStreamPort(void) +{ + ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EINVAL, 0); + ULOG_ERRNO_RETURN_VAL_IF(mClosing, EPERM, 0); + + return mDemuxer->getSingleStreamLocalStreamPort(); +} + + +uint16_t QPdrawDemuxerPriv::getSingleStreamLocalControlPort(void) +{ + ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EINVAL, 0); + ULOG_ERRNO_RETURN_VAL_IF(mClosing, EPERM, 0); + + return mDemuxer->getSingleStreamLocalControlPort(); +} + + +bool QPdrawDemuxerPriv::isReadyToPlay(void) +{ + ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EINVAL, false); + ULOG_ERRNO_RETURN_VAL_IF(mClosing, EPERM, false); + + return mDemuxer->isReadyToPlay(); +} + + +bool QPdrawDemuxerPriv::isPaused(void) +{ + ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EINVAL, false); + ULOG_ERRNO_RETURN_VAL_IF(mClosing, EPERM, false); + + return mDemuxer->isPaused(); +} + + +int QPdrawDemuxerPriv::play(float speed) +{ + ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(mClosing, EPERM); + + return mDemuxer->play(speed); +} + + +int QPdrawDemuxerPriv::pause(void) +{ + ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(mClosing, EPERM); + + return mDemuxer->pause(); +} + + +int QPdrawDemuxerPriv::previousFrame(void) +{ + ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(mClosing, EPERM); + + return mDemuxer->previousFrame(); +} + + +int QPdrawDemuxerPriv::nextFrame(void) +{ + ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(mClosing, EPERM); + + return mDemuxer->nextFrame(); +} + + +int QPdrawDemuxerPriv::seek(int64_t delta, bool exact) +{ + ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(mClosing, EPERM); + + return mDemuxer->seek(delta, exact); +} + + +int QPdrawDemuxerPriv::seekForward(uint64_t delta, bool exact) +{ + ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(mClosing, EPERM); + + return mDemuxer->seekForward(delta, exact); +} + + +int QPdrawDemuxerPriv::seekBack(uint64_t delta, bool exact) +{ + ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(mClosing, EPERM); + + return mDemuxer->seekBack(delta, exact); +} + + +int QPdrawDemuxerPriv::seekTo(uint64_t timestamp, bool exact) +{ + ULOG_ERRNO_RETURN_ERR_IF(mDemuxer == nullptr, EINVAL); + ULOG_ERRNO_RETURN_ERR_IF(mClosing, EPERM); + + return mDemuxer->seekTo(timestamp, exact); +} + + +uint64_t QPdrawDemuxerPriv::getDuration(void) +{ + ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EINVAL, 0); + ULOG_ERRNO_RETURN_VAL_IF(mClosing, EPERM, 0); + + return mDemuxer->getDuration(); +} + + +uint64_t QPdrawDemuxerPriv::getCurrentTime(void) +{ + ULOG_ERRNO_RETURN_VAL_IF(mDemuxer == nullptr, EINVAL, 0); + ULOG_ERRNO_RETURN_VAL_IF(mClosing, EPERM, 0); + + return mDemuxer->getCurrentTime(); +} + + +void QPdrawDemuxerPriv::demuxerOpenResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status) +{ + Q_UNUSED(pdraw); + Q_UNUSED(demuxer); + + emit mParent->openResponse(status); +} + + +void QPdrawDemuxerPriv::demuxerCloseResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status) +{ + Q_UNUSED(pdraw); + Q_UNUSED(demuxer); + + emit mParent->closeResponse(status); +} + + +void QPdrawDemuxerPriv::onDemuxerUnrecoverableError(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer) +{ + Q_UNUSED(pdraw); + Q_UNUSED(demuxer); + + emit mParent->onUnrecoverableError(); +} + + +int QPdrawDemuxerPriv::demuxerSelectMedia( + IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + const struct pdraw_demuxer_media *medias, + size_t count) +{ + int ret = -ENOSYS; + + Q_UNUSED(pdraw); + Q_UNUSED(demuxer); + + emit mParent->selectMedia(medias, (unsigned int)count, &ret); + + return ret; +} + + +void QPdrawDemuxerPriv::demuxerReadyToPlay(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + bool ready) +{ + Q_UNUSED(pdraw); + Q_UNUSED(demuxer); + + emit mParent->readyToPlay(ready); +} + + +void QPdrawDemuxerPriv::onDemuxerEndOfRange(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + uint64_t timestamp) +{ + Q_UNUSED(pdraw); + Q_UNUSED(demuxer); + + emit mParent->onEndOfRange(timestamp); +} + + +void QPdrawDemuxerPriv::demuxerPlayResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp, + float speed) +{ + Q_UNUSED(pdraw); + Q_UNUSED(demuxer); + + emit mParent->playResponse(status, timestamp, speed); +} + + +void QPdrawDemuxerPriv::demuxerPauseResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp) +{ + Q_UNUSED(pdraw); + Q_UNUSED(demuxer); + + emit mParent->pauseResponse(status, timestamp); +} + + +void QPdrawDemuxerPriv::demuxerSeekResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp, + float speed) +{ + Q_UNUSED(pdraw); + Q_UNUSED(demuxer); + + emit mParent->seekResponse(status, timestamp, speed); +} + +} /* namespace Internal */ + + +QPdrawDemuxer::QPdrawDemuxer(QPdraw *parent) : QObject(parent) +{ + qRegisterMetaType("pdraw_demuxer_media"); + + mPriv = new Internal::QPdrawDemuxerPriv(this); +} + + +QPdrawDemuxer::~QPdrawDemuxer() +{ + delete mPriv; +} + + +int QPdrawDemuxer::open(const std::string &url) +{ + return mPriv->open(url); +} + + +int QPdrawDemuxer::open(const std::string &localAddr, + uint16_t localStreamPort, + uint16_t localControlPort, + const std::string &remoteAddr, + uint16_t remoteStreamPort, + uint16_t remoteControlPort) +{ + return mPriv->open(localAddr, + localStreamPort, + localControlPort, + remoteAddr, + remoteStreamPort, + remoteControlPort); +} + + +int QPdrawDemuxer::open(const std::string &url, struct mux_ctx *mux) +{ + return mPriv->open(url, mux); +} + + +int QPdrawDemuxer::close(void) +{ + return mPriv->close(); +} + + +uint16_t QPdrawDemuxer::getSingleStreamLocalStreamPort(void) +{ + return mPriv->getSingleStreamLocalStreamPort(); +} + + +uint16_t QPdrawDemuxer::getSingleStreamLocalControlPort(void) +{ + return mPriv->getSingleStreamLocalControlPort(); +} + + +bool QPdrawDemuxer::isReadyToPlay(void) +{ + return mPriv->isReadyToPlay(); +} + + +bool QPdrawDemuxer::isPaused(void) +{ + return mPriv->isPaused(); +} + + +int QPdrawDemuxer::play(float speed) +{ + return mPriv->play(speed); +} + + +int QPdrawDemuxer::pause(void) +{ + return mPriv->pause(); +} + + +int QPdrawDemuxer::previousFrame(void) +{ + return mPriv->previousFrame(); +} + + +int QPdrawDemuxer::nextFrame(void) +{ + return mPriv->nextFrame(); +} + + +int QPdrawDemuxer::seek(int64_t delta, bool exact) +{ + return mPriv->seek(delta, exact); +} + + +int QPdrawDemuxer::seekForward(uint64_t delta, bool exact) +{ + return mPriv->seekForward(delta, exact); +} + + +int QPdrawDemuxer::seekBack(uint64_t delta, bool exact) +{ + return mPriv->seekBack(delta, exact); +} + + +int QPdrawDemuxer::seekTo(uint64_t timestamp, bool exact) +{ + return mPriv->seekTo(timestamp, exact); +} + + +uint64_t QPdrawDemuxer::getDuration(void) +{ + return mPriv->getDuration(); +} + + +uint64_t QPdrawDemuxer::getCurrentTime(void) +{ + return mPriv->getCurrentTime(); +} + +} /* namespace QPdraw */ diff --git a/qpdraw/src/qpdraw_demuxer_priv.hpp b/qpdraw/src/qpdraw_demuxer_priv.hpp index bd9c702..8e90a3c 100644 --- a/qpdraw/src/qpdraw_demuxer_priv.hpp +++ b/qpdraw/src/qpdraw_demuxer_priv.hpp @@ -1,142 +1,142 @@ -/** - * Parrot Drones Awesome Video Viewer - * Qt PDrAW demuxer object - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _QPDRAW_DEMUXER_PRIV_HPP_ -#define _QPDRAW_DEMUXER_PRIV_HPP_ - -#include -#include - -using namespace Pdraw; - -namespace QPdraw { -namespace Internal { - - -class QPdrawDemuxerPriv : public IPdraw::IDemuxer::Listener { - -public: - explicit QPdrawDemuxerPriv(QPdrawDemuxer *parent); - ~QPdrawDemuxerPriv(); - - int open(const std::string &url); - - int open(const std::string &localAddr, - uint16_t localStreamPort, - uint16_t localControlPort, - const std::string &remoteAddr, - uint16_t remoteStreamPort, - uint16_t remoteControlPort); - - int open(const std::string &url, struct mux_ctx *mux); - - int close(void); - - uint16_t getSingleStreamLocalStreamPort(void); - - uint16_t getSingleStreamLocalControlPort(void); - - bool isReadyToPlay(void); - - bool isPaused(void); - - int play(float speed = 1.0f); - - int pause(void); - - int previousFrame(void); - - int nextFrame(void); - - int seek(int64_t delta, bool exact = false); - - int seekForward(uint64_t delta, bool exact = false); - - int seekBack(uint64_t delta, bool exact = false); - - int seekTo(uint64_t timestamp, bool exact = false); - - uint64_t getDuration(void); - - uint64_t getCurrentTime(void); - -private: - IPdraw *getPdrawInternal(); - - void demuxerOpenResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status); - - void demuxerCloseResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status); - - void onDemuxerUnrecoverableError(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer); - - int demuxerSelectMedia(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - const struct pdraw_demuxer_media *medias, - size_t count); - - void demuxerReadyToPlay(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - bool ready); - - void onDemuxerEndOfRange(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - uint64_t timestamp); - - void demuxerPlayResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp, - float speed); - - void demuxerPauseResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp); - - void demuxerSeekResponse(IPdraw *pdraw, - IPdraw::IDemuxer *demuxer, - int status, - uint64_t timestamp, - float speed); - - QPdrawDemuxer *mParent; - IPdraw::IDemuxer *mDemuxer; - bool mClosing; -}; - -} /* namespace Internal */ -} /* namespace QPdraw */ - -#endif /* !_QPDRAW_DEMUXER_PRIV_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer + * Qt PDrAW demuxer object + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _QPDRAW_DEMUXER_PRIV_HPP_ +#define _QPDRAW_DEMUXER_PRIV_HPP_ + +#include +#include + +using namespace Pdraw; + +namespace QPdraw { +namespace Internal { + + +class QPdrawDemuxerPriv : public IPdraw::IDemuxer::Listener { + +public: + explicit QPdrawDemuxerPriv(QPdrawDemuxer *parent); + ~QPdrawDemuxerPriv(); + + int open(const std::string &url); + + int open(const std::string &localAddr, + uint16_t localStreamPort, + uint16_t localControlPort, + const std::string &remoteAddr, + uint16_t remoteStreamPort, + uint16_t remoteControlPort); + + int open(const std::string &url, struct mux_ctx *mux); + + int close(void); + + uint16_t getSingleStreamLocalStreamPort(void); + + uint16_t getSingleStreamLocalControlPort(void); + + bool isReadyToPlay(void); + + bool isPaused(void); + + int play(float speed = 1.0f); + + int pause(void); + + int previousFrame(void); + + int nextFrame(void); + + int seek(int64_t delta, bool exact = false); + + int seekForward(uint64_t delta, bool exact = false); + + int seekBack(uint64_t delta, bool exact = false); + + int seekTo(uint64_t timestamp, bool exact = false); + + uint64_t getDuration(void); + + uint64_t getCurrentTime(void); + +private: + IPdraw *getPdrawInternal(); + + void demuxerOpenResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status); + + void demuxerCloseResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status); + + void onDemuxerUnrecoverableError(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer); + + int demuxerSelectMedia(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + const struct pdraw_demuxer_media *medias, + size_t count); + + void demuxerReadyToPlay(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + bool ready); + + void onDemuxerEndOfRange(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + uint64_t timestamp); + + void demuxerPlayResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp, + float speed); + + void demuxerPauseResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp); + + void demuxerSeekResponse(IPdraw *pdraw, + IPdraw::IDemuxer *demuxer, + int status, + uint64_t timestamp, + float speed); + + QPdrawDemuxer *mParent; + IPdraw::IDemuxer *mDemuxer; + bool mClosing; +}; + +} /* namespace Internal */ +} /* namespace QPdraw */ + +#endif /* !_QPDRAW_DEMUXER_PRIV_HPP_ */ diff --git a/qpdraw/src/qpdraw_priv.hpp b/qpdraw/src/qpdraw_priv.hpp index f5d8e8e..71423ed 100644 --- a/qpdraw/src/qpdraw_priv.hpp +++ b/qpdraw/src/qpdraw_priv.hpp @@ -1,74 +1,74 @@ -/** - * Parrot Drones Awesome Video Viewer - * Qt PDrAW object - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _QPDRAW_PRIV_HPP_ -#define _QPDRAW_PRIV_HPP_ - -#include -#include - -using namespace Pdraw; -using namespace PdrawBackend; - -namespace QPdraw { -namespace Internal { - - -class QPdrawPriv : public IPdraw::Listener { - -public: - explicit QPdrawPriv(QPdraw *parent); - ~QPdrawPriv(); - - int start(void); - - int stop(void); - - intptr_t getInternal(void); - - struct pomp_loop *getLoop(void); - -private: - void stopResponse(IPdraw *pdraw, int status); - - void onMediaAdded(IPdraw *pdraw, const struct pdraw_media_info *info); - - void onMediaRemoved(IPdraw *pdraw, const struct pdraw_media_info *info); - - void onSocketCreated(IPdraw *pdraw, int fd); - - QPdraw *mParent; - IPdrawBackend *mPdraw; -}; - -} /* namespace Internal */ -} /* namespace QPdraw */ - -#endif /* !_QPDRAW_PRIV_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer + * Qt PDrAW object + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _QPDRAW_PRIV_HPP_ +#define _QPDRAW_PRIV_HPP_ + +#include +#include + +using namespace Pdraw; +using namespace PdrawBackend; + +namespace QPdraw { +namespace Internal { + + +class QPdrawPriv : public IPdraw::Listener { + +public: + explicit QPdrawPriv(QPdraw *parent); + ~QPdrawPriv(); + + int start(void); + + int stop(void); + + intptr_t getInternal(void); + + struct pomp_loop *getLoop(void); + +private: + void stopResponse(IPdraw *pdraw, int status); + + void onMediaAdded(IPdraw *pdraw, const struct pdraw_media_info *info); + + void onMediaRemoved(IPdraw *pdraw, const struct pdraw_media_info *info); + + void onSocketCreated(IPdraw *pdraw, int fd); + + QPdraw *mParent; + IPdrawBackend *mPdraw; +}; + +} /* namespace Internal */ +} /* namespace QPdraw */ + +#endif /* !_QPDRAW_PRIV_HPP_ */ diff --git a/qpdraw/src/qpdraw_widget.cpp b/qpdraw/src/qpdraw_widget.cpp index 74f8b3a..92c1364 100644 --- a/qpdraw/src/qpdraw_widget.cpp +++ b/qpdraw/src/qpdraw_widget.cpp @@ -1,339 +1,339 @@ -/** - * Parrot Drones Awesome Video Viewer - * Qt PDrAW widget - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "qpdraw_widget_priv.hpp" - -#define ULOG_TAG qpdraw_widget -#include -ULOG_DECLARE_TAG(ULOG_TAG); - -namespace QPdraw { -namespace Internal { - - -QPdrawWidgetPriv::QPdrawWidgetPriv(QPdrawWidget *parent) : - mParent(parent), mPdraw(nullptr), mRenderer(nullptr) -{ - connect(this, - &QPdrawWidgetPriv::onRenderReady, - this, - &QPdrawWidgetPriv::renderReady, - Qt::QueuedConnection); -} - - -QPdrawWidgetPriv::~QPdrawWidgetPriv() -{ - stop(); -} - - -void QPdrawWidgetPriv::start(QPdraw *pdraw, - unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params) -{ - ULOG_ERRNO_RETURN_IF(mPdraw != nullptr, EBUSY); - - IPdraw *pdrawInternal = - reinterpret_cast(pdraw->getInternal()); - ULOG_ERRNO_RETURN_IF(pdrawInternal == nullptr, EPROTO); - - int res = pdrawInternal->createVideoRenderer( - mediaId, renderPos, params, this, &mRenderer); - if (res == 0) - mPdraw = pdraw; -} - - -void QPdrawWidgetPriv::stop() -{ - if (mPdraw == nullptr) - return; - - delete mRenderer; - mRenderer = nullptr; - mPdraw = nullptr; -} - - -bool QPdrawWidgetPriv::resizeGL(const struct pdraw_rect *renderPos) -{ - if (mPdraw == nullptr) - return false; - - ULOG_ERRNO_RETURN_VAL_IF(mRenderer == nullptr, EINVAL, false); - - int res = mRenderer->resize(renderPos); - if (res < 0) { - ULOG_ERRNO("IVideoRenderer::resize", -res); - return false; - } - - return true; -} - - -bool QPdrawWidgetPriv::paintGL() -{ - if (mPdraw == nullptr) - return false; - - ULOG_ERRNO_RETURN_VAL_IF(mRenderer == nullptr, EINVAL, false); - - int res = mRenderer->render(nullptr); - if (res < 0) { - ULOG_ERRNO("IVideoRenderer::render", -res); - return false; - } - - return true; -} - - -void QPdrawWidgetPriv::onVideoRendererMediaAdded( - IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer, - const struct pdraw_media_info *info) -{ - Q_UNUSED(pdraw); - Q_UNUSED(renderer); - - struct pdraw_media_info info_copy = *info; - emit mParent->mediaAdded(info_copy); -} - - -void QPdrawWidgetPriv::onVideoRendererMediaRemoved( - IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer, - const struct pdraw_media_info *info) -{ - Q_UNUSED(pdraw); - Q_UNUSED(renderer); - - struct pdraw_media_info info_copy = *info; - emit mParent->mediaRemoved(info_copy); -} - - -void QPdrawWidgetPriv::onVideoRenderReady(IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer) -{ - Q_UNUSED(pdraw); - Q_UNUSED(renderer); - - emit onRenderReady(); -} - - -int QPdrawWidgetPriv::loadVideoTexture(IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer, - unsigned int textureWidth, - unsigned int textureHeight, - const struct pdraw_media_info *mediaInfo, - struct mbuf_raw_video_frame *frame, - const void *frameUserdata, - size_t frameUserdataLen) -{ - int ret = -ENOSYS; - - Q_UNUSED(pdraw); - Q_UNUSED(renderer); - - emit mParent->loadVideoTexture(textureWidth, - textureHeight, - mediaInfo, - frame, - frameUserdata, - (unsigned int)frameUserdataLen, - &ret); - return ret; -} - - -int QPdrawWidgetPriv::renderVideoOverlay( - IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer, - const struct pdraw_rect *renderPos, - const struct pdraw_rect *contentPos, - const float *viewMat, - const float *projMat, - const struct pdraw_media_info *mediaInfo, - struct vmeta_frame *frameMeta, - const struct pdraw_video_frame_extra *frameExtra) -{ - int ret = -ENOSYS; - - Q_UNUSED(pdraw); - Q_UNUSED(renderer); - - emit mParent->renderVideoOverlay(renderPos, - contentPos, - viewMat, - projMat, - mediaInfo, - frameMeta, - frameExtra, - &ret); - return ret; -} - - -void QPdrawWidgetPriv::renderReady() -{ - mParent->update(); -} - - -} /* namespace Internal */ - - -QPdrawWidget::QPdrawWidget(QWidget *parent) : QOpenGLWidget(parent) -{ - qRegisterMetaType("pdraw_media_info"); - - mPriv = new Internal::QPdrawWidgetPriv(this); - - QSurfaceFormat format; - format.setSwapBehavior(QSurfaceFormat::TripleBuffer); - - /* Must be called before the widget or its parent - * window gets shown */ - setFormat(format); -} - - -QPdrawWidget::~QPdrawWidget() -{ - delete mPriv; -} - - -void QPdrawWidget::start(QPdraw *pdraw, unsigned int mediaId) -{ - struct pdraw_rect renderPos = { - .x = 0, - .y = 0, - .width = (unsigned int)(rect().width() * devicePixelRatio()), - .height = (unsigned int)(rect().height() * devicePixelRatio()), - }; - - struct pdraw_video_renderer_params params = { - .scheduling_mode = - PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ADAPTIVE, - .fill_mode = PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_EXTEND, - .enable_transition_flags = 0xFFFFFFFF, - .enable_hmd_distortion_correction = 0, - .hmd_ipd_offset = 0.f, - .hmd_x_offset = 0.f, - .hmd_y_offset = 0.f, - .video_scale_factor = 0.f, - .enable_overexposure_zebras = 0, - .overexposure_zebras_threshold = 0, - .enable_histograms = 0, - .video_texture_width = 0, - .video_texture_dar_width = 0, - .video_texture_dar_height = 0, - }; - - QPdrawWidget::start(pdraw, mediaId, &renderPos, ¶ms); -} - - -void QPdrawWidget::start(QPdraw *pdraw, - unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params) -{ - makeCurrent(); - - mPriv->start(pdraw, mediaId, renderPos, params); - - doneCurrent(); -} - - -void QPdrawWidget::stop() -{ - makeCurrent(); - - mPriv->stop(); - - doneCurrent(); -} - - -QRect QPdrawWidget::getVideoRect() const -{ - return rect(); -} - - -void QPdrawWidget::initializeGL() -{ - QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); - f->glClearColor(0.0f, 0.0f, 0.0f, 0.0f); -} - - -void QPdrawWidget::resizeGL(int w, int h) -{ - Q_UNUSED(w); - Q_UNUSED(h); - - /* TODO: w and h? */ - - struct pdraw_rect renderPos = { - 0, - 0, - (unsigned int)(rect().width() * devicePixelRatio()), - (unsigned int)(rect().height() * devicePixelRatio())}; - - bool ret = mPriv->resizeGL(&renderPos); - if (ret) - return; - - QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); - f->glClear(GL_COLOR_BUFFER_BIT); -} - - -void QPdrawWidget::paintGL() -{ - bool ret = mPriv->paintGL(); - if (ret) - return; - - QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); - f->glClear(GL_COLOR_BUFFER_BIT); -} - -} /* namespace QPdraw */ +/** + * Parrot Drones Awesome Video Viewer + * Qt PDrAW widget + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 "qpdraw_widget_priv.hpp" + +#define ULOG_TAG qpdraw_widget +#include +ULOG_DECLARE_TAG(ULOG_TAG); + +namespace QPdraw { +namespace Internal { + + +QPdrawWidgetPriv::QPdrawWidgetPriv(QPdrawWidget *parent) : + mParent(parent), mPdraw(nullptr), mRenderer(nullptr) +{ + connect(this, + &QPdrawWidgetPriv::onRenderReady, + this, + &QPdrawWidgetPriv::renderReady, + Qt::QueuedConnection); +} + + +QPdrawWidgetPriv::~QPdrawWidgetPriv() +{ + stop(); +} + + +void QPdrawWidgetPriv::start(QPdraw *pdraw, + unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params) +{ + ULOG_ERRNO_RETURN_IF(mPdraw != nullptr, EBUSY); + + IPdraw *pdrawInternal = + reinterpret_cast(pdraw->getInternal()); + ULOG_ERRNO_RETURN_IF(pdrawInternal == nullptr, EPROTO); + + int res = pdrawInternal->createVideoRenderer( + mediaId, renderPos, params, this, &mRenderer); + if (res == 0) + mPdraw = pdraw; +} + + +void QPdrawWidgetPriv::stop() +{ + if (mPdraw == nullptr) + return; + + delete mRenderer; + mRenderer = nullptr; + mPdraw = nullptr; +} + + +bool QPdrawWidgetPriv::resizeGL(const struct pdraw_rect *renderPos) +{ + if (mPdraw == nullptr) + return false; + + ULOG_ERRNO_RETURN_VAL_IF(mRenderer == nullptr, EINVAL, false); + + int res = mRenderer->resize(renderPos); + if (res < 0) { + ULOG_ERRNO("IVideoRenderer::resize", -res); + return false; + } + + return true; +} + + +bool QPdrawWidgetPriv::paintGL() +{ + if (mPdraw == nullptr) + return false; + + ULOG_ERRNO_RETURN_VAL_IF(mRenderer == nullptr, EINVAL, false); + + int res = mRenderer->render(nullptr); + if (res < 0) { + ULOG_ERRNO("IVideoRenderer::render", -res); + return false; + } + + return true; +} + + +void QPdrawWidgetPriv::onVideoRendererMediaAdded( + IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer, + const struct pdraw_media_info *info) +{ + Q_UNUSED(pdraw); + Q_UNUSED(renderer); + + struct pdraw_media_info info_copy = *info; + emit mParent->mediaAdded(info_copy); +} + + +void QPdrawWidgetPriv::onVideoRendererMediaRemoved( + IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer, + const struct pdraw_media_info *info) +{ + Q_UNUSED(pdraw); + Q_UNUSED(renderer); + + struct pdraw_media_info info_copy = *info; + emit mParent->mediaRemoved(info_copy); +} + + +void QPdrawWidgetPriv::onVideoRenderReady(IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer) +{ + Q_UNUSED(pdraw); + Q_UNUSED(renderer); + + emit onRenderReady(); +} + + +int QPdrawWidgetPriv::loadVideoTexture(IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer, + unsigned int textureWidth, + unsigned int textureHeight, + const struct pdraw_media_info *mediaInfo, + struct mbuf_raw_video_frame *frame, + const void *frameUserdata, + size_t frameUserdataLen) +{ + int ret = -ENOSYS; + + Q_UNUSED(pdraw); + Q_UNUSED(renderer); + + emit mParent->loadVideoTexture(textureWidth, + textureHeight, + mediaInfo, + frame, + frameUserdata, + (unsigned int)frameUserdataLen, + &ret); + return ret; +} + + +int QPdrawWidgetPriv::renderVideoOverlay( + IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer, + const struct pdraw_rect *renderPos, + const struct pdraw_rect *contentPos, + const float *viewMat, + const float *projMat, + const struct pdraw_media_info *mediaInfo, + struct vmeta_frame *frameMeta, + const struct pdraw_video_frame_extra *frameExtra) +{ + int ret = -ENOSYS; + + Q_UNUSED(pdraw); + Q_UNUSED(renderer); + + emit mParent->renderVideoOverlay(renderPos, + contentPos, + viewMat, + projMat, + mediaInfo, + frameMeta, + frameExtra, + &ret); + return ret; +} + + +void QPdrawWidgetPriv::renderReady() +{ + mParent->update(); +} + + +} /* namespace Internal */ + + +QPdrawWidget::QPdrawWidget(QWidget *parent) : QOpenGLWidget(parent) +{ + qRegisterMetaType("pdraw_media_info"); + + mPriv = new Internal::QPdrawWidgetPriv(this); + + QSurfaceFormat format; + format.setSwapBehavior(QSurfaceFormat::TripleBuffer); + + /* Must be called before the widget or its parent + * window gets shown */ + setFormat(format); +} + + +QPdrawWidget::~QPdrawWidget() +{ + delete mPriv; +} + + +void QPdrawWidget::start(QPdraw *pdraw, unsigned int mediaId) +{ + struct pdraw_rect renderPos = { + .x = 0, + .y = 0, + .width = (unsigned int)(rect().width() * devicePixelRatio()), + .height = (unsigned int)(rect().height() * devicePixelRatio()), + }; + + struct pdraw_video_renderer_params params = { + .scheduling_mode = + PDRAW_VIDEO_RENDERER_SCHEDULING_MODE_ADAPTIVE, + .fill_mode = PDRAW_VIDEO_RENDERER_FILL_MODE_FIT_PAD_BLUR_EXTEND, + .enable_transition_flags = 0xFFFFFFFF, + .enable_hmd_distortion_correction = 0, + .hmd_ipd_offset = 0.f, + .hmd_x_offset = 0.f, + .hmd_y_offset = 0.f, + .video_scale_factor = 0.f, + .enable_overexposure_zebras = 0, + .overexposure_zebras_threshold = 0, + .enable_histograms = 0, + .video_texture_width = 0, + .video_texture_dar_width = 0, + .video_texture_dar_height = 0, + }; + + QPdrawWidget::start(pdraw, mediaId, &renderPos, ¶ms); +} + + +void QPdrawWidget::start(QPdraw *pdraw, + unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params) +{ + makeCurrent(); + + mPriv->start(pdraw, mediaId, renderPos, params); + + doneCurrent(); +} + + +void QPdrawWidget::stop() +{ + makeCurrent(); + + mPriv->stop(); + + doneCurrent(); +} + + +QRect QPdrawWidget::getVideoRect() const +{ + return rect(); +} + + +void QPdrawWidget::initializeGL() +{ + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + f->glClearColor(0.0f, 0.0f, 0.0f, 0.0f); +} + + +void QPdrawWidget::resizeGL(int w, int h) +{ + Q_UNUSED(w); + Q_UNUSED(h); + + /* TODO: w and h? */ + + struct pdraw_rect renderPos = { + 0, + 0, + (unsigned int)(rect().width() * devicePixelRatio()), + (unsigned int)(rect().height() * devicePixelRatio())}; + + bool ret = mPriv->resizeGL(&renderPos); + if (ret) + return; + + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + f->glClear(GL_COLOR_BUFFER_BIT); +} + + +void QPdrawWidget::paintGL() +{ + bool ret = mPriv->paintGL(); + if (ret) + return; + + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + f->glClear(GL_COLOR_BUFFER_BIT); +} + +} /* namespace QPdraw */ diff --git a/qpdraw/src/qpdraw_widget_priv.hpp b/qpdraw/src/qpdraw_widget_priv.hpp index 2cd9218..59a5fb6 100644 --- a/qpdraw/src/qpdraw_widget_priv.hpp +++ b/qpdraw/src/qpdraw_widget_priv.hpp @@ -1,115 +1,115 @@ -/** - * Parrot Drones Awesome Video Viewer - * Qt PDrAW widget - * - * Copyright (c) 2018 Parrot Drones SAS - * Copyright (c) 2016 Aurelien Barre - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 _QPDRAW_WIDGET_PRIV_HPP_ -#define _QPDRAW_WIDGET_PRIV_HPP_ - -#include -#include - -using namespace Pdraw; - -namespace QPdraw { -namespace Internal { - - -class QPdrawWidgetPriv : public QObject, - public IPdraw::IVideoRenderer::Listener { - Q_OBJECT - -public: - explicit QPdrawWidgetPriv(QPdrawWidget *parent); - ~QPdrawWidgetPriv(); - - void start(QPdraw *pdraw, - unsigned int mediaId, - const struct pdraw_rect *renderPos, - const struct pdraw_video_renderer_params *params); - void stop(); - bool resizeGL(const struct pdraw_rect *renderPos); - bool paintGL(); - -private: - void onVideoRendererMediaAdded(IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer, - const struct pdraw_media_info *info); - - void onVideoRendererMediaRemoved(IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer, - const struct pdraw_media_info *info); - - void onVideoRenderReady(IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer); - - int loadVideoTexture(IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer, - unsigned int textureWidth, - unsigned int textureHeight, - const struct pdraw_media_info *mediaInfo, - struct mbuf_raw_video_frame *frame, - const void *frameUserdata, - size_t frameUserdataLen); - - int - renderVideoOverlay(IPdraw *pdraw, - IPdraw::IVideoRenderer *renderer, - const struct pdraw_rect *renderPos, - const struct pdraw_rect *contentPos, - const float *viewMat, - const float *projMat, - const struct pdraw_media_info *mediaInfo, - struct vmeta_frame *frameMeta, - const struct pdraw_video_frame_extra *frameExtra); - -signals: - /** - * Render ready signal, called by onVideoRenderReady() method, - * internally connected to the renderReady() slot, as the widget - * update must be done from the Qt GUI thread. - */ - void onRenderReady(); - -private slots: - /** - * Render ready slot, connected to the onRenderReady() signal, - * used to trigger the widget update in the Qt GUI thread. - */ - void renderReady(); - -private: - QPdrawWidget *mParent; - QPdraw *mPdraw; - IPdraw::IVideoRenderer *mRenderer; -}; - -} /* namespace Internal */ -} /* namespace QPdraw */ - -#endif /* !_QPDRAW_WIDGET_PRIV_HPP_ */ +/** + * Parrot Drones Awesome Video Viewer + * Qt PDrAW widget + * + * Copyright (c) 2018 Parrot Drones SAS + * Copyright (c) 2016 Aurelien Barre + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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 _QPDRAW_WIDGET_PRIV_HPP_ +#define _QPDRAW_WIDGET_PRIV_HPP_ + +#include +#include + +using namespace Pdraw; + +namespace QPdraw { +namespace Internal { + + +class QPdrawWidgetPriv : public QObject, + public IPdraw::IVideoRenderer::Listener { + Q_OBJECT + +public: + explicit QPdrawWidgetPriv(QPdrawWidget *parent); + ~QPdrawWidgetPriv(); + + void start(QPdraw *pdraw, + unsigned int mediaId, + const struct pdraw_rect *renderPos, + const struct pdraw_video_renderer_params *params); + void stop(); + bool resizeGL(const struct pdraw_rect *renderPos); + bool paintGL(); + +private: + void onVideoRendererMediaAdded(IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer, + const struct pdraw_media_info *info); + + void onVideoRendererMediaRemoved(IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer, + const struct pdraw_media_info *info); + + void onVideoRenderReady(IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer); + + int loadVideoTexture(IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer, + unsigned int textureWidth, + unsigned int textureHeight, + const struct pdraw_media_info *mediaInfo, + struct mbuf_raw_video_frame *frame, + const void *frameUserdata, + size_t frameUserdataLen); + + int + renderVideoOverlay(IPdraw *pdraw, + IPdraw::IVideoRenderer *renderer, + const struct pdraw_rect *renderPos, + const struct pdraw_rect *contentPos, + const float *viewMat, + const float *projMat, + const struct pdraw_media_info *mediaInfo, + struct vmeta_frame *frameMeta, + const struct pdraw_video_frame_extra *frameExtra); + +signals: + /** + * Render ready signal, called by onVideoRenderReady() method, + * internally connected to the renderReady() slot, as the widget + * update must be done from the Qt GUI thread. + */ + void onRenderReady(); + +private slots: + /** + * Render ready slot, connected to the onRenderReady() signal, + * used to trigger the widget update in the Qt GUI thread. + */ + void renderReady(); + +private: + QPdrawWidget *mParent; + QPdraw *mPdraw; + IPdraw::IVideoRenderer *mRenderer; +}; + +} /* namespace Internal */ +} /* namespace QPdraw */ + +#endif /* !_QPDRAW_WIDGET_PRIV_HPP_ */